從0系統(tǒng)學(xué)Android--4.1探究碎片
本系列文章目錄:更多精品文章分類
本系列持續(xù)更新中.... 初級(jí)階段內(nèi)容參考《第一行代碼》
第四章:手機(jī)平板要兼顧--探究碎片
平板電腦和手機(jī)最大的區(qū)別就在于屏幕的大小,一般手機(jī)的屏幕大小會(huì)在 3 英寸到 6 英寸之間,而一般平板電腦屏幕大小會(huì)在 7 英寸到 10 英寸之間。屏幕大小差距過(guò)大會(huì)導(dǎo)致同樣的界面視覺(jué)效果有很大的差異。
為了兼顧手機(jī)和平板開(kāi)發(fā),Android 3.0 引入了碎片的概念,可以讓界面在平板上更好的展示。
4.1 碎片是什么
碎片(Fragment)是一種可以嵌入到 Activity 中的 UI 片段,讓程序更加合理和充分利用屏幕的空間。它和 Activity 很像,同樣都能包含布局,同樣有生命周期。
如何利用平板的屏幕空間呢?比如我們要開(kāi)發(fā)一個(gè)新聞?lì)惖?APP。在手機(jī)端可以是這樣的。
可以是如果在平板上也這樣設(shè)計(jì),那么新聞標(biāo)題列表就會(huì)給拉伸的很長(zhǎng),而新聞的標(biāo)題一般都不會(huì)太長(zhǎng),這樣設(shè)計(jì)就會(huì)導(dǎo)致頁(yè)面不合理。
因此,更好的設(shè)計(jì)方案是將新聞列表和新聞詳細(xì)內(nèi)容界面放到兩個(gè)碎片中,然后在同一 Activity 中引入這兩個(gè)碎片,這樣屏幕空間就充分利用起來(lái)了。
4.2 碎片的使用方式
首先我們先創(chuàng)建一個(gè)平板的模擬器,準(zhǔn)備好后新建一個(gè)包用于碎片化的練習(xí)。
4.2.1 碎片的簡(jiǎn)單用法
寫一個(gè)最簡(jiǎn)單的碎片示例,在一個(gè) Activity 中添加兩個(gè)碎片,并讓這兩個(gè)碎片平方 Activity 空間。
- 新建一個(gè)左側(cè)碎片布局
left_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:layout_gravity="center_horizontal"
android:id="@+id/bt"/>
</LinearLayout>
- 新建一個(gè)右側(cè)碎片布局
right_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF0000"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="This is right Fragment"/>
</LinearLayout>
- 新建 LeftFragment 類,讓他繼承 Fragment,F(xiàn)ragment 可能會(huì)有兩個(gè)不同的包,建議使用支持庫(kù)中的 Fragment,因?yàn)樗梢宰?Fragment 在所有的 Android 系統(tǒng)版本中保持功能一致性。比如在 Fragment 中嵌套 Fragment ,如果你使用的是系統(tǒng)內(nèi)置的 Fragment 則在 Android 4.2 系統(tǒng)之前的設(shè)備上運(yùn)行程序會(huì)崩潰。
public class LeftFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}
}
- 同樣的方法再創(chuàng)建一個(gè)
RightFragment
public class RightFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}
}
- 修改
fragmentbaseuse_activity代碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.firstcode.fourth_chapter.LeftFragment"
android:id="@+id/fg_left"/>
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.firstcode.fourth_chapter.RightFragment"
android:id="@+id/fg_right"/>
</LinearLayout>
這里使用了 <fragment> 標(biāo)簽在布局中添加碎片,然后在標(biāo)簽中通過(guò) android:name 屬性來(lái)指明要添加的碎片的類名,注意一定要把包名加上。
運(yùn)行結(jié)果:
4.2.2 動(dòng)態(tài)添加碎片
在上一節(jié)中我們學(xué)習(xí)了如何在布局中添加碎片,下面我們來(lái)學(xué)習(xí)如何用代碼動(dòng)態(tài)的添加碎片。
新建一個(gè) another_right_fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFF00"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="another fragment"/>
</LinearLayout>
里面代碼基本相同,只是更該了一下背景顏色,用來(lái)區(qū)分。
再新建一個(gè) Fragement
public class AnotherRightFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.another_right_fragment, container, false);
return view;
}
}
修改主頁(yè)面的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.firstcode.fourth_chapter.LeftFragment"
android:id="@+id/fg_left"/>
<!-- <fragment
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.example.firstcode.fourth_chapter.RightFragment"
android:id="@+id/fg_right"/>-->
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:id="@+id/framelayout"/>
</LinearLayout>
將 <fragment>替換成了 FrameLayout ,F(xiàn)ragmentLayout 是 Android 中最簡(jiǎn)單的一種布局,所有的控件默認(rèn)會(huì)擺放在布局的左上角。這里僅需要放入一個(gè)碎片,不需要任何定位,因此非常適合使用 FrameLayout
下面在代碼中向 FrameLayout 中添加內(nèi)容,從而實(shí)現(xiàn)動(dòng)態(tài)添加碎片的功能。
public class FragmentBaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragmentbase);
Button button = findViewById(R.id.bt);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
replaceFragment(new AnotherRightFragment());
}
});
replaceFragment(new RightFragment());
}
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.framelayout,fragment);
fragmentTransaction.commit();
}
}
首先給左側(cè)碎片中的按鈕注冊(cè)了點(diǎn)擊事件,然后調(diào)用了 replaceFragment() 方法動(dòng)態(tài)的添加了 RightFragment 這個(gè)碎片。當(dāng)點(diǎn)擊左側(cè)按鈕的時(shí)候,就會(huì)觸發(fā) replaceFragment() 這個(gè)方法。
動(dòng)態(tài)添加碎片主要分為 5 步:
- 創(chuàng)建待添加的碎片
- 獲取
FrgmentManager,FragmentManager是一個(gè)抽象類,在 Activity 中通過(guò)getSupportFragmentManager()方法來(lái)獲取。 - 開(kāi)啟一個(gè)事務(wù),通過(guò)調(diào)用
beginTransaction()方法開(kāi)啟 - 向容器中添加或者替換掉已經(jīng)添加的碎片,一般使用
replace()就可以了。 - 提交事務(wù),調(diào)用
commit()方法來(lái)完成。
這樣就完成了。
4.2.3 在碎片中模擬返回棧
在上一小節(jié)中已經(jīng)學(xué)習(xí)了如何動(dòng)態(tài)添加碎片,不過(guò)當(dāng)我們按下 Back 鍵程序就直接退出了,如何實(shí)現(xiàn)類似于返回棧的效果,當(dāng)按下 back 鍵的時(shí)候返回到上一個(gè)碎片呢?
FragmentTransaction 中提供了一個(gè) addToBackStack() 方法,可以用于將一個(gè)事務(wù)添加到返回棧中。
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.framelayout,fragment);
// 可以接受一個(gè)名字用于描述返回棧的狀態(tài),一般傳入 null 即可 會(huì)將這個(gè) Fragment 添加到棧中
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
這樣再運(yùn)行程序,你會(huì)發(fā)現(xiàn)按下 Back 后程序沒(méi)有立馬退出,而是先退出了 Fragment,等 Fragment 界面也消失了,再按下 back 才會(huì)退出。
4.2.4 碎片和活動(dòng)之間進(jìn)行通信
為了方便碎片和活動(dòng)之間進(jìn)行通信,F(xiàn)ragmentManager 提供了一個(gè)類似于 findViewById() 的方法,專門用于從布局文件中獲取碎片的實(shí)例。
RightFragment rightFragment = (RightFragment)getSupportFragmentManager().findFragmentById(R.id.right_fragment)
這個(gè)方法是適用于在布局中通過(guò)<fragment>靜態(tài)添加 Fragment 的情況,如果是動(dòng)態(tài)的,就直接 new Fragment() 了。
那么如何在碎片中調(diào)用 Activity 呢?其實(shí)每個(gè)碎片中都可以通過(guò)調(diào)用 getActivity() 方法來(lái)得到和當(dāng)前碎片相關(guān)聯(lián)的 Activity 實(shí)例。
那么碎片與碎片直接如何通信呢?
思路:首先在一個(gè)碎片中可以得到與之關(guān)聯(lián)的 Activity,然后通過(guò)這個(gè) Activity 再去獲取另外一個(gè)碎片實(shí)例就可以了。