Activity中使用Fragment笔记
来源:互联网 发布:评定nba历史地位的算法 编辑:程序博客网 时间:2024/05/21 17:19
一 Activity中添加Fragment方式
(1)Xml布局文件添加,如下
<fragment android:id="@+id/fragment" android:name="xxx.xxx.MyFragment" android:layout_width="match_parent" android:layout_height="match_parent" />
(2)View中动态添加
Fragment fragment = getSupportFragmentManager().findFragmentByTag("tag");if(fragment == null){ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); fragment = MyFragment.newInstance(); transaction.add(R.id.fragment_container, fragment, "tag"); transaction.commit();}
或
Fragment fragment = null;if(savedInstanceState == null){ fragment = MyFragment.newInstance(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.fragment_container, fragment, "tag"); transaction.commit();}else{ fragment = getSupportFragmentManager().findFragmentByTag("tag");}
之所以在添加Fragment前判断是否有存在相同的Fragment,是因为在内存不足或横竖屏切换(如未在android:configChanges设置)时,会保存其内的Fragment状态信息。当Activity被创建显示时,Fragment会被自动恢复(恢复Fragment和状态的代码在Fragment Activity的onCreate方法中)。如果不判断,则添加多个相同的Fragment。
在添加Fragment时,如果需要向其传递一些初始化的参数,则可使用Bundle来传递。在Fragment内使用getArguments()获取参数。代码如下:
//------ 传递参数 ------Bundle data = new Bundle();//除了基本数据类型的值,还可以传递序列化和实现Parcelable类型的数据data.putString(key, value);...fragment.setArguments(data);//------ 获取参数 ------Bundle data = getArguments();if(data != null){ //获取参数 String value = data.getString(key); ...}
(3)使用TabLayout和ViewPager添加
在布局文件添加:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="40dp" /> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_below="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="match_parent" /></RelativeLayout>
给ViewPager设置Fragment适配器和TabLayout绑定ViewPager:
TabLayout table_layout = (TabLayout) findViewById(R.id.tab_layout);ViewPager view_pager = (ViewPager) findViewById(R.id.view_pager);//设置自定义的Fragment适配器view_pager.setAdapter(new MyFragAdapter(getSupportFragmentManager()));//TabLayout绑定ViewPagertable_layout.setupWithViewPager(view_pager);table_layout.setTabMode(TabLayout.MODE_FIXED);
自定义的Fragment适配器必须要继承FragmentStatePagerAdapter或FragmentPagerAdapter,而这两个类则继承了android.support.v4.view.PagerAdapter。FragmentStatePagerAdapter与FragmentPagerAdapter区别则体现在Fragment的添加,移除和状态保存这三个方面。FragmentStatePagerAdapter添加Fragment时会缓存到mFragments集合中,并恢复其状态(如果有保存状态),而当不可见时则会先使用Fragment.SavedState来保存其状态信息,将其在mFragments的缓存置为null,然后直接从FragmentManager中remove掉。而FragmentPagerAdapter直接使用FragmentManager管理Fragment缓存,当不可见时,只会调用detach将其与Activity分离。
FragmentStatePagerAdapter的instantiateItem和destroyItem方法代码(使用…省略部分代码):
@Overridepublic Object instantiateItem(ViewGroup container, int position) { ... //本地缓存有(非FragmentManager缓存),则直接返回 if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } ... //创建实例 Fragment fragment = getItem(position); ... if (mSavedState.size() > position) { Fragment.SavedState fss = mSavedState.get(position); //恢复保存的状态信息 if (fss != null) { fragment.setInitialSavedState(fss); } } //增加本地缓存 while (mFragments.size() <= position) { mFragments.add(null); } ... //添加到本地缓存 mFragments.set(position, fragment); mCurTransaction.add(container.getId(), fragment); return fragment;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) { ... //增加状态缓存 while (mSavedState.size() <= position) { mSavedState.add(null); } //判断是否保存Fragment状态信息 mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null); //清除Fragment对象缓存 mFragments.set(position, null); //从FragmentManager中移除 mCurTransaction.remove(fragment);}...@Overridepublic Parcelable saveState() { Bundle state = null; //保存状态信息 ... return state;}@Overridepublic void restoreState(Parcelable state, ClassLoader loader) { if (state != null) { //恢复状态信息 ... }}
FragmentPagerAdapter的instantiateItem和destroyItem方法代码:
@Overridepublic Object instantiateItem(ViewGroup container, int position) { ... final long itemId = getItemId(position); String name = makeFragmentName(container.getId(), itemId); //获取FragmentManager缓存 Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { //attach到当前Activity mCurTransaction.attach(fragment); } else { //创建实例 fragment = getItem(position); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.detach((Fragment)object);}...@Overridepublic Parcelable saveState() { //默认未做状态信息保存 return null;}@Overridepublic void restoreState(Parcelable state, ClassLoader loader) { //默认未恢复状态信息}
由这几个方法对比可知:
FragmentStatePagerAdapter适用于管理大数量Fragemnt,如新闻页面,根据不同的频道显示对应的新闻信息,占用内存较少,保存状态信息有利于再次快速回显数据。
FragmentPagerAdapter使用少量的固定的Tab页面,所有被显示的Fragment都会保存在内存中,这使得切换页面时显示更快,同时也会占用一定的内存。
在使用ViewPager作为Fragment容器时,需要根据显示Fragmet的数量来使用Adapter,以免占用过多的内存或切换页面时内容显示缓慢。
二 Fragment中使用Fragment
Fragment中添加子Fragment的方式与Activity中添加方式基本相同,只是获取FragmentManager对象的方法不同。Activity中使用getSupportFragmentManager()方法获取,Fragment中需要使用getChildFragmentManager(),而不是调用getFragmentManager()。
Fragment的getFragmentManager()代码注释:
Return the FragmentManager for interacting with fragments associated with this fragment’s activity
获取与Fragment关联的Activity交互的FragmentManager。
Fragment的getFragmentManager()代码注释:
Return a private FragmentManager for placing and managing Fragments inside of this Fragment.
获取Fragment内一个管理子Fragment的私有的FragmentManager。
子Fragment使用startActivityForResult()方法在低版本时onActivityResult得不到执行完成的回调问题。
先看看android 4.4源码中的FragmentActivity的onActivityResult代码:
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { mFragments.noteStateNotSaved(); int index = requestCode>>16; if (index != 0) { index--; ... //mFragments为FragmentManagerImpl实例,通过getSupportFragmentManager()获取的即该实例。 Fragment frag = mFragments.mActive.get(index); if (frag == null) { ... } else { frag.onActivityResult(requestCode&0xffff, resultCode, data); } return; } super.onActivityResult(requestCode, resultCode, data);}
通过以上注释可知:
FragmentActivity的onActivityResult方法只查找了通过getSupportFragmentManager()关联的Fragment,而通过getFragmentManager()与Fragment关联的子Fragment则直接被忽略了,从而导致在子Fragment无法收到回调。
这个问题在23.2.0以下的support-v4包中都存在,23.2.0版本才修复了这个Bug,改用以上版本则可避免。或着在自定义的Activity基类中重写此方法,自己实现对子Fragment的onActivityResult回调。建议还是使用高版本的v4包,低版本的Fragment中问题实在是太多了。
三 Fragment内部数据保存和恢复
在onSaveInstanceState()保存Fragment数据,自定义的业务类尽量实现Parcelable接口(建议不使用Serializable)。代码如下:
@Overridepublic void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); //保存数据 outState.putString(key, value);}
在Fragment被调用onSaveInstanceState方法后,如果有操作DialogFragment,需使用commitAllowingStateLoss()提交,使用commit()则会抛出异常。
在onViewStateRestored()恢复数据并更新UI。
@Overridepublic void onViewStateRestored(@Nullable Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); if(savedInstanceState != null){ //恢复数据 String value = savedInstanceState.getString(key); ... }}
四 ViewPager中Fragment延时加载数据
自定义一个Fragment基类:
public abstract class BaseFragment extends Fragment { /** * 是否已加载数据 */ protected boolean hasLoadData = false; @Override public void onResume() { super.onResume(); if (getUserVisibleHint() && !hasLoadData) { //加载数据 loadData(true); hasLoadData = true; } } @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); if(getUserVisibleHint() && !hasLoadData){ //加载数据 loadData(true); hasLoadData = true; } } /** * 加载数据 * @param isRefresh true为刷新,false为加载更多 */ public abstract void loadData(boolean isRefresh);}
子中实现数据加载,在onSaveInstanceState方法中保存已经加载的数据,onViewStateRestored方法中恢复数据并将数据更新到UI。这样既可以避免加载未显示的Fragment,又可以在Fragment被重新显示时无需加载即可快速恢复数据并显示。
五 Fragment获取Activity
Fragment中获取FragmentActivity的getActivity方法代码:
final public FragmentActivity getActivity() { return mHost == null ? null : (FragmentActivity) mHost.getActivity();}
Fragment是通过属性mHost(FragmentHostCallbackde实例)关联FragmentActivity,该属性没有限定标识符,即给其赋值的类在同一包名中。既然管理Fragment的是FragmentManager,那么给mHost赋值的操作应该在其方法内。而FragmentManager为抽象类,子类为FragmentManagerImpl,则赋值操在其方法内。通过查看Fragment添加和显示理财,可知具体设置mHost值是在moveToState方法中:
void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { ... switch (f.mState) { case Fragment.INITIALIZING: if (f.mSavedFragmentState != null) { ... //恢复保存的状态信息 } //设置mHost f.mHost = mHost; // f.mParentFragment = mParent; // f.mFragmentManager = mParent != null ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl(); f.mCalled = false; //执行Fragment的onAttach方法 f.onAttach(mHost.getContext()); ... case Fragment.CREATED: if (newState < Fragment.CREATED) { if (f.mAnimatingAway != null) { ... } else { if (!f.mRetaining) { f.performDestroy(); } f.mCalled = false; //执行Fragment的onDetach f.onDetach(); ... //如果不需要保活此Fragment if (!keepActive) { if (!f.mRetaining) { makeInactive(f); } else { //将Fragment中的mHost置为null f.mHost = null; f.mParentFragment = null; f.mFragmentManager = null; f.mChildFragmentManager = null; } } } }
在onAttach之后才可以调用getActivity获取到FragmentActivity对象,在onDetach之后一般情况下是获取不到值。
请尊重博主劳动成果,转载请标明原文链接。
- Activity中使用Fragment笔记
- Activity中使用Fragment
- activity中使用Fragment和Fragment中使用Fragment
- Android Activity中使用Fragment
- activity与fragment中使用OnActivityResult方法
- Android Fragment及Activity 中使用sharedpreferences
- Activity中多Fragment使用方案1
- 在Activity,Fragment,嵌套Fragment中使用ToolBar
- Fragment中加载Activity
- Fragment 中调用 activity
- 使用Fragment代替Activity
- Android Activity中嵌套多个Fragment的使用
- Android Activity中嵌套多个Fragment的使用
- 在Activity中使用Fragment需要先集成FragmentActivity
- activity中动态嵌套使用fragment的方法
- ButterKnife在activity和Fragment中使用的区别
- Activity和Fragment小笔记
- 【学习笔记】 Activity/Fragment 生命周期
- ViewPager切换防止Fragment销毁以及取消Fragment的预加载
- 批量导入不受信任的证书及软件限制策略的应用
- HDOJ 5475 An easy problem(线段树)
- 香蕉派上安装FTP服务器
- Linux磁盘系统
- Activity中使用Fragment笔记
- Error:(13, 8) 错误: xxx不是抽象的, 并且未覆盖xxx中的抽象方法onBindViewHolder(BaseSimpleRecyclerAdapter.ViewHolder,int)
- 不一样的四月
- [LeetCode]506. Relative Ranks(相对应排名)
- DNS域名解析
- Vim插件之ctrlp
- QT:面向对象的Qt编程
- 方立勋_30天掌握JavaWeb_JavaBean、mvc开发模式、el表达式、jstl标签
- Android UI-实现底部切换标签之方式三 ──Activity(底部采用FragmentTabHost)添加5个子Fragment