ViewPager 用来做多 Tab 处理已经是十分常见的了,ViewPager 需要一个 Fragment List 来生成 Adapter,这些 Fragment 有时是完全一样的,界面元素、展示风格都一致,只是数据源不一致。通过抽象的思想,很容易写出基类,类似这样:
1 | abstract class BaseListFragment : BaseFragment<MyData>() { |
然后在创建 Fragment List 时,使用匿名类实现getData()
方法来获取数据。就像这样:
1 | viewPager.adapter = object : FragmentPagerAdapter(fragmentManager) { |
但是这样会编译不过:
Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static.
然后加上@SuppressLint("ValidFragment")
注解,可以编译过了,但是运行的时候仍然报错。
Fragment null must be a public static class to be properly recreated from instance state.
异常是在BackStackRecord.doAddOp()
抛出的,看到源码:
1 | private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { |
抛出异常的 3 个条件:
- 匿名类
- 访问域不是 Public
- 是 成员类,但是访问域不是 Static
抛出来的异常也很明显了:Fragment 必须 public static 的类,以便系统可以用初始状态重建。这也和 Fragment newIntance 使用 setArguments 不谋而合。
1 | public static MyFragment newInstance() { |
Fragmnet 经常会被销毁重新实例化,Android Framework 只会调用 Fragment 无参的构造函数。如果直接将参数放到构造函数里,那么 Framework 在重建 Fragment 就会丢失这些参数了。
再看下 Fragment 的实例化:
1 | public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { |
看吧,很清晰了,用的反射取到默认的构造函数,然后创建实例,使用 setArguments 设置参数。
小结一下:
- 不能使用匿名 Fragment
- 不能使用非 public static 的 Fragment
- 参数传递使用 setArguments
那么再回到题头:基类已经有了,怎么样减少类的创建呢?不想再写多个子类。
我的方法是:写一个通用类,在 Fragment 创建后,setAction 来设置 getData 操作。
1 | class CommonListFragment : BaseFragment<MyData>() { |
题外话
遇到 Activity 后台被杀,然后自动重建的问题。
1 | class DetailActivity : BaseActivity() { |
通过 setArguments 设置 KEY_SHOW_SHARE_GUIDE 参数到 DetailFragment,然后操作界面后把 KEY_SHOW_SHARE_GUIDE 从 true 置为了 false。App 进入后台被杀之后,重建回来的 KEY_SHOW_SHARE_GUIDE 会还是 true。
将 onSaveInstanceState 改为如下,问题解决。
1 | override fun onSaveInstanceState(outState: Bundle?) { |