Bootstrap

33、Fragment使用方法详解

一、Fragment的生命周期

Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:

可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach(Activity)
当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图
onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用
onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用
onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用
注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现,
 

1、Fragment的创建

创建Fragment类,继承Fragment,在Fragment类中的onCreateView()方法中绑定对应的Xml布局,类似Activity类中的onCreate()方法

Fragment的布局文件和平时Activity中的没什么区别,就是你需要向Activity中添加的碎片内容。

2、向Activity中添加Fragment

(1)用Xml布局写死(静态的使用Fragment)

创建需要在Activity中加载的Fragment碎片(fragment类),然后在Activity类绑定的布局中用<frament…>方式写入要加载的Fragment,指定加载的Fragment类是由<fragment…>中的name属性决定的。这种方法虽然简单,但也限制了Fragment使用的灵活性和复用,可以视情况而定。

(2)在Activity类中用代码动态添加

注:replace()方法使用起来方便,但最大的问题是,每次执行都会把之前的fragment队列中的所有fragment对象全部清除,然后重新加载一个新的fragment进去,相当于使用remove()+ add();这会造成每次页面切换的时候都重新加载页面,如果你里面有网络请求等耗时操作,就更浪费资源。
add()方法是使用add()  +   show() +  hide()三个方法结合使用。使用这三个方法你会发现 当第二次切换时,oncreateview()方法就不再执行。add方式你切换fragment不会让fragment重新刷新,而用replace方式会使fragment重新刷新,因为add方式是将fragment隐藏了而不是销毁再创建,replace方式每次都是重新创建。

  (2.1)使用replace方法

   使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

xml布局:

<FrameLayout
        android:id="@+id/right_layout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" >

</FrameLayout>

代码:

  AnotherRightFragment fragment = new AnotherRightFragment();
  FragmentManager fragmentManager = getSupportFragmentManager();
  FragmentTransaction transaction = fragmentManager. beginTransaction();
  transaction.replace(R.id.right_layout, fragment); 
  transaction.addToBackStack(null);
  transaction.commit();

  (2.2)使用add方法

res/values/strings.xml文件:
<resources>
    <string-array name="tab">
        <item>home</item>
        <item>video</item>
        <item>dis</item>
        <item>mine</item>
    </string-array>
</resources>
String[] tabSArray = getResources().getStringArray(R.array.tab);
ArrayList<String>  mFragmentTags = new ArrayList<>(Arrays.asList(tabSArray));
FragmentManager fragmentManager = getSupportFragmentManager();
private void showFragment() {
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
    Fragment fragment = mFragmentManager.findFragmentByTag(mFragmentTags.get(mCurrIndex));
    if (fragment == null) {
        fragment = instantFragment(mCurrIndex);
    }
    for (int i = 0; i < mFragmentTags.size(); i++) {
        Fragment f = mFragmentManager.findFragmentByTag(mFragmentTags.get(i));

        if (f != null && f != fragment && f.isAdded()) {
            fragmentTransaction.hide(f);
        }
    }
    if (fragment.isAdded()) {
        fragmentTransaction.show(fragment);
    } else {
        fragmentTransaction.add(R.id.fragment_container, fragment, mFragmentTags.get(mCurrIndex));
    }

    fragmentTransaction.commitAllowingStateLoss();(或fragmentTransaction.commit();)
    mFragmentManager.executePendingTransactions();
}
private Fragment instantFragment(int currIndex) {
    switch (currIndex) {
        case 0:
            return new HomeFragment();
        case 1:
            return new VideoFragment();
        case 2:
            return new DiscoverFragment2();
        case 3:
            return new MineFragment2();
        default:
            return new HomeFragment();
    }
}

详情:Fragment进阶-FragmentTransaction详解 - 简书

3、碎片和活动之间进行通信

(1)活动中调用碎片里的方法

  为了方便碎片和活动之间进行通信,FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例,代码如下所示:

        RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);

  调用FragmentManager的findFragmentById()方法,可以在活动中得到相应碎片的实例,然后就能轻松地调用碎片里的方法了。

(2)碎片中调用活动里的方法

  在每个碎片中都可以通过调用getActivity()方法来得到和当前碎片相关联的活动实例,代码如下所示:

    MainActivity activity = (MainActivity) getActivity();

  有了活动实例之后,在碎片中调用活动里的方法就变得轻而易举了。另外当碎片中需要使用Context对象时,也可以使用getActivity()方法,因为获取到的活动本身就是一个Context对象了。

另也可在构造fragment的时候, 通过Bundle传递参数.

Activity类中:
HomeFeedFragment  feedFragment = HomeFeedFragment.newInstance(entity);

HomeFeedFragment 类中:
public static HomeFeedFragment newInstance(HomeMenuCateEntity entity) {
    HomeFeedFragment feedListFragment = new HomeFeedFragment();
    Bundle bundle = new Bundle();
    bundle.putSerializable(INSTANCE_TAG, entity);
    feedListFragment.setArguments(bundle);
    return feedListFragment;
}

