Fragment总结
2017-02-16 18:46
120 查看
零、前言
今年可能要实习了,准备把之前学过的知识点重头开始回顾下,不学习新东西了,这篇讲Fragment。一、生命周期
onAttach(Activity activity)
当Fragment第一次被添加到宿主Activity上时调用。
onCreate(@Nullable Bundle savedInstanceState)
初始化Fragment时调用,调用时期:在onAttach()方法之后,在onCreateView()方法之前。
onCreateView(inflater,container,savedInstanceState)
当已经有了一个fragment实例,一般我们在这里创建Fragment的视图。
在onCreate()之后,onActivityCreated(Bundle)之前调用。
如果在这里返回了一个View,那么当在释放时将会调用onDestroyView().
onActivityCreated(Bundle)
当Fragment的宿主Activity已经被create()了并且这个Fragment的视图也被实例化了的时候调用。在这个函数里,我们可以进行一些状态的恢复,加载数据等操作。当然,这里可以调用
setRetainInstance(boolean)来保存他们的实例。这个函数的调用时期:onCreateView()之前,
onViewStateRestored()之后。
接着几个生命周期方法对应着Activity的生命周期。就不叙述了。
onDestoryView()
上面提过,和onCreateView相对应,当onCreateView方法已经返回一个视图,那么在释放资源时,将调用该方法。
onDetach(Activity activity)
与onAttach相对应,当Fragment从宿主上面移除时调用。
注意:除了onCreateView方法外,其它方法重写的话必须调用父类对该方法的实现,也就是super()。
二、Fragment的使用
1、静态使用
继承Fragment,在onCreateView中初始化视图在Activity的布局中使用fragment 布局,并且添加name属性,属性的值为第一步创建的Fragment类。
就这样两步就实现了。
2、动态使用
获取FragmentManager对象调用FragmentManager的beginTransaction()方法,获取FragmentTransaction 对象
创建一个继承了Fragment对象的类
调用FragmentTransaction 的replace()方法
调用 FragmentTransaction 的commit()
3、Fragment的回退栈
类似与Activity,Fragment也有自己的回退栈,当我们把一个Fragment放入栈中,这个Fragment实例并不会被销毁,但是界面会被销毁,也就会调用onDestoryView()方法,当我们从另一个Fragment回退到放入回退栈中的Fragment时,会调用onCreateView()方法。三、常用API
在上面动态使用中涉及了几个常用的API,还有未介绍的APIFragmentTransaction 类
transaction.add() :往一个Activity中添加一个Fragment
transaction.remove() :如果这个Fragment没有被添加到回退站,则这个Fragment则会被销毁,也就是调用onDestoryView()方法。
transaction.replace():remove()和add()合体。
addToBackStack(null):将当前Fragment添加到回退栈
transaction.hide():隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show():显示之前隐藏的Fragment
transatcion.commit():提交一个事务,注意:一定要在Activity.onSaveInstance()之前调用,否则会报State loss这样的错误。
Fragment类
detach():会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach():重建view视图,附加到UI上并显示。
四、和ViewPager结合使用
Fragment和ViewPager结合使用的情况还是比较多大。将每一个Fragment作为ViewPager的一页。我们不用操作事务,利用adapter帮助我们完成。1、FragmentPagerAdapter
FragmentPagerAdapter 继承自 PagerAdapter。相比通用的 PagerAdapter,该类更专注于每一页均为 Fragment 的情况。这个adapter每生成一个Fragment都会报错在内存中,因此适合页面比较少的情况。使用该类需要重写两个方法getItem()
这个函数在instantiateItem()函数中被调用。看看源码。
@Override public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else { fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; }
首先判断事务是否为null,如果为空,则开启事务,接着调用getItemId(position)方法获取对应位置的itemid,通过itemid获取Fragment绑定的name,再获取Fragment。
然后判断Fragment是否为null,如果不为null,则调用事务的attach()方法,如果为null,则调用getItem()返回一个Fragment。从这里可以看出并不是每次创建都会创建Fragment,也就是说,不是每次都会调用getItem()方法,只有在第一次调用才会调用。这也是为什么有时候调用notifyDataSetChanged()不起作用的原因
getCount()
这个函数比较简单,只是单纯返回数量。
destroyItem() :该函数被调用后,会对 Fragment 进行 FragmentTransaction.detach()。Fragment 所占用的资源不会被释放。
@Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment)object).getView()); mCurTransaction.detach((Fragment)object); }
2、FragmentPagerStateAdapter
FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一样,是继承子 PagerAdapter。但是,和 FragmentPagerAdapter 不一样的是,该 PagerAdapter 的实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需要显示时,生成新的页面(就像 ListView 的实现一样)。这么实现的好处就是当拥有大量的页面时,不必在内存中占用大量的内存。instantiateItem()源码:
@Override public Object instantiateItem(ViewGroup container, int position) { // If we already have this item instantiated, there is nothing // to do. This can happen when we are restoring the entire pager // from its saved state, where the fragment manager has already // taken care of restoring the fragments we previously had instantiated. if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } Fragment fragment = getItem(position); if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); if (mSavedState.size() > position) { Fragment.SavedState fss = mSavedState.get(position); if (fss != null) { fragment.setInitialSavedState(fss); } } while (mFragments.size() <= position) { mFragments.add(null); } fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); mFragments.set(position, fragment); mCurTransaction.add(container.getId(), fragment); return fragment; }
destroyItem() :将 Fragment 移除,即调用 FragmentTransaction.remove(),并释放其资源。
@Override public void destroyItem(ViewGroup container, int position, Object object) { Fragment fragment = (Fragment) object; if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + " v=" + ((Fragment)object).getView()); while (mSavedState.size() <= position) { mSavedState.add(null); } mSavedState.set(position, fragment.isAdded() ? mFragmentManager.saveFragmentInstanceState(fragment) : null); mFragments.set(position, null); mCurTransaction.remove(fragment); }
五、横竖屏切换时的问题
解决这个问题之前我们先了解下当手机横竖屏切换时,Activity的生命周期方法的调用情况。进入Activity
02-16 18:28:50.341 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate 02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器 02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart 02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume
切换为横屏之后
02-16 18:28:50.341 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate 02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器 02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart 02-16 18:28:50.345 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onPause
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStop
02-16 18:29:41.861 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onDestroy
02-16 18:29:41.873 4655-4655/com.lw.fragment I/System.out: ScreenFragment-----无参构造器
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onCreate
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onStart
02-16 18:29:41.885 4655-4655/com.lw.fragment I/System.out: ScreenActivity:onResume
从上面可以知道,当从竖屏切到横屏时,依次调用了onPause、onStop、onDestroy、onCreate、onStart、onResume。这就相当于重新启动了一个Activity。
OK、结合Fragment来看。
这里我贴上我的代码
Activity
public class ScreenActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_screen); System.out.println("ScreenActivity:onCreate"); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction().add(R.id.fl_content, new ScreenFragment("我是参数")).commit(); // getSupportFragmentManager().beginTransaction().add(R.id.fl_content, ScreenFragment.newInstance("我是参数")).commit(); } } @Override protected void onStart() { super.onStart(); System.out.println("ScreenActivity:onStart"); } @Override protected void onResume() { super.onResume(); System.out.println("ScreenActivity:onResume"); } @Override protected void onPause() { super.onPause(); System.out.println("ScreenActivity:onPause"); } @Override protected void onStop() { super.onStop(); System.out.println("ScreenActivity:onStop"); } @Override protected void onDestroy() { super.onDestroy(); System.out.println("ScreenActivity:onDestroy"); } @Override protected void onRestart() { super.onRestart(); System.out.println("ScreenActivity:onRestart"); } }
Fragment
public class ScreenFragment extends Fragment { private String arg = "无参构造器"; public ScreenFragment() { System.out.println("ScreenFragment-----无参构造器"); } public ScreenFragment(String arg) { this.arg = arg; } public static ScreenFragment newInstance(String arg) { Bundle bundle = new Bundle(); bundle.putString("arg", arg); ScreenFragment fragment = new ScreenFragment(); fragment.setArguments(bundle); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_screen, container, false); TextView tv = (TextView) view.findViewById(R.id.tv); tv.setText(arg); // tv.setText(getArguments().getString("arg")); return view; } }
如果安照上面的代码执行,则在从竖屏切换成为横屏时呈现无参构造器。解决方法:将Activity和Fragment两个地方的注释取消,并把注释上面的代码删除即可。
OK,关于Fragment就复习到这里了,以后要是又学到新东西再添加。
这篇博客所用到的代码: 源码下载
相关文章推荐
- Android中Fragment的解析和使用详解
- 一个Activity中多个Fragment实现沉浸式状态栏的解决方法
- Android Activity与Fragment实现底部导航器
- Android用Fragment创建选项卡
- Android中fragment与activity之间的交互(两种实现方式)
- Android Fragment 基本了解(图文介绍)
- Android程序开发之Fragment实现底部导航栏实例代码
- Android开发 Activity和Fragment详解
- Android应用开发中Fragment与Activity间通信示例讲解
- Android的Fragment的生命周期各状态和回调函数使用
- 浅谈Android App开发中Fragment的创建与生命周期
- 实例探究Android开发中Fragment状态的保存与恢复方法
- Android Fragment的使用方法(翻译)
- Android开发技巧之Fragment的懒加载
- 实例探究Android应用编写时Fragment的生命周期问题
- Android App开发中创建Fragment组件的教程
- Android Fragment+FragmentTabHost组件实现常见主页面(仿微信新浪)
- Android Fragment多层嵌套重影问题的解决方法
- 详解Android中fragment和viewpager的那点事儿
- Android中的Fragment类使用进阶