fragment “分段”、“碎片”的意思,一般与Activity一起使用,嵌套在activity中表示为Activity界面的一部分。
它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段,有点像您可以在不同 Activity 中重复使用的“子 Activity”。
当您将片段作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且片段会定义其自己的视图布局。
安卓3.0(api 11)引入了fragment,主要就是为大屏幕(平板)添加更加灵活、动态的支持。
由于平板电脑的屏幕比手机屏幕大得多,因此可用于组合和交换 UI 组件的空间更大。利用片段实现此类设计时,您无需管理对视图层次结构的复杂更改。 通过将 Activity 布局分成片段,您可以在运行时修改 Activity 的外观,并在由 Activity 管理的返回栈中保留这些更改。
由于fragment依赖于activity所以其生命周期受activity生命周期影响。fragment的生命周期图如下:
(1)fragment的生命周期图( Activity 运行时)
要想创建fragment,您必须创建 Fragment 的子类(或已有其子类)。Fragment 类的代码与 Activity 非常相似。它包含与 Activity 类似的回调方法,如 onCreate()、onCreateView()、onStart()、onPause() 和 onStop()。
(1)创建类继承Fragment
(2)实现onCreateView方法。
系统会在fragment首次绘制其用户界面时调用此方法。 要想为您的片段绘制 UI,您从此方法中返回的 View 必须是片段布局的根视图。如果片段未提供 UI,您可以返回 null。
(3)使用
(1)创建子类 实现核心方法
/**
* LayoutInflater 详情参考:https://blog.csdn.net/u012702547/article/details/52628453
* */
public class BlankFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
/**
* 参数:
* 1、R.layout.fragment_blank 自定义布局的资源id
*
* 2、container 将作为自定义布局的父ViewGroup。是否将自定义布局添加到 container(由第三个参数的boolean值决定)
*
* 3、boolean值
*
* */
return inflater.inflate(R.layout.fragment_blank, container, false);
}
}
(2)MainActivity的xml中使用
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Fragment"/>
<fragment
android:id="@+id/ft"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.sunnyday.fragmentlearn.BlankFragment"/>
</LinearLayout>
注意:这里fragment必须要有id或者tag的否则崩溃(吧上代码中id去掉时)
Caused by: java.lang.IllegalArgumentException: Binary XML file line #16: Must specify unique android:id, android:tag, or have a parent with an id for com.sunnyday.fragmentlearn.BlankFragment
(3)拓展:为啥需要id这样的标识
每个fragment都需要一个唯一的标识符,重启 Activity 时,系统可以使用该标识符来恢复fragment(也可以使用该标识符来获取 fragment 以执行某些事务,比如将其移出),可以通过三种方式为 fragment 提供 ID:
- 通过 android:id 属性提供唯一 ID
- 通过 android:tag 属性提供唯一字符串
- 如果都没设置,系统会使用容器视图的 ID
(1)如上修改MainActivity的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello Fragment"/>
<FrameLayout
android:id="@+id/fl_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
(2)代码动态添加
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获得FragmentManager对象
FragmentManager fragmentManager = getSupportFragmentManager();
// 通过FragmentManager对象获得FragmentTransaction对象(开启事务)
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fl_layout, new BlankFragment());//动态添加
fragmentTransaction.commit();//提交 提交 提交(很重要)
}
}
上例展示了如何向 Activity 添加fragment以提供 UI。不过,您还可以使用fragment为 Activity 提供后台行为,而不展示 UI:
系统也提供了fragment 派生的一些子类
对fragment进行相关操作,提供了一些方法:
1、调用findFragmentById 或 findFragmentByTag 获取Activity Fragment
2、调用 popBackStack 将 Fragment 从返回栈中弹出
3、调用addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。
4、调用beginTransaction 获取 FragmentTransaction
在 Activity 中使用 Fragment 一大优点是:可以根据用户行为通过它们执行添加、移出、替换fragment以及其操作。
(1)FragmentTransaction常用方法
- add()
- remove()
- replace()
(2)事务注意点
尽管 Fragment 是作为独立于 Activity 的对象实现,并且可在多个 Activity 内使用,但fragment实例会直接绑定到包含它的 Activity
activity可以先获得fragmentManager的实例,然后通过fragmentManager实例的对象调用findFragmentById() 或 findFragmentByTag()来获得Fragment的实例。
fragment可以通过 getActivity() 获得其所依附的 Activity 实例
由于二者依附同一activity时,我们可以这样做
1、首先在fragmentA中获取activity的实例,
2、然后通过activity的实例获得fragmentManager实例
3、通过fragmentManager实例的对象调用findFragmentById() 或 findFragmentByTag()来获得FragmentB的实例。
使用接口回调也可让fragment之间进行通信。具体可看下官方文档的实例。
之所以不用带参的构造方法,原因在于 Activity 在一些特殊情况下会发生销毁并重建的情形,比如屏幕旋转、内存吃紧等;对应的,依附于 Activity 存在的 Fragment 也会发生类似的状况。而一旦重建Fragment 便会调用默认的无参构造函数,导致无法执行有参构造函数进行初始化工作。
public static OneFragment newInstance(int args){
OneFragment oneFragment = new OneFragment();
Bundle bundle = new Bundle();
bundle.putInt("someArgs", args);
oneFragment.setArguments(bundle);
return oneFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
int args = bundle.getInt("someArgs");
}
当 Fragment 中存在类似网络请求之类的异步耗时任务时,该任务执行完毕回调 Fragment 的方法用到 Activity 对象时,可能宿主 Activity 已经销毁,从而引发空指针异常,所以最好都判空。一般情况下,获取 Context,可以通过 getContext() 获取。
异常情况下:当 Activity 销毁并重建的时候,Activity 重新执行 onCreate 方法,那么就创建两次 Fragment 而导致 UI 重叠。解决如下。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
mFrameLayout = findViewById(R.id.fl_content);
if (savedInstanceState != null) {
mFirstFragment = (FirstFragment) mFragmentManager.findFragmentByTag("fragment1");
} else {
mFirstFragment = FirstFragment.newInstance();
mFragmentTransaction.add(mFirstFragment, "fragment1");
}
}
fragmentA中要使用getChildFragmentManager来获得FragmentManager对象,否则你虽然动态的添加了fragmentB当是FragmentA会被覆盖、或者移除。
FragmentManager fragmentManager = getChildFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container,new TestFragment());
fragmentTransaction.commit();
fragment 是基于事务的,每次事件就是一个一次性任务。所以每次操作时重新获得一个FragmentTransaction 再操作即可。
buttonBottom.setTabSelectedListener(new BottomNavigationBar.OnTabSelectedListener() {
@Override
public void onTabSelected(int position) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
if (position == 0) {
fragmentTransaction.replace(R.id.container, new TestFragment());
} else if (position == 1) {
fragmentTransaction.replace(R.id.container, new DiscFragment());
} else {
fragmentTransaction.replace(R.id.container, new GameFragment());
}
fragmentTransaction.commit();
}
@Override
public void onTabUnselected(int position) {
}
@Override
public void onTabReselected(int position) {
}
});
}
这里需要注意startActivityForResult不要使用activity的,要使用fragment自身的
参考:
安卓官方文档