Android 技能树 — Fragment 总体小结,kotlin 安卓开发教程视频
4.Fragment 配合 ViewPager
ViewPager 配合 Fragment 的时候,主要使用FragmentPagerAdapter
和FragmentStatePagerAdapter
这二个 Adapter。其实使用很简单(一般的最最简单的写法):
public class FragmentAdapter extends FragmentPagerAdapter{private ArrayList<Fragment> list;
//通过构造获取 fragment 集合 public Fragment_pager(FragmentManager fm,ArrayList<Fragment> list) {super(fm);this.list=list;}//设置具体 position 的 fragment@Overridepublic Fragment getItem(int position) {// TODO Auto-generated method stubreturn list.get(position);}//设置有多少个 fragment@Overridepublic int getCount() {// TODO Auto-generated method stubreturn list.size();}}
然后ViewPager.setAdapter(xxxx);
但是大家会奇怪为啥有二个 Adapter:FragmentPagerAdapter
和FragmentStatePagerAdapter
,他们的区别我们可以看具体的源码:
FragmentPagerAdapter 源码:
public abstract class FragmentPagerAdapter extends PagerAdapter {
//'初始化创建 Item:'@NonNullpublic Object instantiateItem(@NonNull ViewGroup container, int position) {if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}
long itemId = this.getItemId(position);String name = makeFragmentName(container.getId(), itemId);Fragment fragment = this.mFragmentManager.findFragmentByTag(name);if (fragment != null) {
//'后面使用 fragment 是通过 FragmentTransaction.attach 方式加进来的,'//'只是重新绘制了 UI,fragment 对象还在。'this.mCurTransaction.attach(fragment);} else {
//'我们知道刚返回 fragment 使用的是 getItem(position)方法'//'我们可以看到第一次使用 fragment 是通过 FragmentTransaction.add 方式加进来的'fragment = this.getItem(position);this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));}
if (fragment != this.mCurrentPrimaryItem) {fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);}
return fragment;}
//'销毁 item:'public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}
//'我们可以看到 FragmentTransaction 只是单纯的 detach 了 fragment,视图不在了,但是 fragment 对象还在'this.mCurTransaction.detach((Fragment)object);}
}
我们可以看到 fragment 并没有真的销毁,FragmentPageAdapter 则适用于固定的,少量的 Fragment 情况,例如和 TabLayout 共同使用时。
FragmentStatePagerAdapter 源码:
public abstract class FragmentStatePagerAdapter extends PagerAdapter {
@NonNullpublic Object instantiateItem(@NonNull ViewGroup container, int position) {Fragment fragment;if (this.mFragments.size() > position) {fragment = (Fragment)this.mFragments.get(position);if (fragment != null) {return fragment;}}
if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}
fragment = this.getItem(position);if (this.mSavedState.size() > position) {SavedState fss = (SavedState)this.mSavedState.get(position);if (fss != null) {fragment.setInitialSavedState(fss);}}
while(this.mFragments.size() <= position) {this.mFragments.add((Object)null);}
fragment.setMenuVisibility(false);fragment.setUserVisibleHint(false);this.mFragments.set(position, fragment);
//'我们可以看到 fragment 都是 add 进来的'this.mCurTransaction.add(container.getId(), fragment);return fragment;}
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {Fragment fragment = (Fragment)object;if (this.mCurTransaction == null) {this.mCurTransaction = this.mFragmentManager.beginTransaction();}
while(this.mSavedState.size() <= position) {this.mSavedState.add((Object)null);}
this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);this.mFragments.set(position, (Object)null);
//'可以看到都是通过 remove 的方式移除了'this.mCurTransaction.remove(fragment);}
}
所以我们知道了FragmentStatePagerAdapter
是真的会把 fragment 对象都销毁,所以如果 fragment 数量很多的话,使用这个会更好,因为 fragment 存在太多,对应用性能造成很大影响,所以要 remove 掉 fragment。
5.无 UI 的 fragment:
5.1 使用 Fragment 保持需要恢复对象
调用 setRetainInstance(true)方法可保留 fragment,如下:
@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(true);...........}
比如旋转屏幕,已保留的 fragment 不会随着 activity 一起被销毁(但会销毁 fragment 的视图); 相反,它会一直保留(进程不消亡的前提下),并在需要时原封不动地传递给新的 Activity。
所以我们比如一些对象可以保持在 fragment 中,这时候 Activity 重新恢复后,其他对象可以从 fragment 中找回。
可以大概看下其他作者文章介绍:
[Fragment 调用 setRetainInstance 的原理](
)
5.2 类似 RxPermission 用于处理回调
RxPermission 里有一个 Fragment 用于分发权限回调。这个是什么意思??
我们知道原生请求权限:
//发出权限请求:int requestCode = 1;requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},requestCode);
//权限处理结果回调 @Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}
是不是觉得在要看复写这个回调方法很麻烦???而且没有美感。
而 RxPermission 是这样申请权限的:
RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.requestEach(//请求的权限 Manifest.permission.CAMERA,Manifest.permission.READ_PHONE_STATE).subscribe(new Consumer<Permission>() {@Overridepublic void accept(@io.reactivex.annotations.NonNull Permission permission) throws Exception {//权限通知回调}});
感觉就是一步呵成的感觉,很棒。但是 RxPermission 只是对系统的原生权限申请做了封装而已,那系统的原本的回调函数:onRequestPermissionsResult
去哪里了呢???
public class RxPermissionsFragment extends Fragment {
.....................
//'申请权限'@TargetApi(23)void requestPermissions(@NonNull String[] permissions) {this.requestPermissions(permissions, 42);}
//'申请权限后结果回调'@TargetApi(23)public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == 42) {boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];
for(int i = 0; i < permissions.length; ++i) {shouldShowRequestPermissionRationale[i] = this.shouldShowRequestPermissionRationale(permissions[i]);}
this.onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);}}
//'回调后的具体处理方法'void onRequestPermissionsResult(String[] permissions, int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {int i = 0;
for(int size = permissions.length; i < size; ++i) {this.log("onRequestPermissionsResult " + permissions[i]);PublishSubject<Permission> subject = (PublishSubject)this.mSubjects.get(permissions[i]);if (subject == null) {Log.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");return;}
this.mSubjects.remove(
permissions[i]);boolean granted = grantResults[i] == 0;
//'subject 主动调用 onNext 方法发送结果'subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));subject.onComplete();}
}
..................
}
我们可以到这个 fragment 内部已经帮我们复写了请求权限的原生方法和权限回调通知的原生方法。然后再通过 subject 在结果处发送通知即可。
这里我不会细讲整个 RxPermission 源码,我以前写过的相关文章,大家可以具体看下:
[项目需求讨论 - 动态权限申请分析及相关第三方库源码分析](
)
[项目需求讨论 — 手把手带你写 RxPermission](
)
6.构造函数和数据传递
6.1 构造函数传递数据
我们知道 fragment 也就是普通的对象,可以通过 new 的方式,我们平常使用对象传递值都是可以直接在构造函数里面定义参数值,直接赋值进去,那 fragment 是否可以这样??答案是可以的,但是不推荐。
public class FragmentOne extends Fragment {
//'在其他地方直接 FragmentOne one = new FragmentOne("青蛙要 fly");进行值传递'//'但是我们不推荐这样'public FragmentOne(String value) {
}
//'而是通过 bundle 来传递,Fragment.setArguments(Bundle)赋值进去'public static FragmentOne newInstance(Bundle args) {FragmentOne fragment = new FragmentOne();if(args != null){fragment.setArguments(args);
}return fragment;}
}
原因:我们可以知道 Activity 重新创建时,会重新构建它所管理的 Fragment,原先的 Fragment 的字段值将会全部丢失(因为当切换横竖屏时,Fragment 会调用自己的无参构造函数,那么在构造函数传参就会失效),但是通过 Fragment.setArguments(Bundle bundle)方法设置的 bundle 会保留下来,从而数据又可以恢复,所以尽量使用 Fragment.setArguments(Bundle bundle)方式来传递参数
6.2 其他数据传递方式
Activity 与 Fragment 数据传递:
Fragment 与 Fragment 数据传递
重点说下 setTargetFragment,因为很多人都不知道。
我们的目标:FragmentA 启动 FragmentB ,然后 FragmentB 做完事情,返回结果给 FragmentA
FragmentB.setTargetFragment(FragmentA);
然后在 B 中:getTargetFragment().onActivityResult(getTargetRequestCode(), resultOK, i);
然后再 FragmentA 中:@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);}
7. Fragment 重建恢复数据
推荐下面这篇文章:
[[译] 保存/恢复 Activity 和 Fragment 状态的最佳实践](
)
引用一段话:
完全分开处理 Fragment 状态和 view 状态 为了使你的代码变得干净和可扩展,你最好把 Fragment 状态和 View 状态分开处理。如果这里有任何属性是属于 View 的,在 View 内部进行保存和恢复.如果这里有任何属性是属于 Fragment 的,在 Fragment 内部进行保存和恢复。
8.常用监听 Fragment 显示方法
这块比较基础,就不细讲了。
9.监听 Fragment 发生变化
回退栈(back stack)状态改变监听:
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {@Overridepublic void onBackStackChanged() {
}});
注册 fragment 的生命监听:
List<Fragment> fragmentList = new ArrayList<>();
getActivity().getSupportFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {@Overridepublic void onFragmentViewCreated(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull View v, @Nullable Bundle savedInstanceState) {super.onFragmentViewCreated(fm, f, v, savedInstanceState);
评论