(3)碎片和碎片之间的通信

首先在一个碎片中可以得到与它相关联的活动,然后再通过这个活动去获取另外一个碎片的实例,这样也就实现了不同碎片之间的通信功能。

4、Fragment家族常用的API

Fragment常用的三个类:

android.app.Fragment 主要用于定义Fragment

android.app.FragmentManager 主要用于在Activity中操作Fragment

android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白~

a、获取FragmentManage的方式:

getFragmentManager() // v4中,getSupportFragmentManager

- getSupportFragmentManager():在Activity中使用Fragment的管理器,对所有Fragment进行管理。 
- getFragmentManager():与 getSupportFragmentManager()功能是一样的,只是是在Fragment中使用 
- getChildFragmentManager():在Fragment嵌套使用中经常使用到

b、主要的操作都是FragmentTransaction的方法

FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务

getFragments()

可以获取所有创建时候add进去的所有Fragment;通常可以通过这个api来获取需要指定操作的fragment对象

manager.findFragmentByTag(String tag)

通过TAG获取指定的Fragment;这个TAG,是在创建Fragment时,调用addToBackStack(String tag)进行绑定关系的

popBackStack()

 弹出栈顶fragment

popBackStack(String tag,int flags)

1.  tag可以为null或者相对应的tag,flags只有0和1(POP_BACK_STACK_INCLUSIVE)两种情况
2.  如果tag为null,flags为0时,弹出回退栈中最上层的那个fragment。
3.  如果tag为null ,flags为1时,弹出回退栈中所有fragment。
4.  如果tag不为null,那就会找到这个tag所对应的fragment,flags为0时,弹出该 fragment以上的Fragment,如果是1,弹出该         fragment(包括该fragment)以 上的fragment。
popBackStackImmediate

相关的方法与上面逻辑是一样的与上面不同的是,在调用的时候会立即执行弹出。
 

transaction.add() 

往Activity中添加一个Fragment

transaction.remove()

从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。

transaction.replace()

使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

transaction.hide()

隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

transaction.show()

显示之前隐藏的Fragment

transaction.addToBackStack(null)

添加一个Fragment事务到回退栈

回退栈详情:Android Fragment 真正的完全解析(下)_鸿洋_的博客-CSDN博客

detach()

会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

attach()

重建view视图,附加到UI上并显示。

transatcion.commit()

提交一个事务

transatcion.commitAllowingStateLoss()

提交一个事务(commit()方法必须在状态存储之前调用,否则会抛出异常,如果觉得状态丢失没关系,可以调用)

注:

commit()方法并不立即执行transaction中包含的动作,而是把它加入到UI线程队列中.
如果想要立即执行,可以在commit之后立即调用FragmentManager的
executePendingTransactions()方法.

commit()方法必须在状态存储之前调用,否则会抛出异常,如果觉得状态丢失没关系,可以调用commitAllowingStateLoss(). 但是除非万不得已, 一般不推荐用这个方法, 会掩盖很多错误.

注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。

a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。

b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。

c、remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。

4、 事务最终的提交方法有四种

  1. commit()
  2. commitAllowingStateLoss()
  3. commitNow()
  4. commitNowAllowingStateLoss()

Android 进阶17:Fragment FragmentManager FragmentTransaction 深入理解_拭心的博客-CSDN博客_android fragmenttransaction

Fragment的FragmentTransaction 的commit()和commitAllowingStateLoss()以及commitNow()和commitNowAllowingState_wings专栏的博客-CSDN博客

FragmentTransaction 的 replace, add, hide, show 的使用和区别_MrFengZH的博客-CSDN博客_android fragmenttransaction hide

5、Android 中子fragment控制父对象改变布局

5.1、父Activity嵌套子 fragment,在Fragment中修改父Activity中的控件

        //在Fragment中使用Activity中控件的方式
        AppCompatActivity activity = (AppCompatActivity) getActivity();
        Toolbar mToolBar = (Toolbar) activity.findViewById(R.id.toolbar);

        mToolBar.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

               mHeaderListView.smoothScrollToPosition(0);

            }
        });


5.2、fragment嵌套fragment, 子fragment控制父对象改变布局

((ParentFragment)(ChildFragment.this.getParentFragment())).changeMs();
同时保证在父布局ParentFragment里面存在公共的方法changMs()方法

6、FragmentTransaction 的 replace, add, hide, show 的使用和区别

FragmentTransaction 的 replace, add, hide, show 的使用和区别_MrFengZH的博客-CSDN博客_android fragmenttransaction hide

Fragment的replace、add、hide、show的使用和详解_345。的博客-CSDN博客_fragment的replace方法

7、Fragment 懒加载机制实现  

Androidx Fragment 懒加载机制实现 - 灰色飘零 - 博客园

viewpager+fragment 懒加载_黑马不是码的博客-CSDN博客_viewpager+fragment懒加载

ViewPager+TabLayout+Fragment懒加载机制完全解析_彳亍走的猪的博客-CSDN博客


 

;