Fragment(一)--Fragment用法常见问题

fragment notes##

fragment相关内容包括

基本定义与使用
回退栈内部实现
fragment通信(与activity 与fragment)
DialogFragment
VP + Fragment
嵌套Fragment
懒加载

基本定义与使用(5个)

fragment依赖于Activity,不能独立存在

一个Activity可以有多个Fragment

一个Fragment可以被多个Activity复用

Fragment有自己的生命周期,并能接受输入事件

能在Activity运行时动态添加或删除Fragment

Fragment优势:(3个)

1.模块化Modularity

从此不用在一个Activity写所有的代码。而是把代码写在各自的Fragment中。

2.可重用Resuability
多个Activity可复用同一个Fragment

3.可适配Adaptability
根据硬件的屏幕尺寸和方向选择不同的布局

Fragment核心类:(3个)

Fragment:任何Fragment的基类

FragmentManager:管理Fragment。FM是抽象类,实现类是FragmentManagerImpl

FragmentTransaction:对Fragment操作,包括添加,删除登需要通过事务处理。FT是抽象类,实现类是BackStackRecord.

Nested Fragment(Fragment内部嵌套Fragment的能力)是Android 4.2提出的.support-fragment库可以兼容到1.6。通过getChildFragmentManager()能够获得管理子Fragment的FragmentManager,在子Fragment中可以通过getParentFragment()获得父Fragment。

	public class Fragment1 extends Fragment{

private static String ARG_PARAM = "param_key";

private String mParam;
private Activity mActivity;

public void onAttach(Context context) {
    mActivity = (Activity) context;
    mParam = getArguments().getString(ARG_PARAM);  //获取参数
}

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_1, container, false);
    TextView view = root.findViewById(R.id.text);
    view.setText(mParam);
    return root;
}

public static Fragment1 newInstance(String str) {
    Fragment1 frag = new Fragment1();
    Bundle bundle = new Bundle();
    bundle.putString(ARG_PARAM, str);
    fragment.setArguments(bundle);   //设置参数
    return fragment;
}
}

Fragment有很多可以复写的方法,其中最常用的就是onCreateView(),该方法返回Fragment的UI布局,需要注意的是inflate()的第三个参数是false,因为在Fragment内部实现中,会把该布局添加到container中,如果设为true,那么就会重复做两次添加,则会抛如下异常

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

创建Fragment时传参必须使用setArguments(Bundle b)###

如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,

原因:

因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据。

这点很重要。否则可能导致严重后果!!!

取参数在onAttached()中取,context上下文也要在该处取###

我们可以在Fragment的onAttach()中通过getArguments()获得传进来的参数,并在之后使用这些参数。如果要获取Activity对象,不建议调用getActivity(),而是在onAttach()中将Context对象强转为Activity对象。

在Activity中添加Fragment的方式有两种:

静态添加:在xml中通过的方式添加,缺点是一旦添加就不能在运行时删除。
动态添加:运行时添加,这种方式比较灵活,因此建议使用这种方式。

虽然Fragment能在XML中添加,但是这只是一个语法糖而已,Fragment并不是一个View,而是和Activity同一层次的

1.Activity提供容器
首先Activity需要有一个容器存放Fragment,一般是FrameLayout,因此在Activity的布局文件中加入FrameLayout:

然后在onCreate()中,通过以下代码将Fragment添加进Activity中。

if (bundle == null) {
getSupportFragmentManager().beginTransaction()
    .add(R.id.container, Fragment1.newInstance("hello world"), "f1")
    //.addToBackStack("fname")
    .commit();
}

需要注意的有如下几点:

因为我们使用了support库的Fragment,因此需要使用getSupportFragmentManager()获取FragmentManager。

add()是对Fragment众多操作中的一种,还有remove(), replace()等,第一个参数是根容器的id(FrameLayout的id,即”@id/container”),第二个参数是Fragment对象,第三个参数是fragment的tag名,指定tag的好处是后续我们可以通过Fragment1 frag = getSupportFragmentManager().findFragmentByTag("f1")从FragmentManager中查找Fragment对象。

