关于android的fragment的一些说明

android fragment

  • 我们会经常被推荐使用fragment,但是当我们看到一个activity中啥也没做,就只是添加了一个fragment时,我们可能会疑惑,这样子我还不如一个activity就搞定了,为何还要用fragment
  • 但是当你面临一个新需求,现在要把两个activity的界面组合在一起,比如平板由于界面比较大,完全可以在一屏显示两个activity的界面,此时你如果之前是按照fragment的写法,那么面对这个需求你能很轻松就实现了,但如果你之前只按照activity的写法,你能怎么做,重新写一个activity来组合之前两个activity的代码吗?
  • 现在你应该知道fragment的重要性了吧

Activity和Fragment的生命周期

在这里插入图片描述
在这里插入图片描述

onResume

  • fragment的onResume并不是指fragment对用户可见时就会触发,这个onResume只是同它attach的Activity的onResume一样的,因此你别指望用onResume来作为fragment是否对用户可见的判断

popBackStack

  • 当我们调用addToBackStack时,fragment就会入栈,那么怎么将fragment出栈呢
  • 这就需要用到popBackStack,在没有任何参数的情况下popBackStack会把栈顶出栈
  • 当然我们也可以通过名字指定从哪里开始出栈,而这个名字就是addToBackStack时设置的那个名字,一般我们都以类名作为名字
  • popBackStack并不会立即执行,如果你想立马执行,可以使用popBackStackImmediate或executePendingTransactions
  • popBackStack还有个flag的参数设置,这个设为0的话,表示只出栈其之上的,它自身还留在栈中,如果是POP_BACK_STACK_INCLUSIVE的话,则表示将其自身以及自身之上的全都出栈

问答

fragment和activity如何相互调用

  • 在fragment中可以通过**getActivity()**方法获得其附着的Activity的引用,然后就可以调用Activity的相关方法了,如
View listView = getActivity().findViewById(R.id.list);
  • 也可以在onAttach时来实例自定义接口,如
