Fragment学习笔记

1.背景

作为一个刚刚工作不久的安卓小白,项目代码中全是Fragment,虽然一段时间接触下有了一定的了解,但始终有一些地方觉得有点懵懵懂懂的,所以打算来详细了解一下这个控件。

2.博客阅读

阅读之前的几个问题:

  • fragment是什么,和activity有什么区别
  •  fragment的生命周期
  • 从一个fragment A跳转至fragment B,再返回fragment A,这个过程中发生了什么
  • fragment和其他控件之间的关系
  •  fragment的特殊用途

【博客】Android Fragment 真正的完全解析(上)

地址:https://blog.csdn.net/lmj623565791/article/details/37970961

 fragment的生命周期

Fragment必须是依存于Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。

上图:

fragment的使用

静态使用:

使用方法比较简单,把fragment当textview之类的直接放在布局文件中即可,fragment中的控件可以直接用findViewById获取、控制

动态使用:

涉及到FragmentManager、FragmentTransaction的使用。FragmentManager 主要用于在Activity中操作Fragment,FragmentTransaction 保证一些列Fragment操作的原子性。

一些重要的方法:

代码/方法说明
getSupportFragmentManager获取FragmentManage的方式
getFragmentManager()获取FragmentManage的方式
FragmentTransaction transaction = fm.benginTransatcion();开启一个事务
transaction.add()往Activity中添加一个Fragment
transaction.remove()从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁
transaction.replace()使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体
transaction.hide()隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show()显示之前隐藏的Fragment
detach()会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护
attach()重建view视图,附加到UI上并显示
transatcion.commit()提交一个事务。commit方法一定要在Activity.onSaveInstance()之前调用

一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们

  1. 比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。
  2. 再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。
  3. remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach

【博客】Android Fragment 真正的完全解析(下)

地址:https://blog.csdn.net/lmj623565791/article/details/37992017

 Fragment回退栈

如何添加一个Fragment事务到回退栈:FragmentTransaction.addToBackStack(String)

所以,fragment是手动添加到回退栈的!!!

Fragment与Activity通信

  • 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
  • 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。
  • 在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。
  • Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

如何处理运行时配置发生变化

通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建,从而决定是否实例化新的Fragment

Fragmeny与ActionBar和MenuItem集成

Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。

  1. 在Fragment的onCreate中调用 setHasOptionsMenu(true);
  2. 然后在Fragment子类中实现onCreateOptionsMenu
  3. 如果希望在Fragment中处理MenuItem的点击,也可以实现onOptionsItemSelected;当然了Activity也可以直接处理该MenuItem的点击事件。

没有布局的Fragment的作用

使用Fragment创建对话框

【博客】Android Fragment完全解析,关于碎片你所需知道的一切

Fragment基础要点

1. 内部加载布局的`inflate()`的第三个参数必须是`false`,否则会导致重复添加,抛出异

2. Fragment需要传入参数该怎么做?

应该通过setArguments(Bundle bundle)方法添加,之后通过onAttach()中的getArguments()获得传进来的参数。

不建议通过给Fragment添加带参数的构造函数来传参数,这样内存紧张时被系统杀掉会导致无法恢复数据

3. Fragment中获取`Activity`对象

不应该使用getActivity()获取

应该在onAttach()中将Context对象强转为Activity对象

4. 不能在onSaveInstanceState()后调用commit()

onSaveInstanceState()在onPause()和onStop()之间调用。

onRestoreInstanceState()在onStart()和onResume()之间调用。

避免异常方法:

不要把Fragment事务放在异步线程的回调中,比如不要把Fragment事务放在AsyncTask的onPostExecute()—onPostExecute()可能会在onSaveInstanceState()之后执行。

无可奈何时使用commitAllowingStateLoss(),一般不推荐。

5. FragmentTransaction的`show()、hide()`方法

Fragment中重写onHiddenChanged()可以对Fragment的hide、show状态进行监听

6. FragmentTransaction的`setCustomAnimations()`

给Fragment的进入/退出设置指定的动画资源