在一次事务中,可以做多个操作,比如同时做add().remove().replace()。

commit()操作是异步的。内部通过mManager.enqueueAction()加入处理队列。

对应的同步方法为commitNow(),commit()内部会有checkStateLoss()操作,如果开发人员使用不当(比如commit()操作在onSaveInstanceState()之后),可能会抛出异常解决方案和产生分析

commitAllowingStateLoss()方法则是不会抛出异常版本的commit()方法,但是尽量使用commit(),而不要使用commitAllowingStateLoss()。

addToBackStack("fname")是可选的。FragmentManager拥有回退栈(BackStack),类似于Activity的任务栈,如果添加了该语句,就把该事务加入回退栈,当用户点击返回按钮,会回退该事务(回退指的是如果事务是add(frag1),那么回退操作就是remove(frag1));如果没添加该语句,用户点击返回按钮会直接销毁Activity。

Fragment有一个常见的问题,即Fragment重叠问题,这是由于Fragment被系统杀掉,并重新初始化时再次将fragment加入activity,因此通过在外围加if语句能判断此时是否是被系统杀掉并重新初始化的情况。

Fragment有个常见的异常:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)

从上面看到,先从mAdded中查找是否有该Fragment,如果没找到,再从mActive中查找是否有该Fragment。mAdded是已经添加到Activity的Fragment的集合,mActive不仅包含mAdded,还包含虽然不在Activity中,但还在回退栈中的Fragment。

该异常出现的原因是:commit()在onSaveInstanceState()后调用。首先,onSaveInstanceState()在onPause()之后,onStop()之前调用。onRestoreInstanceState()在onStart()之后,onResume()之前。

因此避免出现该异常的方案有:

不要把Fragment事务放在异步线程的回调中,比如不要把Fragment事务放在AsyncTask的onPostExecute(),因此onPostExecute()可能会在onSaveInstanceState()之后执行。
逼不得已时(即一定会出现事务丢失时)使用commitAllowingStateLoss()。

FragmentTransaction有一些基本方法,下面给出调用这些方法时,Fragment生命周期的变化:

add(): onAttach()->…->onResume()。

remove(): onPause()->…->onDetach()。

replace(): 相当于旧Fragment调用remove(),新Fragment调用add()。

show(): 不调用任何生命周期方法,调用该方法的前提是要显示的Fragment已经被添加到容器,只是纯粹把Fragment UI的setVisibility为true。

hide(): 不调用任何生命周期方法,调用该方法的前提是要显示的Fragment已经被添加到容器,只是纯粹把Fragment UI的setVisibility为false。

detach(): onPause()->onStop()->onDestroyView()。UI从布局中移除,但是仍然被FragmentManager管理。

attach(): onCreateView()->onStart()->onResume()。

Fragment实现原理与BackStack

Fragment向Activity传递数据

首先,在Fragment中定义接口,并让Activity实现该接口

在Fragment的onAttach()中,将参数Context强转为OnFragmentInteractionListener对象:

**FABridge
**

由于通过接口的方式从Fragment向Activity进行数据传递比较麻烦,需要在Fragment中定义interface,并让Activity实现该interface,FABridge通过注解的形式免去了这些定义。

Activity向Fragment传递数据

Activity向Fragment传递数据比较简单,获取Fragment对象,并调用Fragment的方法即可,比如要将一个字符串传递给Fragment,则在Fragment中定义方法

Fragment之间通信

由于Fragment之间是没有任何依赖关系的,因此如果要进行Fragment之间的通信,建议通过Activity作为中介,不要Fragment之间直接通信。

DialogFragment

DialogFragment是Android 3.0提出的,代替了Dialog,用于实现对话框。他的优点是:即使旋转屏幕,也能保留对话框状态。

原文地址:https://www.cnblogs.com/zharma/p/8345963.html