public static class FragmentA extends ListFragment {
    OnArticleSelectedListener listener;
    ...
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (OnArticleSelectedListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

  • 在Activity中可以通过FragmentManager的 findFragmentById()findFragmentByTag()popBackStack()addOnBackStackChangedListener() 来管理fragment

调用remove()或replace()方法时,原先的fragment会被销毁吗

  • 会不会销毁得看你的fragment在事务中有没有调用addToBackStack(),如果没有调用,则原先的fragment会被销毁(即执行到onDetach),否则只会执行到onDestroyView

add()和replace()有什么区别

  • 可以这么理解,add就像是图层叠加,如果你的fragment背景是透明的,当你add多个fragment,你会看到画面叠加在一起
  • replace就相当于把前面已经有的图层移走,因此即使你replace的fragment背景是透明的,也看不到前面的图层的画面
  • add()方法不会销毁原先的fragment,而replace()则有可能会销毁原先的fragment,为什么说有可能呢,这是因为销不销毁得看原先的fragment在replace()的事务中有没有调用addToBackStack(),有的话那就不会销毁

怎么知道堆栈中还有多少个fragment

  • 可以通过getSupportFragmentManager().getBackStackEntryCount()或者getSupportFragmentManager().getFragments().size()获得

Activity中的fragment什么时候会被销毁

  • 当Activity被销毁时,则附着于此Activity的fragment都会被销毁,值得注意的是Activity先于fragment被销毁

addToBackStack()的作用是什么

  • 在add或remove或replace片段(fragment)时,如果调用了addToBackStack方法,则当按物理返回键时会执行回退动作,例如依次加入了a,b,c,则返回时会回到b,再回到a再退出

如何在fragment之间传递数据

fragment中的getContext和getActivity有何区别

  • 在androidx.fragment.app.Fragment中,
 	@Nullable
    final public FragmentActivity getActivity() {
        return mHost == null ? null : (FragmentActivity) mHost.getActivity();
    }
	@Nullable
    public Context getContext() {
        return mHost == null ? null : mHost.getContext();
    }

  • 从代码中我们可以看到getContext和getActivity都和mHost有关,因此只要mHost为null,getContext和getActivity也就为null了,而mHost是在attach时被赋值的,在detach时被置为null的,因此只要fragment生命周期执行到了onDetach,我们就不能再使用getContext和getActivity了,否则就会产生空指针异常
  • 上面我们已经知道getContext和getActivity都和mHost有关,那么mHost是个什么类型的对象呢
  • 翻看源码可以知道mHost是FragmentHostCallback的对象实例,于是转向查看FragmentHostCallback的构造函数
  • 在androidx.fragment.app.FragmentHostCallback中,
 public FragmentHostCallback(@NonNull Context context, @NonNull Handler handler,
            int windowAnimations) {
        this(context instanceof Activity ? (Activity) context : null, context, handler,
                windowAnimations);
    }

    FragmentHostCallback(@NonNull FragmentActivity activity) {
        this(activity, activity /*context*/, new Handler(), 0 /*windowAnimations*/);
    }

    FragmentHostCallback(@Nullable Activity activity, @NonNull Context context,
            @NonNull Handler handler, int windowAnimations) {
        mActivity = activity;
        mContext = Preconditions.checkNotNull(context, "context == null");
        mHandler = Preconditions.checkNotNull(handler, "handler == null");
        mWindowAnimations = windowAnimations;
    }
  • 我们可以从构造函数中得知,其实getContext和getActivity都是同一个对象,当你打印日志的时候也能发现他们都是同一个对象
  • 在这里插入图片描述
  • 而且在onAttach回调时getContext和getActivity就不为null了,可以正常使用

销毁时是Activity先销毁还是Fragment先销毁

  • 如果fragment没有加入回退栈中,则当我们跟踪生命周期会发现Activity先执行了onDestroy,然后才轮到Fragment的onDestroyView
  • 也就是可能跟我们想的不太一样,Activity先被销毁了,附加在此Activity上的Fragment才被销毁

当屏幕旋转时,Fragment被实例多次,该怎么解决

  • 当屏幕旋转时,我们会发现fragment被创建了两个,再旋转回来时,此时fragment已经有3个了,也就是旋转n次,就会有n+1个fragment,
  • 这种情况不管是add和replace都会出现,不同的是add会导致画面叠加,所以你看灰色文字会变深,而replace则不会导致画面叠加,灰色文字还是原来的灰色
  • 而且这种情况跟是否有addToBackStack没关系,只要屏幕旋转,fragment实例对象就会增加一个
  • 当你屏幕旋转两次时,你会在日志中发现总共创建了6个fragment实例对象
    在这里插入图片描述
  • 造成这种现象的原因是系统由于配置变化(如屏幕旋转)或内存不足杀掉原先的Activity再重启Activity时会还原原先的fragment,如果在Activity的onCreate中没有判断这种重启的情况,则会产生多个fragment实例,一个是系统自动恢复产生的,一个是onCreate中再次创建的
  • 知道原因后可以在onCreate中通过findFragmentByTag来判断fragment是否存在,存在则不要重新创建了,例如
if (savedInstanceState != null) {
            Fragment fragment = mFragmentManager.findFragmentByTag(AFragment.class.getSimpleName());
            if (fragment != null) {
                mFragmentManager.beginTransaction()
                        .show(fragment)//这里直接show即可,如果用add则会抛异常,因为之前已经add过了,同一个实例不能add两次
                        .commit();
            }

        } else {
            mFragmentManager.beginTransaction()
                    .replace(R.id.frag_container, AFragment.newInstance("", ""), AFragment.class.getSimpleName())
                    .commit();
        }
  • 另外在fragment中的onCreate方法中可以调用 setRetainInstance(true)来保留fragment,这样配置发生变化时fragment还能保持原来那个实例
 class RetainFragment extends Fragment {
        private static final String TAG = "RetainFragment";

        public RetainFragment() {}

        public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
            RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
            if (fragment == null) {
                fragment = new RetainFragment();
                fm.beginTransaction().add(fragment, TAG).commit();
            }
            return fragment;
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setRetainInstance(true);
        }
    }

如何获得前台fragment

/**
     * 获得当前栈顶fragment,只适用于addToBackStack是指定的name和add/replace时指定的tag一致的情况
     *
     * @return
     */
    public Fragment getTopFragment() {
        int n = fragmentManager.getBackStackEntryCount();
        if (n > 0) {
            FragmentManager.BackStackEntry entry = fragmentManager.getBackStackEntryAt(n - 1);
            return fragmentManager.findFragmentByTag(entry.getName());
        }
        return null;
    }

有时发现fragment的内容会顶到状态栏去

在这里插入图片描述

  • 遇到这种情况在根布局加上android:fitsSystemWindows=“true” 即可

其他

  • 有时你可能遇到如下错误:
 Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead [ValidFragment]
  • 这是因为你重载了了fragment的构造方法,但是在一些情况下,如屏幕翻转时,fragment被重新创建,就可能会造成数据丢失
  • 因此参数传递推荐使用Fragment.setArguments

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值