一、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、 事务最终的提交方法有四种
commit()
commitAllowingStateLoss()
commitNow()
commitNowAllowingStateLoss()
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 的使用和区别
Fragment的replace、add、hide、show的使用和详解_345。的博客-CSDN博客_fragment的replace方法
7、Fragment 懒加载机制实现
Androidx Fragment 懒加载机制实现 - 灰色飘零 - 博客园
viewpager+fragment 懒加载_黑马不是码的博客-CSDN博客_viewpager+fragment懒加载
ViewPager+TabLayout+Fragment懒加载机制完全解析_彳亍走的猪的博客-CSDN博客