fragment
http://www.cnblogs.com/smyhvae/p/3978989.html
http://www.jianshu.com/p/ec0397cbca06
http://www.jianshu.com/p/d9143a92ad94
https://segmentfault.com/a/1190000006691830
http://www.jianshu.com/p/662c46cd3b5f
http://blog.csdn.net/shan_yao/article/details/52152804
http://blog.csdn.net/harvic880925/article/details/44917955
http://www.jianshu.com/users/2ce7b74b592b/latest_articles
http://www.bkjia.com/Androidjc/995847.html
http://android.jobbole.com/83606/
http://www.jianshu.com/p/1b824e26105b
http://www.jianshu.com/p/626229ca4dc2
http://www.jianshu.com/p/78ec81b42f92
懒加载
https://github.com/xmagicj/LazyFragment
http://www.jianshu.com/p/92befb8ab8c0
http://blog.csdn.net/feixiangdexin123087/article/details/42059975
http://www.jianshu.com/p/c5d29a0c3f4c/comments/1825997
http://blog.csdn.net/jdsjlzx/article/details/49970017
http://www.wangchenlong.org/2016/08/07/1608/076-bottom-dialog-fragment/
https://github.com/MoshDev/BackgroundViewPager
目录
概述
fragment加载到activity中
fragment的生命周期
fragment的返回栈
fragment的通信
fragment使用中会出现的问题(原文地址:http://www.jianshu.com/p/d9143a92ad94)
0概述
一个新事物的产生总是为了解决旧事物存在的问题,Fragment是android3.0的产物,在android3.0之前解决手机、平板电脑的适配问题是很头疼的,对ActivityGroup有印象的朋友,应该能深深的体会到ActivityGroup包裹的多个Activity之间切换等一系列的性能问题。由此Fragment诞生了。个人总结的Fragment的使命:
解决手机、平板电脑等各种设备的适配问题
解决多个Activity之间切换性能问题
模块化,因为模块化导致复用的好处
1.Fragment加载到Activity中
//步骤一:添加一个FragmentTransaction的实例
FragmentManager fragmentManager =getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
//步骤二:用add()方法加上Fragment的对象rightFragment
RightFragment rightFragment = new RightFragment();
transaction.add(R.id.right, rightFragment);
//步骤三:调用commit()方法使得FragmentTransaction实例的改变生效
transaction.commit();
FragmentTransaction:
FragmentTransaction对象通过getFragmentManager().beginTransaction()获取。它将开启一个事务,用于操作一个ViewGroup中的Fragment。
FragmentTransaction的常用方法:
add():往Activity中添加一个Fragment。
remove():从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
replace():使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体。
hide():隐藏当前的Fragment,仅仅是设为不可见,并不会销毁。
show():显示之前隐藏的Fragment
detach():将此Fragment从Activity中分离,会销毁其布局,但不会销毁该实例。
attach():将从Activity中分离的Fragment,重新关联到该Activity,重新创建其视图层次。
commit():提交一个事务。
2.Fragment的生命周期

进入页面
09-20 14:57:01.788: I/MainActivity(524): --MainActivity->>onCreate
09-20 14:57:01.788: I/MyFragment(524): --MyFragment->>onAttach
09-20 14:57:01.788: I/MyFragment(524): --MyFragment->>onCreate
09-20 14:57:01.788: I/MyFragment(524): --MyFragment->>onCreateView
09-20 14:57:02.018: I/MyFragment(524): --MyFragment->>onActivityCreated
09-20 14:57:02.018: I/MainActivity(524): --MainActivity->>onStart
09-20 14:57:02.018: I/MyFragment(524): --MyFragment->>onStart
09-20 14:57:02.018: I/MainActivity(524): --MainActivity->>onResume
09-20 14:57:02.018: I/MyFragment(524): --MyFragment->>onResume
按back键
09-20 14:58:25.128: I/MyFragment(524): --MyFragment->>onPause
09-20 14:58:25.128: I/MainActivity(524): --MainActivity->>onPause
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onStop
09-20 14:58:25.978: I/MainActivity(524): --MainActivity->>onStop
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onDestroyView
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onDestroy
09-20 14:58:25.978: I/MyFragment(524): --MyFragment->>onDetach
09-20 14:58:25.978: I/MainActivity(524): --MainActivity->>onDestroy
3.backstack
假设现在我们有两个Fragment:FragmentA和FragmentB,我们现在从FragmentA的界面跳到FragmentB,然后按Back键,发现程序是直接退出了,而不是返回到FragmentA。
如果现在想实现以下功能:从FragmentA的界面跳到FragmentB,然后按Back键,会返回到FragmentA。这个功能该怎么实现呢?这其实就利用到了返回栈的知识。
FragmentTransaction中提供了一个addToBackStack()方法,可以将一个事务添加到返回栈中
4.Fragment两种推荐的通信方案
4.1setArguments
调用方式
getFragmentManager().beginTransaction()
.replace(android.R.id.content, ExampleFragment.newInstance(data))
.addToBackStack(null)
.commitAllowingStateLoss();
public class ExampleFragment extends Fragment {
private String type;
public static ExampleFragment newInstance(String type) {
ExampleFragment myFragment = new ExampleFragment();
Bundle args = new Bundle();
args.putString("type", type);
myFragment.setArguments(args);
return myFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
type=getArguments().getString("type");
...相应的处理代码
}
}
4.2接口回调方式
//MainActivity实现MainFragment开放的接口
public class MainActivity extends FragmentActivity implements FragmentListener{
@override
public void func(){ }
...
}
public class MainFragment extends Fragment{
public FragmentListener mListener;
//MainFragment开放的接口
public static interface FragmentListener{
void func();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//对传递进来的Activity进行接口转换
if(activity instance FragmentListener){
mListener = ((FragmentListener)activity);
}
}
...
}
5.Fragment的坑
5.1内存重启:
安卓app有一种特殊情况,就是 app运行在后台的时候,系统资源紧张的时候导致把app的资源全部回收(杀死app的进程),这时把app再从后台返回到前台时,app会重启。这种情况下文简称为:“内存重启”。(屏幕旋转等配置变化也会造成当前Activity重启,本质与“内存重启”类似)
在系统要把app回收之前,系统会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。在“内存重启”后,Activity的恢复是从栈顶逐步恢复,Fragment会在宿主Activity的onCreate
方法调用后紧接着恢复(从onAttach生命周期开始)。
5.2getActivity空指针
大多数情况下的原因:你在调用了getActivity()时,当前的Fragment已经onDetach()了宿主Activity。
比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。
解决方法:
设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是相比空指针闪退,这种做法“安全”些),即:
protected Activity mActivity;
@Overridepublic void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
5.3重叠
如果你add()了几个Fragment,使用show()、hide()方法控制,比如微信、QQ的底部tab等情景,如果你什么都不做的话,在“内存重启”后回到前台,app的这几个Fragment界面会重叠。
原因是FragmentManager帮我们管理Fragment,当发生“内存重启”,他会从栈底向栈顶的顺序一次性恢复Fragment;
但是因为没有保存Fragment的mHidden属性,默认为false,即show状态,所以所有Fragment都是以show的形式恢复,我们看到了界面重叠。
(如果是replace,恢复形式和Activity一致,只有当你pop之后上一个Fragment才开始重新恢复,所有使用replace不会造成重叠现象)
5.3.1第一个解决方案 findFragmentByTag
即在add()或者replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag找到对应的Fragment,并hide()需要隐藏的fragment。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “内存重启”时调用
targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
// 解决重叠问题
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常时
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
getFragmentManager().beginTransaction()
.add(R.id.container, targetFragment, targetFragment.getClass().getName())
.add(R.id,container,hideFragment,hideFragment.getClass().getName())
.hide(hideFragment)
.commit();
}
}
5.3.2第二个解决方案 *****FragmentManager().getFragments()*****
getFragments可以获取到所有的fragments对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TargetFragment targetFragment;
HideFragment hideFragment;
if (savedInstanceState != null) { // “内存重启”时调用
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if(fragment instanceof TartgetFragment){
targetFragment = (TargetFragment)fragment;
}else if(fragment instanceof HideFragment){
hideFragment = (HideFragment)fragment;
}
}
// 解决重叠问题
getFragmentManager().beginTransaction()
.show(targetFragment)
.hide(hideFragment)
.commit();
}else{ // 正常时
targetFragment = TargetFragment.newInstance();
hideFragment = HideFragment.newInstance();
// 这里add时,tag可传可不传
getFragmentManager().beginTransaction()
.add(R.id.container)
.add(R.id,container,hideFragment)
.hide(hideFragment)
.commit();
}
}
5.3.3第三个解决方案:Fragment保存自己的Hidden状态
前两种方案的局限性
在Fragment嵌套的场景下,恢复会有问题,原因在于:页面重启后,在父Fragment没有初始化完成前,getChildFragmentManager()子栈内的子Fragment是空,只有父Fragment初始化完成后,子栈内的子Fragment才能正确获取到。
我们知道了发生Fragment重叠的根本原因在于FragmentState没有保存Fragment的显示状态,即mHidden,导致页面重启后,该值为默认的false,即show状态,所以导致了Fragment的重叠。
public class BaseFragment extends Fragment {
private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);
FragmentTransaction ft = getFragmentManager().beginTransaction();
if (isSupportHidden) {
ft.hide(this);
} else {
ft.show(this);
}
ft.commit();
}
@Override
public void onSaveInstanceState(Bundle outState) {
...
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
}
}
public class MainActivity{
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
// 这里一定要在save为null时才加载Fragment,Fragment中onCreateView等生命周里加载根子Fragment同理
// 因为在页面重启时,Fragment会被保存恢复,而此时再加载Fragment会重复加载,导致重叠
if(saveInstanceState == null){
// 这里加载根Fragment
}
}
}
5.4出栈方法
如果你想让某一个Fragment出栈,使用remove()在加入回退栈时并不靠谱。
如果你在add的同时将Fragment加入回退栈:addToBackStack(name)的情况下,它并不能真正将Fragment从栈内移除,如果你在2秒后(确保Fragment事务已经完成)打印getSupportFragmentManager().getFragments(),会发现该Fragment依然存在,并且依然可以返回到被remove的Fragment,而且是空白页面。
如果你没有将Fragment加入回退栈,remove方法可以正常出栈。
如果你加入了回退栈,popBackStack()系列方法才能真正出栈
case R.id.button1:
fragment01 = new Fragment01();
transaction.replace(R.id.right, fragment01, "fragment01");
transaction.addToBackStack("fragment01");
transaction.commit();
break;
case R.id.button2:
transaction.remove(fragment01);
transaction.commit();
运行效果:
点击button1,fragment1加载进来
点击button2,移出
点击back,空白界面
点击back,退出
case R.id.button1:
fragment01 = new Fragment01();
transaction.replace(R.id.right, fragment01, "fragment01");
transaction.addToBackStack("fragment01");// 添加到Activity管理的回退栈中。
transaction.commit();
break;
case R.id.button2:
manager.popBackStack();
运行效果:
按顺序button1,button2,
点击back,退出
5.5 popBackStack的注意事项
case R.id.button1:
fragment01 = new Fragment01();
transaction.replace(R.id.right, fragment01, "fragment01");
transaction.addToBackStack("fragment01");// 添加到Activity管理的回退栈中。
transaction.commit();
break;
case R.id.button2:
manager.popBackStack();
break;
case R.id.button3:
Fragment03 fragment03 = new Fragment03();
transaction.replace(R.id.right, fragment03, "fragment03");
transaction.addToBackStack("fragment03");// 添加到Activity管理的回退栈中。
transaction.commit();
break;
运行效果:
点击button1,button3
点击两次button2
popBackStack的
public class FragmentTransactionBugFixHack {
public static void reorderIndices(FragmentManager fragmentManager) {
if (!(fragmentManager instanceof FragmentManagerImpl))
return;
FragmentManagerImpl fragmentManagerImpl = (FragmentManagerImpl) fragmentManager;
if (fragmentManagerImpl.mAvailIndices != null
&& fragmentManagerImpl.mAvailIndices.size() > 1) {
Collections.sort(fragmentManagerImpl.mAvailIndices,
Collections.reverseOrder());
}
}
}
在调用popBackStack之后立马执行
hanler.post(new Runnable(){
@Override
public void run() {
FragmentTransactionBugFixHack.reorderIndices(fragmentManager));
}
});
1.对Fragment传递数据,建议使用setArguments(Bundle args),而后在onCreate中使用getArguments()取出,在 “内存重启”前,系统会帮你保存数据,不会造成数据的丢失。和Activity的Intent原理一致。
2.使用newInstance(参数)创建Fragment对象,优点是调用者只需要关系传递的哪些数据,而无需关心传递数据的Key是什么。
3.如果你需要在Fragment中用到宿主Activity对象,建议在你的基类Fragment定义一个Activity的全局变量,在onAttach中初始化,这不是最好的解决办法,但这可以有效避免一些意外Crash。下面会说到此问题的解决方案。
原文链接:http://www.jianshu.com/p/662c46cd3b5f
网友评论