getSupportFragmentManager不支持属性动画,仅支持补间动画。getFragmentManager支持属性动画。

setCustomAnimations一定要放在add、remove…等操作之前。

   例如:

 //1. 载入Fragment动画
   getFragmentManager().beginTransaction()
     //1. 设置动画
     .setCustomAnimations(R.animator.enter_animator, R.animator.exit_animator)
       .add(R.id.main_java_activity_container,  new BlankFragment())
         .commit();
   //2. 销毁Fragment时动画(support-v4中只能用)
   getSupportFragmentManager().beginTransaction()
                       .setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim)
                       .remove(getSupportFragmentManager().findFragmentById(R.id.fl_main))
                       .commit();

Fragment与ViewPager

1. `ViewPager`常用于实现`Fragment的左右滑动切换`的效果
2. `ViewPager`会缓存当前页相邻的界面,比如当滑动到第2页时,会初始化第1页和第3页的Fragment对象,且生命周期函数运行到onResume()。
3. 通过`ViewPager的setOffscreenPageLimit(count)`设置离线缓存的界面个数。

3.源码阅读分析

这里首先阅读的是`android.app.Fragment`。类头注释翻译过来内容大致是这样的:

Fragment是应用程序的用户界面或行为的一部分,可以放在Activity中。与片段的交互是通过FragmentManager完成的。Fragment是依存于Activity的,它的生命周期和它关联的Activity强相关,与Fragment通信依赖于FragmentManager。所有Fragment的子类必须有无参构造函数,否则可能引发运行时异常。

这段注释比较长,还介绍了fragment的生命周期,回退栈等,这里就不一一说明了。

setArguments()

public void setArguments(Bundle args) {
        // The isStateSaved requirement below was only added in Android O and is compatible
        // because it loosens previous requirements rather than making them more strict.
        // See method javadoc.
        if (mIndex >= 0 && isStateSaved()) {
            throw new IllegalStateException("Fragment already active");
        }
        mArguments = args;
    }

看这个if就知道,这个方法在必须在fragment添加到FragmentManager之前调用。

setTargetFragment

    public void setTargetFragment(Fragment fragment, int requestCode) {
        // Don't allow a caller to set a target fragment in another FragmentManager,
        // but there's a snag: people do set target fragments before fragments get added.
        // We'll have the FragmentManager check that for validity when we move
        // the fragments to a valid state.
        final FragmentManager mine = getFragmentManager();
        final FragmentManager theirs = fragment != null ? fragment.getFragmentManager() : null;
        if (mine != null && theirs != null && mine != theirs) {
            throw new IllegalArgumentException("Fragment " + fragment
                    + " must share the same FragmentManager to be set as a target fragment");
        }

        // Don't let someone create a cycle.
        for (Fragment check = fragment; check != null; check = check.getTargetFragment()) {
            if (check == this) {
                throw new IllegalArgumentException("Setting " + fragment + " as the target of "
                        + this + " would create a target cycle");
            }
        }
        mTarget = fragment;
        mTargetRequestCode = requestCode;
    }

targetFragment和当前的fragment必须属于同一个Fragmentmanager, 不能形成一个target环(虽然还不知道这个方法有什么用)

有两个Manager:mFragmentManager和mChildFragmentManager 个人理解,前者是管理该Fragment, 后者是管理该Fragment中的子Fragment。

final public FragmentManager getChildFragmentManager() {
        if (mChildFragmentManager == null) {
            instantiateChildFragmentManager();
            if (mState >= RESUMED) {
                mChildFragmentManager.dispatchResume();
            } else if (mState >= STARTED) {
                mChildFragmentManager.dispatchStart();
            } else if (mState >= ACTIVITY_CREATED) {
                mChildFragmentManager.dispatchActivityCreated();
            } else if (mState >= CREATED) {
                mChildFragmentManager.dispatchCreate();
            }
        }
        return mChildFragmentManager;
    }

有一个空方法,是需要在子类里重写吗?

public void onHiddenChanged(boolean hidden) {
}

看Fragment看不出什么,要搭配FragmentManager

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值