谈谈Fragment的构造函数、重新创建(recreate)及相关

本文分享一些Android Fragment使用经验,不准备详细介绍,只是介绍一些使用注意点,对于有一定开发经验的朋友应该会比较有帮助。对于我理解不对的地方,希望批评指正,谢谢!

1、Fragment初始化
一定要提供默认构造函数。

不能用构造函数传递参数!不要写带参数的构造函数。参数通过下面介绍的方式传递。

原因:Fragment会被重新销毁(Activity销毁的时候它里面的Fragment就被销毁了,可能因为内存不足,手机配置发生变化,横竖屏切换)。在重新创建的时候系统调用的是无参构造函数。

标准做法是:

在Fragment里添加获取Fragment的newInstance函数,以后获取Fragment就使用这个函数,不要使用构造函数新建Fragment!


public static MyFragment newInstance(Bundle args) {
    MyFragment f = new MyFragment();
    f.setArguments(args);
    return f;
}

Fragment内部在初始化的时候需要获取外界传递的参数,这时候就用getArguments获取Bundle,再从Bundle里获取对应的参数。Bundle在Fragment销毁和重新创建的时候持续保存。
比如:


private TextView mTextView;
 
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view=inflater.inflate(R.layout.fragment, container, false);
    initViews(view);
        return view;
}
 
private void initViews(View view){
    mTextView = view.findViewById(R.id.tv);
    Bundle bundle = getArguments();
    mTextView.setText(bundle == null ? "" : bundle.getString("text"));
}

Activity里使用MyFragment时应该这样写:
Bundle args = new Bundle();
args.putString("text","Hello");
MyFragment f = MyFragment.newInstance(args)


其实还是有点小问题,具体见第5点。
2、关于getActivity()
Fragment的方法getActivity()只在被关联到Activity之后才会得到结果,也就是在onAttach和onDetach两个生命周期之间会非空(此时isAdded返回值是true)。其他时候不应该使用Activity!如果要使用,那说明你设计得不好。
使用所依附的Activity时应该判断getActivity是否为空或者isAdded是否为true。

Fragment依附的Activity随时可能被destroy掉!很多时候是在不经意间。机型适配的时候就会发现。


3、Activity引用
Fragment里不应该保留Activity引用!需要用到的时候通过getActivity()获取,因为那个引用不仅会导致内存泄露,而且在你用的时候,那个Activity可能已经不是正在显示的那个Activity了,这个Fragment也可能已经不是正在显示的那个Fragment了。
4、关于生命周期
通过add添加Fragment会触发Fragment生命周期,hide和show不会触发生命周期。

像微信那样,一个Activity里有四个Fragment,下面四个按钮,点击一个按钮显示其中一个Fragment。这种情况下,为了优化性能,你可以这样做:

起初只add第一个Fragment,其余三个Fragment都不添加。点击某个标签的时候先看对应的Fragment是否已经添加,没有则new一个并add这个Fragment,隐藏其他所有Fragment;如果已经添加了就直接show这个Fragment,隐藏其他所有Fragment。

5、关于Fragment销毁和重建
(1)Activity因内存不足、配置变化等原因被销毁的时候,包含的Fragment也会被销毁,无论此Fragment是否有id,系统都会调用Fragment的onSaveInstanceState,并且保留之前setArguments的Bundle,并且Activity的FragmentManager里的Fragment会被记录。

(2)由于(1)的原因,如果一个Activity里的fragment是通过add方式添加的,那么如果出现上述情况,就必须在Activity的add地方判断是否应该重新添加Fragment,避免重复添加。

(3)刚刚在第1点时说到Fragment在Activity中的使用。那个地方说得还是有点问题。其实应该给每个Fragment设一个tag,在FragmentTransaction.add方法添加Fragment的时候,第三个参数使用这个tag。初始化的时候,应该先从FragmentManager里查看这个Fragment是否已经有,如果有就直接使用原来的,如果没有再新建。

系统在重建Activity时会重建Fragment(调用Fragment的默认构造函数),并且将这些添加到FragmentManager里,并且设置之前setArguments时使用的Bundle。此时系统回调Fragment的onCreate(Bundle)、onActivityCreated(Bundle)、onViewStateRestored(Bundle)这类方法的时候,参数不为空。

注意:新建的Fragment和原来对应的Fragment不是同一个实例!

Activity内正确使用Fragment的代码如下:

FragmentManager fm = getSupportFragmentManager();
Fragment ft = fm.findFragmentByTag("MyFragment");
Bundle args = new Bundle();
args.putString("text","Hello");
MyFragment = ft == null ? MyFragment.newInstance(args) : (MyFragment)ft;
if (savedInstanceState==null){
    fm.beginTransaction()
        .add(R.id.fragment, ft, "MyFragment")
        .commitAllowingStateLoss();
}

--------------------- 
作者:小飞_Xiaofei 
来源:CSDN 
原文:https://blog.csdn.net/xiaofei_it/article/details/45675497 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果要在不重新创建 Fragment 的情况下切换布局,可以通过 `ViewStub` 来实现。 `ViewStub` 是一个轻量级的视图,它可以被声明在布局文件中但不会立即被加载。当你需要使用它时,它会被填充并替换成你指定的布局文件。 具体实现方法如下: 1. 在 Fragment 的布局文件中添加 `ViewStub`。 ```xml <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"> <ViewStub android:id="@+id/stub" android:layout="@layout/layout1" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> ``` 2. 在 Fragment 中定义 `ViewStub` 和两个布局文件对应的布局 ID。 ```java private ViewStub mViewStub; private View mInflatedView1; private View mInflatedView2; ``` 3. 在 `onCreateView()` 方法中获取 `ViewStub` 并设置它的监听器。 ```java @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_layout, container, false); mViewStub = view.findViewById(R.id.stub); mViewStub.setOnInflateListener(new ViewStub.OnInflateListener() { @Override public void onInflate(ViewStub stub, View inflated) { if (inflated.getId() == R.id.layout1) { mInflatedView1 = inflated; } else { mInflatedView2 = inflated; } } }); return view; } ``` 4. 在需要切换布局的地方调用 `inflate()` 方法。 ```java if (mInflatedView1 == null) { mViewStub.setLayoutResource(R.layout.layout1); mInflatedView1 = mViewStub.inflate(); } else { mViewStub.setLayoutResource(R.layout.layout2); mInflatedView2 = mViewStub.inflate(); } ``` 这样就可以在同一个 Fragment 中通过 `ViewStub` 来切换不同的布局文件了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值