一 简介
1.1 Fragment是Android3.0新增的概念,中文意思是“碎片”,它与Activity非常相似,是用来描述一些行为或者一部分用户界面
1.2 可以在一个单独的Activity中建立多个Fragment面板,也可以在多个Activity中复用Fragment
1.3 Fragment拥有自己的生命周期和接收、处理用户的事件,可以动态的添加、替换和移除某个Fragment
1.4 Fragment必须总是被嵌入到一个Activity中,它的生命周期受宿主Activity生命周期影响,它的状态会随宿主的状态变化而变化
1.5 要创建一个Fragment 必须创建一个Fragment的子类,或者继承自另一个已经存在的Fragment的子类,并重写onCreateView()方法加载UI
二 Fragment生病周期
图示:
- onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
- onCreate():Fragment被创建时调用。
- onCreateView():创建Fragment的布局。
- onActivityCreated():当Activity完成onCreate()时调用。
- onStart():当Fragment可见时调用。
- onResume():当Fragment可见且可交互时调用。
- onPause():当Fragment不可交互但可见时调用。
- onStop():当Fragment不可见时调用。
- onDestroyView():当Fragment的UI从视图结构中移除时调用。
- onDestroy():销毁Fragment时调用。
- onDetach():当Fragment和Activity解除关联时调用。
三 Fragment 静态使用
3.1 使用步骤实战:
第一步:创建fragment
public class Fragment1 extends Fragment {
private TextView tvText;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
tvText = (TextView) view.findViewById(R.id.tv_text);
return view;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onStop() {
super.onStop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onDetach() {
super.onDetach();
}
}
第二步:创建Activity
public class TestActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
}
第三步:在activity的布局文件xml里面设置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">
<fragment
android:id="@+id/fragment1"
android:name="com.dinghe.servicetest.fragment.Fragment1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
3.2 注意:
- 通过name属性将Fragment引入Activity布局,被引入的Fragment要写全路径
- fragment必须指定id,否则报错
四 Fragment的动态使用
4.1 使用步骤
第一步:还是创建fragment
public class Fragment1 extends Fragment {
private TextView tvText;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
tvText = (TextView) view.findViewById(R.id.tv_text);
return view;
}
}
第二步: 创建Activity的布局文件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">
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
第三步:创建Activity,并在上面的fl_content里面添加fragment
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
Fragment1 fragment1 = new Fragment1();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fl_content, fragment1);
transaction.commit();
}
}
4.2 使用流程总结:
- 获取到FragmentManager,在Activity中可以直接通过getSupportFragmentManager得到
- 开启一个事务,通过调用getSupportFragmentManager().beginTransaction()方法开启
- 向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例
- FragmentTransaction提交事务,调用commit方法提交
五 Fragment的add使用
5.1 除了上面的 transaction.replace() 添加Fragment方法,还有 transaction.add() 也可以添加,添加之后就可以用 transaction.show() 和 transaction.hide() 来控制Fragment显示和隐藏
5.2 那replace和add方法有什么区别呢?
replace是覆盖的意思,使用该方式会替换原有的Fragment,这就意味着原来的Fragment会结束其生命周期,缓存会被清理,会释放内存。
add是添加的意思,可以添加很多个Fragment,通过show和hide来控制显示和隐藏,所以该方式会缓存Fragment的全部内容,这也会导致内存会累加,占用大量内存。
总结起来,一个Fragment两者基本没区别,多个Fragment:
- replace可以节省内存,但多个fragment频繁创建和删除会造成性能的开销大。
- 而add可以缓存Fragment,性能流畅,但也伴随着内存的增加,可能发生OOM风险。
5.3 使用:
第一步:创建两个Fragment
public class Fragment1 extends Fragment {
private TextView tvText;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
tvText = (TextView) view.findViewById(R.id.tv_text);
return view;
}
}
public class Fragment2 extends Fragment {
private TextView tvText;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
tvText = (TextView) view.findViewById(R.id.tv_text);
return view;
}
}
第二步: 一样创建Activity的布局文件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">
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="50dp"
android:layout_margin="10dp"
android:text="页面1"/>
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="50dp"
android:layout_margin="10dp"
android:text="页面2"/>
</LinearLayout>
</LinearLayout>
第三步:Activity添加Fragment,并控制显示隐藏
public class TestActivity extends AppCompatActivity implements View.OnClickListener {
private FrameLayout flContent;
private Button button1;
private Button button2;
private Fragment1 fragment1;
private Fragment2 fragment2;
private FragmentManager fragmentManager;
private FragmentTransaction transaction;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
flContent = (FrameLayout) findViewById(R.id.fl_content);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.button1:
//先隐藏全部Fragment
hideFragment();
//添加并显示Fragment1
fragmentManager = getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
if(fragment1==null){
fragment1 = new Fragment1();
transaction.add(R.id.fl_content, fragment1);
}
transaction.show(fragment1);
transaction.commit();
break;
case R.id.button2:
//先隐藏全部Fragment
hideFragment();
//添加并显示Fragment2
fragmentManager = getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
if(fragment2==null){
fragment2 = new Fragment2();
transaction.add(R.id.fl_content, fragment2);
}
transaction.show(fragment2);
transaction.commit();
break;
}
}
/**
* 隐藏全部Fragment
*/
private void hideFragment(){
if(fragment1!=null){
transaction.hide(fragment1);
}
if(fragment2!=null){
transaction.hide(fragment2);
}
transaction.commit();
}
}
5.4 使用注意:
- 判断Fragment是否被添加过,没添加要先add,再show,避免重复添加
- show之前要先隐藏其它Fragment,否则可能出现页面重叠
- 每次提交commit()之后,再add,show,hide都需要新开始一个事务beginTransaction(),否则会报异常
六 Fragment与viewpager的使用
6.1 上面是通过按钮控制Fragment的显示和隐藏,还有一种配合ViewPager的使用,可以做左右滑动切换布局的效果,不用再手动控制添加,显示和隐藏,完全通过ViewPager的滑动状态来自动完成。
6.2 我们经常使用ListView,RecycleView等滑动控件,都有自己的Adapter适配器。ViewPager也是滑动控件,也有自己的适配器PagerAdapter,可以加载任何控件,比如VIew,ImageView,TextView等。如下PagerAdapte简单示例:
public class MyViewPagerActivity extends Activity {
private ViewPager viewPager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
viewPager = (ViewPager) findViewById(R.id.view_pager);
List<View> viewList = new ArrayList<>();
viewList.add(new View(this));
viewList.add(new View(this));
ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(viewList);
//设置ViewPager适配器
viewPager.setAdapter(viewPagerAdapter);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//监听滑动距离
}
@Override
public void onPageSelected(int position) {
//监听滑动页码
}
@Override
public void onPageScrollStateChanged(int state) {
//监听滑动状态,滑动中还是滑动结束
}
});
}
/**
* ViewPager适配器
*/
private class ViewPagerAdapter extends PagerAdapter {
private List<View> viewList;
public ViewPagerAdapter(List<View> viewList) {
this.viewList = viewList;
}
// 获取要滑动的控件的数量,在这里我们以滑动的广告栏为例,那么这里就应该是展示的广告图片的ImageView数量
@Override
public int getCount() {
return viewList.size();
}
// 来判断显示的是否是同一张图片,这里我们将两个参数相比较返回即可
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
// PagerAdapter只缓存三张要显示的图片,如果滑动的图片超出了缓存的范围,就会调用这个方法,将图片销毁
@Override
public void destroyItem(ViewGroup view, int position, Object object) {
view.removeView(viewList.get(position));
}
// 当要显示的图片可以进行缓存的时候,会调用这个方法进行显示图片的初始化,我们将要显示的ImageView加入到ViewGroup中,然后作为返回值返回即可
@Override
public Object instantiateItem(ViewGroup view, int position) {
view.addView(viewList.get(position));
return viewList.get(position);
}
}
}
6.3 上面示例是ViewPager加载View控件,那ViewPager加载Fragment也是有适配器的,即FragmentPagerAdapter,它继承于上面说的PagerAdapter,专门用于加载Fragment。介绍如下:
- FragmentPagerAdapter派生自PagerAdapter,它是专门用来呈现 Fragment页面的。
- 该类中每一个生成的Fragment都将保存在内存中,所以这个适配器更适合那些数量相对较少,静态的页面。对于存在多个fragment的情况,一般推荐使用FragmentStatePagerAdapter。
- FragmentPagerAdapter重载了几个必须实现的函数:getItem()、 getCount()。
6.4 我们常见的Activity种类有,Activity,FragmentActivity和AppCompatActivity,这对于Fragment的支持也不同,如下区别:
- Activity是最基础的页面类,对应getFragmentManager方法来控制Activity和 Fragment之间的交互。
- FragmentActivity间接继承自Activity,并提供了对v4包中support Fragment的支持,在FragmentActivity中必须使用getSupportFragmentManager方法来处理 support Fragment的交互。
- AppCompatActivity继承自FragmentActivity,为Material Design风格控件提供 了便利。
6.5 示例:
第一步:还是创建两个Fragment
public class Fragment1 extends Fragment {
private TextView tvText;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
tvText = (TextView) view.findViewById(R.id.tv_text);
return view;
}
}
public class Fragment2 extends Fragment {
private TextView tvText;
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
tvText = (TextView) view.findViewById(R.id.tv_text);
return view;
}
}
第二步:创建ViewPager布局
<?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">
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
第三步:Activity里面加载 FragmentPagerAdapter 适配器
public class MyFragmentViewPagerActivity extends AppCompatActivity {
private ViewPager viewPager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
viewPager = (ViewPager) findViewById(R.id.view_pager);
List<Fragment> viewList = new ArrayList<>();
viewList.add(new Fragment1());
viewList.add(new Fragment2());
MyFragmentPagerAdapter viewPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),viewList);
//设置ViewPager适配器
viewPager.setAdapter(viewPagerAdapter);
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//监听滑动距离
}
@Override
public void onPageSelected(int position) {
//监听滑动页码
}
@Override
public void onPageScrollStateChanged(int state) {
//监听滑动状态,滑动中还是滑动结束
}
});
}
/**
* Fragment适配器
*/
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
//1.创建Fragment数组
private List<Fragment> mFragments;
//2.接收从Activity页面传递过来的Fragment数组
public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments){
super(fm);
mFragments = fragments;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
}
}
6.6 可以看到 PagerAdapter 和 FragmentPagerAdapter 的用法几乎一样,只是把集合View换成了Fragment
七 Fragment事务Transaction详解
7.1 add()
添加一个Fragment到Activity中。
7.2 remove()
从Activity中移除一个Fragment。如果Fragment没有被加入回退栈中,则该Fragment会进行销毁。
7.3 replace()
用另一个Fragment替换当前的Fragment,本质上时先remove再add。
7.4 hide()
隐藏当前的Fragment,设置为不可见,不会销毁,与之对应的是show。本质是对View进行调用了View.GONE。
7.5 show()
显示之前隐藏的Fragment,与之对应的是hide。本质是对View进行调用了View.VISIBLE。
7.6 detach()
将视图进行移除,但是并不销毁Fragment,Fragment依旧由FragmentManager进行管理。
7.7 addToBackStack()
将Fragment加入返回栈。当移除或替换一个Fragment并向返回栈添加事务时,系统会停止(而非销毁)移除的Fragment。如果没有addToBackStack,则被替换的Fragment会直接进行销毁。
//示例,如果用户执行回退操作进行Fragment的恢复,被替换的Fragment将重新启动。
transaction.replace(R.id.fl_content, LearnFragment()).addToBackStack(null).commitAllowingStateLoss()
7.8 setMaxLifecycle()
设定Fragment生命周期最高上限。如果设置的生命周期上限低于当前的生命周期上限,则会进行回退设定的生命周期。
比如,当前生命周期为RESUME,如果设置MaxLifecycle为START,则生命周期则会回到RESUME。
说一个例子,在调用hide和show函数的时候,Frgament的生命周期并不会进行变化。但是我就是想要在show的时候让Fragment走到RESUME呢?此时可以进行如下操作。
//隐藏
transaction.setMaxLifecycle(fragment,Lifecycle.State.STARTED).hide(fragment)
//显示
transaction.setMaxLifecycle(fragment,Lifecycle.State.RESUMED).show(fragment)
7.9 commit/commitAllowingStateLoss()
提交一个事务。commit如果状态丢失则会抛异常,commitAllowingStateLoss则不会。
八 Fragment提交事务种类区分
8.1 commit()
异步的操作。对应的同步方法为commitNow()。安排当前事务FragmentTransaction进行提交。但是提交后Fragment不会立即创建,而是由主线程异步来创建。也就是说使用commit()之后,你的Fragment不会被立即加入到Activity中。
本次提交,必须在Activity的onSaveInstanceState调用之前提交。否则会抛异常。
8.2 commitAllowingStateLoss()
和commit类似。但是如果本次是在Activity的onSaveInstanceState调用之后,那么本次提交记录在Activity恢复的时候,可能不被保存。
8.3 commitNow()
同步的操作。对应的异步方法为commit()。将事务立即提交。所有添加的Fragment会被立即初始化,并开始生命周期。所有被移除的Fragment将会被立即移除。调用这个方法,相当于调用commit,然后调用FragmentManager的executePendingTransactions()。
8.4 commitNowAllowingStateLoss()
和commitNow类似。但是如果在在Activity的onSaveInstanceState调用之后,那么本次提交记录在Activity恢复的时候,可能不被保存。
8.5 总结:
- 每个FragmentTransaction只能提交commit一次。包括commit、commitNow、commitAllowingStateLoss、commitNowAllowingStateLoss。
- 提交完一次之后,再commit就会抛出异常,也就是说不要每进行一个操作就commit一次,应该把多个操作都在FragmentTransact
九 Fragment通信和Fragment数据更新
9.1 在Fragment初始化的时候通过setArguments传值如下:
Activity新建Fragemnt的时候设置数据
public static QianggouChildFragment newInstance(int type, String categoryId) {
QianggouChildFragment newFragment = new QianggouChildFragment();
Bundle bundle = new Bundle();
bundle.putInt("type", type);
bundle.putString("categoryId", categoryId);
newFragment.setArguments(bundle);
return newFragment;
}
Fragment里面接收数据:
public class Fragment1 extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int type = getArguments().getInt("type");
String categoryId = getArguments().getString("categoryId");
}
}
9.2 获取Fragment实例更新数据
Fragment里面添加设置数据方法
public class Fragment1 extends Fragment {
int type;
String categoryId;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
type = getArguments().getInt("type");
categoryId = getArguments().getString("categoryId");
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
return view;
}
//设置数据
public void setCategoryId(String categoryId) {
this.categoryId = categoryId;
}
//获取数据
public String getCategoryId() {
return categoryId;
}
}
Activity里面获取Fragment实例,设置数据和获取数据
//设置数据
public void setData(){
if(fragment1!=null){
fragment1.setCategoryId("111");
}
}
//获取数据
public void getData(){
if(fragment1!=null){
fragment1.getCategoryId();
}
}
完整Activity
public class TestActivity extends AppCompatActivity implements View.OnClickListener {
private FrameLayout flContent;
private Button button1;
private Button button2;
private Fragment1 fragment1;
private Fragment2 fragment2;
private FragmentManager fragmentManager;
private FragmentTransaction transaction;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
flContent = (FrameLayout) findViewById(R.id.fl_content);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.button1:
//先隐藏全部Fragment
hideFragment();
//添加并显示Fragment1
fragmentManager = getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
if(fragment1==null){
fragment1 = new Fragment1();
transaction.add(R.id.fl_content, fragment1);
}
transaction.show(fragment1);
transaction.commit();
break;
case R.id.button2:
//先隐藏全部Fragment
hideFragment();
//添加并显示Fragment2
fragmentManager = getSupportFragmentManager();
transaction = fragmentManager.beginTransaction();
if(fragment2==null){
fragment2 = new Fragment2();
transaction.add(R.id.fl_content, fragment2);
}
transaction.show(fragment2);
transaction.commit();
break;
}
}
/**
* 隐藏全部Fragment
*/
private void hideFragment(){
if(fragment1!=null){
transaction.hide(fragment1);
}
if(fragment2!=null){
transaction.hide(fragment2);
}
transaction.commit();
}
//设置数据
public void setData(){
if(fragment1!=null){
fragment1.setCategoryId("111");
}
}
//获取数据
public void getData(){
if(fragment1!=null){
fragment1.getCategoryId();
}
}
}
9.3 有种比较特殊,Fragment+ViewPager里面加载Fragment采用动态加载单例的方式,并不是Fragment的List集合,单例这种是获取不到Fragment实例的,如下实例:
public class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return Fragment1.newInstance(type, categoryList.get(position).id);
}
@Override
public int getCount() {
return 2;
}
}
通过id也获取不到,因为在ViewPager里Fragment是new出来动态加到ViewPager里的,上需的Id只是Fragment的布局文件的根Id,不是Fragment 的Id,故获取的Fragment为空。如下示例:
Fragment1 fragment1 =(Fragment1)getSupportFragmentManager().findFragmentById(R.id.fragment);
解决方法:通过tag获取
Fragemnt在创建的时候,系统会为Fragment分配tag,其格式如下:"android:switcher:" + R.id.viewpager + ":0" 其中0是Fragment在viewpager中的位置
示例:
//获取当前fragment,android:switcher:2131364838:0
public Fragment getCurrentFragment(int position) {
FragmentManager manager = getChildFragmentManager();
Fragment fragment = manager.findFragmentByTag(makeFragmentName(mDataBinding.viewPager.getId(), position));
return fragment;
}
使用:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
//滑动结束,不是当前item,取消懒加载
if (state == ViewPager.SCROLL_STATE_SETTLING) {
//遍历Fragment
for (int i = 0; i < pagerAdapter.getCount(); i++) {
//获取Fragment实例
Fragment1 fragment1 = (Fragment1) getCurrentFragment(i);
if (fragment1 != null) {
//刷新数据
mDataBinding.resetData();
}
}
}
}
});
9.4 当然还有其它方式:比如Fragment集合获取索引实例,EventBus等消息机制来进行数据交互
十 Fragment懒加载
10.1 由于Fragment添加之后就会调用资源和请求数据,和ViewPager配合使用也是如此,可能刚初始化还未被用户可见就调用很多Fragment。为了解决这种问题,防止大量数据的加载,就有了懒加载的方案,所谓懒加载就是Fragment被用户可见的时候才去加载资源和请求数据。
10.2 利用setUserVisibleHint()实现懒加载,Fragment提供了界面被用户可见的监听,即setUserVisibleHint(),用户可见会返回true,不可见会返回false。如下示例:
public abstract class BaseFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return null;
}
/**
* Fragment数据的懒加载.
*/
protected boolean isVisible;//是否可见
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getUserVisibleHint()) {
//可见
isVisible = true;
lazyLoad();
} else {
//不可见
isVisible = false;
}
}
protected void lazyLoad() {
//懒加载数据,比如请求接口
}
}
10.3 也有利用onHiddenChanged方法进行懒加载的,但onHiddenChanged使用可能会出现问题,并不是所有Fragment每次切换都会调用,所以该方式有风险。想尝试的可以参考下面示例:
public class FragmentTest extends Fragment {
//判断是否已进行过加载,避免重复加载
private boolean isLoad=false;
//判断当前fragment是否可见
private boolean isHidden=true;
@Override
public void onResume() {
super.onResume();
lazyLoad();
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
//hidden==false 表示不可见
isHidden=hidden;
lazyLoad();
}
@Override
public void onDestroyView() {
super.onDestroyView();
isLoad=false; //注意当view销毁时需要把isLoad置为false
}
private void lazyLoad() {
if (!isLoad&&!isHidden){
//懒加载
isLoad=true;
}
}
}
十一 Fragment 返回监听
11.1 由于Fragment依赖与Activity,所以Fragment的返回键会被Activity拦截,那怎样来监听Fragment的返回事件呢,下面介绍三种方法
11.2 在宿主Activity的返回监听里面循环赋予Fragment监听回退功能,如下:
第一步:建一个基类,增加退回监听方法
public class BaseFragment extends Fragment {
/*
* fragment中的返回键
*
* 默认返回flase,交给Activity处理
* 返回true:执行fragment中需要执行的逻辑
* 返回false:执行activity中的 onBackPressed
* */
public boolean onBackPressed() {
//返回true拦截
return false;
}
}
第二步:在Activity的onBackPressed 返回监听里面循环调用Fragment的方法
@Override
public void onBackPressed() {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
//如果是自己封装的Fragment的子类 判断是否需要处理返回事件
if (fragment instanceof Fragment) {
if (((Fragment) fragment).onBackPressed()) {
//在Fragment中处理返回事件
return;
}
}
}
super.onBackPressed();
}
11.3 在Fragment的onResume里面监听View的点击事件
@Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
// handle back button
return true;
}
return false;
}
});
}
但这种方法存在一定的问题:当Fragment中的某些控件获取到焦点后,下述方法则无法再监听到 KeyEvent
11.4 使用Google提供的 OnBackPressedDispatcher 实现监听
public abstract class BackFragment extends BaseFragment {
/*
* 执行放弃方法
*/
abstract void abandon();
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
requireActivity().getOnBackPressedDispatcher().addCallback(this/*LifecycleOwner*/, mCallback);
}
private OnBackPressedCallback mCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
abandon();
}
};
@Override
public void onDestroy() {
super.onDestroy();
mCallback.remove();
}
}
这里OnBackPressedCallback的构造需要传一个参数enabled,标识fragment是否要处理这个返回键事件,我们可以动态设置这个值来决定fragment是否要处理用户的返回键事