参考书籍:《第一行代码》 第二版 郭霖
如有错漏,请批评指出!
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基础回顾(四)| 关于广播机制