參考書籍:《第一行代碼》 第二版 郭霖
如有錯漏,請批評指出!
Fragment的簡單使用
-
靜態添加Fragment
首先創建一個項目,自動生成MainActivity和它的布局文件activity_main.xml,然后創建三個Fragment,取消掉下面兩項的勾選,僅僅為它自動創建布局文件。
這里我們需要注意一下,Android中有兩個包下的Fragment,一個是系統內置的android.app.Fragment,另一個是support-v4庫中的android.support.v4.app.Fragment,系統自動為我們創建的Fragment是support-v4包下的,因為它對Android的版本適配更好,因此后面我們使用的所有Fragment都是support-v4包下的。
將三個Fragment分別命名為TopFragment1、TopFragment2、ButtomFragment,布局文件fragment_top_1、fragment_top_2、fragment_buttom。
修改布局文件 fragment_top_1.xml:<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorWhite" tools:context="com.laughter.AboutFragment.fragment.TopFragment1"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/fragment_1" android:textAllCaps="false" android:textSize="24sp"/> </FrameLayout>
修改 fragment_top_2.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorGray" tools:context="com.laughter.AboutFragment.fragment.TopFragment2"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/fragment_2" android:textSize="24sp" android:textAllCaps="false"/> </FrameLayout>
這些修改只是為了將兩個Fragment區分開來,并沒有什么特別的地方。接下來修改MainActivity的布局文件activity_main.xml
我們可以看到,這里我們在LinearLayout中添加了兩個 <fragment/> 標簽,我們通過android:name屬性為fragment標簽靜態綁定Fragment,這里需要注意一定要寫完整的包名。下面看運行結果:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:baselineAligned="false"> <fragment android:id="@+id/top_fragment" android:name="com.laughter.AboutFragment.fragment.TopFragment1" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="4"/> <fragment android:id="@+id/buttom_fragment" android:name="com.laughter.AboutFragment.fragment.TopFragment2" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout>
-
動態添加Fragment
相比于靜態添加Fragment,動態添加就要靈活的多了。在前面的基礎上,我們先修改一下MainActivity的布局activity_main.xml(僅僅將第一個<fragment/>標簽改為FrameLayout。):<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:baselineAligned="false"> <FrameLayout android:id="@+id/frame_layout" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="4"/> <fragment android:id="@+id/buttom_fragment" android:name="com.laughter.AboutFragment.fragment.ButtomFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout>
接下來修改ButtomFragment的布局文件fragment_buttom.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:gravity="center" tools:ignore="ButtonStyle" tools:context="com.laughter.AboutFragment.fragment.ButtomFragment"> <Button android:id="@+id/but_left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="8dp" android:layout_margin="8dp" android:foreground="?android:attr/selectableItemBackground" android:text="@string/frag_1" android:textSize="24sp" android:textAllCaps="false" /> <Button android:id="@+id/but_right" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="8dp" android:layout_margin="8dp" android:foreground="?android:attr/selectableItemBackground" android:text="@string/frag2" android:textSize="24sp" android:textAllCaps="false"/> </LinearLayout>
我們添加了兩個Buttom,用于切換Fragment。最后在MainActivity中實現Fragment的動態添加和切換:
public class MainActivity extends AppCompatActivity{ private TopFragment1 fragment1; private TopFragment2 fragment2; private final String FRAG1 = "frag1"; private final String FRAG2 = "frag2"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick({R.id.but_left, R.id.but_right}) public void show(View view) { FragmentManager manager = getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); hideFragments(manager, transaction); switch (view.getId()) { case R.id.but_left: if (fragment1 == null){ fragment1 = new TopFragment1(); transaction.add(R.id.frame_layout, fragment1, FRAG1); } transaction.show(fragment1); break; case R.id.but_right: if (fragment2 == null){ fragment2 = new TopFragment2(); transaction.add(R.id.frame_layout, fragment2, FRAG2); } transaction.show(fragment2); break; default: break; } transaction.commit(); } private void hideFragments(FragmentManager manager, FragmentTransaction transaction) { fragment1 = (TopFragment1)manager.findFragmentByTag(FRAG1); fragment2 = (TopFragment2)manager.findFragmentByTag(FRAG2); if (fragment1 != null) { transaction.hide(fragment1); } if (fragment2 != null) { transaction.hide(fragment2); } } }
上面代碼中有關于 ButterKnife 的使用,可以參考我的另一篇博客 Android實踐(二) | 注解框架ButterKnife基本使用 。
- 最初,MainActivity的FrameLayout中是什么都沒有的,當我們觸發點擊事件時,我們通過 getSupportFragmentManager() 方法獲取到FragmentManager實例,然后調用它的 beginTransaction() 方法開啟一個事務;
- 然后先隱藏所有的Fragment(若事務中已經add了Fragment實例,可以通過 FragmentManager 的 findFragmentByTag() 方法獲取到已經存在的實例,若不存在則返回null);
- 接下來判斷Fragment實例是否已經被創建,若未創建則創建實例并通過 FragmentTranscation 的add()方法添加到事務中,這里需要注意 add() 方法有多個重載方法,這里因為我們要使用到TAG這個參數,因此我們使用add(int resId, Fragment fragment, String tag)這個。第一個參數傳入一個layout,也就是將Fragment添加到哪個布局中;第二個參數就是Fragment實例,第三個參數也就是tag,用于標識我們傳入的Fragment實例。
-
最后別忘了還要調用 FragmentTranscation 的 commit() 方法提交事務,否則我們前面所做的都無效。
這樣,我們動態添加Fragment的功能就完成了,看效果:
Fragment的生命周期
和Activity一樣,Fragment也有自己的生命周期,并且它的生命周期和Activity很相似。為了直觀感受Fragment的生命周期,我們通過一個例子來觀察創建及銷毀一個Fragment完整的生命周期:
還是通過修改前面的例子,我們給重寫Fragmeng的每個生命周期方法,加上打印信息(這里只給出TopFragment1的代碼,TopFragment2一樣):
public class TopFragment1 extends Fragment {
private final String TAG = "Fragment1";
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
return inflater.inflate(R.layout.fragment_top1, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach");
}
}
為了生命周期的過程更加清晰,我們用replace()的方式來切換Fragment:
public class MainActivity extends AppCompatActivity {
private TopFragment1 fragment1;
private TopFragment2 fragment2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick({R.id.but_left, R.id.but_right})
public void show(View view) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
switch (view.getId()) {
case R.id.but_left:
if (fragment1 == null) {
fragment1 = new TopFragment1();
}
transaction.replace(R.id.frame_layout, fragment1);
break;
case R.id.but_right:
if (fragment2 == null){
fragment2 = new TopFragment2();
}
transaction.replace(R.id.frame_layout, fragment2);
break;
default:
break;
}
transaction.commit();
}
}
補充說明:
Fragment的切換有兩種方式,第一種是add()+show()+hide()的方式,第二種是replace()的方式,關于它們的區別,后面我會寫一篇文章來總結。
下面我們來進行測試:
-
點擊Fragment1按鈕,將TopFragment1添加到Activity中,生命周期如下:
可以看到,Fragment的創建過程比Activity多了幾個方法:
onAttach():Fragment與Activity建立關聯時調用
onCreateView():為Fragment加載布局時調用
onActivityCreated():當Activity的 onCreate() 方法執行完后調用 -
然后點擊Fragment2按鈕,將TopFragment2添加到Activity中,生命周期如下:
當我們切換Fragment時,首先Activity會與Fragment2建立關聯,然后創建Fragment2,接下來暫停、銷毀Fragment1(因為我們使用的是replace()方式切換Fragment,會默認銷毀前一個Fragment的實例),然后為Fragment2加載布局,啟動Fragment2。
onDestroyView():與onCreateView相對應,Fragment的布局被移除時調用
onDetach():Activity與Fragment解除關聯時調用 -
再切換回Fragment1,又會創建Fragment1的實例,并移除Fragment2:
根據上面的測試,我們對Fragment的生命周期已經有了清晰的認識了,不過Fragment不獨立存在,一般都是和Activity關聯使用,因此我們還需要了解Fragment與Activity生命周期是如何穿插在一起的。
如果你想熟悉一下Activity的生命周期,可以參考:Android筆記(一) | Activity的生命周期
Activity與Fragment生命周期的關系
在前面的基礎上,我們只需要重寫Activity生命周期的各個方法,并打印出來:
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
private TopFragment1 fragment1;
private TopFragment2 fragment2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
Log.d(TAG, "onCreate");
}
@OnClick({R.id.but_left, R.id.but_right})
public void show(View view) { ··· }
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
}
接下來再來測試:
為了觀察Activity與Fragment的生命周期是如何交叉在一起的,我們查看靜態加載的ButtomFragment的生命周期(需要重寫ButtomFragment生命周期的的各個方法,并打印,和前面TopFragment一樣),因為靜態加載是和Activity一起創建的。
-
創建
-
息屏
-
亮屏
-
進入別的Activity
-
再回到MainActivity
-
按back鍵退出
有關Fragment的基本知識就是這些了,在實際項目中Fragment的使用十分靈活,還得自己慢慢摸索。
上一篇:Android基礎回顧(二)| 常用控件 — ListView和RecyclerView
下一篇:Android基礎回顧(四)| 關于廣播機制