这篇文章来总结下 Fragment。全文包括 Fragment 介绍,Fragment 回退栈、V4 包差异 和 Fragment 的优化四块内容。
Fragment 介绍
1. 简介
Fragment 是在 Android 3.0 版本中添加的,主要是为了解决 Android 设备尺寸多样化后界面的显示问题。Fragment 比 Activity 较轻量级,可以提供与用户交互的界面并且有自己的生命周期,不用在 Manifest.xml 中注册但它必须嵌套在 Activity 中使用。之前需要使用多个 Activity 显示的内容,现在可以用一个 Activity 嵌套多个 Fragment 来实现。
2. Activity 创建 Fragment 的方式
- 静态创建具体步骤
首先我们同样需要注册一个 xml 文件,然后创建与之对应的 java 文件,通过 onCreatView() 的返回方法进行关联,最后我们需要在 Activity 中进行配置相关参数即在 Activity 的 xml 文件中放上 fragment 的位置。
- 动态创建具体步骤
(1) 创建待添加的碎片实例。
(2) 获取 FragmentManager,在活动中可以直接通过调用 getSupportFragmentManager() 方法得到。
(3) 开启一个事务,通过调用 beginTransaction() 方法开启。
(4) 向容器内添加或替换碎片,一般使用 repalce() 方法实现,需要传入容器的 id 和待添加的碎片实例。
(5) 提交事务,调用 commit() 方法来完成。
3. FragmentPageAdapter 和 FragmentPageStateAdapter 的区别
- FragmnetPageAdapter 在每次切换页面时,只是将 Fragment 进行分离,适合页面较少的 Fragment 使用以保存一些内存,对系统内存不会多大影响。
- FragmentPageStateAdapter 在每次切换页面的时候,是将 Fragment 进行回收,适合页面较多的 Fragment 使用,这样就不会消耗更多的内存。
4. Fragment 的生命周期
首先既然 Fragment 必须依附于 Activity 才能存在,我们就来具体分析一下:
- 运行状态:当一个碎片是可见的,并且它所关联的活动正处于运行状态时,该碎片也处于运行状态。
- 暂停状态:当一个活动进入暂停状态时 (由于另一个未占满屏幕的活动被添加到栈顶),与它相关联的可见碎片就会进入到暂停状态。
- 停止状态:当一个活动进入到停止状态时,与它相关联的碎片就会进入到停止状态,或者通过调用 FragmentTransaction 的removed()、replace() 方法将碎片从活动中移除,但如果在事务提交之前调用 addToBackStack() 方法,这时碎片就会进入到停止状态。总的来说,进入停止状态的碎片对用户来说是完全不可见的,有可能会被系统回收。
- 销毁状态:碎片总是依附于活动而存在的,因此当活动被销毁时,与它相关联的碎片就会进入到销毁状态。或者通过调用FragmentTransaction 的remove()、replace() 方法将碎片从活动中移除,但在事务提交之前并没有调用 addToBackStack() 方法,这时的碎片也会进入到销毁状态。
onAttach(): 当碎片和活动建立关联的时候调用。
onCreateView(): 为碎片创建视图 (加载布局) 时调用。
onActivityCreated(): 确保与碎片相关联的活动一定已经创建完毕的时候调用。
onDestroyView(): 当于碎片相关联的视图移除时调用。
onDetach(): 当碎片和活动解除关联的时候调用。
5. Fragment 家族常用的 API
Fragment常用的三个类:
- android.app.Fragment 主要用于定义 Fragment。
- android.app.FragmentManager 主要用于在 Activity 中操作 Fragment。
- android.app.FragmentTransaction 保证一些列 Fragment 操作的原子性。
获取 FragmentManage 的方式:
getFragmentManager() // v4中,getSupportFragmentManager
主要的操作都是 FragmentTransaction 的方法:
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
transaction.add():往 Activity 中添加一个 Fragment。
transaction.remove():从 Activity 中移除一个 Fragment,如果被移除的 Fragment 没有添加到回退栈,这个 Fragment 实例将会被销毁。
transaction.replace():使用另一个 Fragment 替换当前的,实际上就是 remove() 然后 add() 的合体。
transaction.hide():隐藏当前的 Fragment,仅仅是设为不可见,并不会销毁。
transaction.show():显示之前隐藏的 Fragment。
add() 和 replace() 的区别:
add() 方式实现 fragment 的效果就是:覆盖原 fragment,添加入一个新 fragment 后,原来的 fragment 仍然存活。
replace() 方式是先 remove 掉相同 id 的所有 fragment,然后在 add() 当前的这个 fragment (先 remove() 再 add())。
6. Fragment 与 Activity 通信方式
- 1. 直接在一个 Fragment 中调用另外一个 Fragment 中的方法
我们可以直接在一个 Fragment 中调用另外一个 Fragment 的公开方法,前提是要先拿到另外一个 Fragment 的实例,我们先来看看怎样在左边的 Fragment 中拿到右边 Fragment 的实例:
ContentFragment cf = (ContentFragment) getActivity()
.getFragmentManager().findFragmentById(
R.id.content_fg);
cf.showPro(name);
我们通过宿主 Activity 拿到 FragmentManager,进而再拿到右边的 Fragment,然后调用右边 Fragment 里边的 showPro 方法,其中要传入的参数是左边点击的人名,我们看看右边 Fragment 中的 showPro() 方法:
public void showPro(String key) {
list = map.get(key);
adapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, list);
lv.setAdapter(adapter);
}
使用这种方式我们可以直接在一个 Fragment 中调用另一个 Fragment 的公开方法,从而实现两个 Fragment 的通信。 这种方法适于那些我们在布局文件中就已经定义了的 Fragment,这种 Fragment 每个都有 id,可以通过 FragmentManager 找到,但是如果我们使用了 ViewPager,即每个 Fragment 都是动态添加进来的,这个时候我们无法通过 FragmentManager 获得另外一个Fragment 的实例,那么该怎么办呢?这时我们就要用到下面这种方式了。
- 2. 使用接口
接口可以实现两个 Fragment 之间的通信,也可以实现 Fragment 和 Activity 之间的通信,这大概是用的比较多的一种方式,也是个人比较推荐的一种方式,使用接口来实现两个 Fragment 之间通信,要通过宿主 Activity 中转一下,如果是 Fragment 和宿主Activity 通信则直接调用即可,首先在左边的 Fragment 中定义一个接口:
public interface showPro {
public void showProByName(String name);
}
然后定义一个接口变量:
private showPro mCallback;
我们要在宿主 Activity 中实现这个接口,这样当 Fragment 调用 onAttach() 方法时我们就可以实例化这个接口了:
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity != null) {
mCallback = (showPro) activity;
}
}
当 mCallback 实例化之后,那么我们在点击的时候就可以调用这里边的 showProByName() 方法了:
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
TextView nameTV = (TextView) view;
String name = nameTV.getText().toString();
if ("xx".equals(name)) {
mCallback.showProByName(name);
}
}
});
当然,这个方法的具体实现在宿主 Activity 中,当宿主 Activity 实现了 showPro() 接口之后,接着就要实现它里边的方法了:
public class MainActivity extends Activity implements showPro {
private ContentFragment cf;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getActionBar().hide();
cf = (ContentFragment) getFragmentManager().findFragmentById(
R.id.content_fg);
}
@Override
public void showProByName(String name) {
cf.showPro(name);
}
}
我们在接口的方法中调用右边 Fragment 中的 showPro() 方法,并将当前人名作为参数传入,这个方法与1中相同,我就不贴代码了。这个方法与1中介绍的方法相比,虽然有点麻烦,但是可以有效的解决在一个 Fragment 中拿不到另一个 Fragment 实例的问题,具体应用场景就是 ViewPager 中的 Fragment 之间通信。
- 3. 使用广播或 EventBus
不论我们有没有用 ViewPager,都可以用广播、EventBus 实现两个 Fragment 之间的通信。
- 4. Fragment 直接调用 Activity 中的 public 方法
((MainActivity) getActivity()).showProByName(name);
- 5. 传递参数
可以使用 bundle 进行参数传递、这样在两个 Fragment 跳转的时候就可以带上参数了、同样也可以传递一个复杂的对象。
ft.hide(getActivity().getSupportFragmentManager().findFragmentByTag(""));
DemoFragment demoFragment = new DemoFragment();
Bundle bundle = new Bundle();
bundle.putString("key", "这是方法二");
demoFragment.setArguments(bundle);
ft.add(R.id.fragmentRoot, demoFragment, SEARCHPROJECT);
ft.commit();
Fragment 回退栈
replace() 方法切换 Fragment,会造成 Fragment 不断销毁、创建,但是有没有办法实现像 Activity 一样通过栈的方式来管理Fragment 呢?答案是可以的。Activity 切换时,相信大家都知道是通过栈的形式,不断压栈出栈,在 Fragment 的时候,如果你不是手动开启回退栈,它是直接销毁再重建,但如果将 Fragment 任务添加到回退栈,情况就会不一样了,它就有了类似 Activity的栈管理方式。
写个小 demo 来看看。
1. 准备3个 Fragment
- Fragment1 有一个按钮,可以跳转到 Fragment2。
- Fragment2 有二个按钮,一个跳转到 Fragment3,一个回退到 Fragment1。
- Fragment3 有一个按钮,可以回退到 Fragment2。
public class Fragment1 extends Fragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.i(FragmentsActivity.TAG, "Fragment1 - onAttach()");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(FragmentsActivity.TAG, "Fragment1 - onCreate()");
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.i(FragmentsActivity.TAG, "Fragment1 - onCreateView()");
return inflater.inflate(R.layout.fragment1, container, false);
}
@Override
public void onStart() {
super.onStart();
Log.i(FragmentsActivity.TAG, "Fragment1 - onStart()");
}
@Override
public void onResume() {
super.onResume();
Log.i(FragmentsActivity.TAG, "Fragment1 - onResume()");
}
@Override
public void onPause() {
super.onPause();
Log.i(FragmentsActivity.TAG, "Fragment1 - onPause()");
}
@Override
public void onStop() {
super.onStop();
Log.i(FragmentsActivity.TAG, "Fragment1 - onStop()");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.i(FragmentsActivity.TAG, "Fragment1 - onDestroyView()");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(FragmentsActivity.TAG, "Fragment1 - onDestroy()");
}
@Override
public void onDetach() {
super.onDetach();
Log.i(FragmentsActivity.TAG, "Fragment1 - onDetach()");
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<Button
android:id="@+id/goto_2_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="goto 2"/>
</RelativeLayout>
其他2个 Fragment 和 Fragment1 类似。Activity 布局如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
2. Activity 中,初始化添加 Fragment1,但不添加回退栈
public class FragmentsActivity extends AppCompatActivity {
public static final String TAG = "TestFragment";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragments);
Fragment1 f1 = new Fragment1();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.fragment_container, f1);
ft.commit();
}
}
3. Fragment1 中按钮事件,将当前的事务添加到了回退栈
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.i(FragmentsActivity.TAG, "Fragment1 - onCreateView()");
View view = inflater.inflate(R.layout.fragment1, container, false);
view.findViewById(R.id.goto_2_btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Fragment2 f2 = new Fragment2();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.fragment_container, f2);
// 将当前的事务添加到了回退栈
tx.addToBackStack(null);
tx.commit();
}
});
return view;
}
4. Fragment2 中按钮事件
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.i(FragmentsActivity.TAG, "Fragment2 - onCreateView()");
View view = inflater.inflate(R.layout.fragment2, container, false);
view.findViewById(R.id.back_to_1_btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
// 回退到Fragment1
FragmentManager fm = getFragmentManager();
// 将当前的事务退出回退栈
fm.popBackStack();
}
});
view.findViewById(R.id.goto_3_btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
// 跳转到Fragment3
Fragment3 f3 = new Fragment3();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.fragment_container, f3);
tx.addToBackStack(null);
tx.commit();
}
});
return view;
}
5. Fragment3 中按钮事件
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.i(FragmentsActivity.TAG, "Fragment3 - onCreateView()");
View view = inflater.inflate(R.layout.fragment3, container, false);
view.findViewById(R.id.back_to_2_btn).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
// 回退到Fragment2
FragmentManager fm = getFragmentManager();
fm.popBackStack();
}
});
return view;
}
6. 测试
- (1) 初始化
04-11 18:09:10.224 26731 26731 I TestFragment: Fragment1 - onAttach()
04-11 18:09:10.224 26731 26731 I TestFragment: Fragment1 - onCreate()
04-11 18:09:10.224 26731 26731 I TestFragment: Fragment1 - onCreateView()
04-11 18:09:10.232 26731 26731 I TestFragment: Fragment1 - onStart()
04-11 18:09:10.235 26731 26731 I TestFragment: Fragment1 - onResume()
此时是正常的,到 onResume(),Fragment1 启动并显示。
- (2) Fragment1 —> Fragment2
04-11 18:11:00.056 26731 26731 I TestFragment: Fragment2 - onAttach()
04-11 18:11:00.056 26731 26731 I TestFragment: Fragment2 - onCreate()
04-11 18:11:00.056 26731 26731 I TestFragment: Fragment2 - onCreateView()
04-11 18:11:00.106 26731 26731 I TestFragment: Fragment2 - onStart()
04-11 18:11:00.107 26731 26731 I TestFragment: Fragment2 - onResume()
04-11 18:11:00.107 26731 26731 I TestFragment: Fragment1 - onPause()
04-11 18:11:00.107 26731 26731 I TestFragment: Fragment1 - onStop()
04-11 18:11:00.107 26731 26731 I TestFragment: Fragment1 - onDestroyView()
此时注意 Fragment1 的生命周期,执行了 onDestroyView() 但未执行 onDestroy(),因为它只是界面消失了,并没有销毁。
- (3) Fragment2 —> Fragment3
04-11 18:12:09.304 26731 26731 I TestFragment: Fragment3 - onAttach()
04-11 18:12:09.304 26731 26731 I TestFragment: Fragment3 - onCreate()
04-11 18:12:09.305 26731 26731 I TestFragment: Fragment3 - onCreateView()
04-11 18:12:09.325 26731 26731 I TestFragment: Fragment3 - onStart()
04-11 18:12:09.326 26731 26731 I TestFragment: Fragment3 - onResume()
04-11 18:12:09.326 26731 26731 I TestFragment: Fragment2 - onPause()
04-11 18:12:09.326 26731 26731 I TestFragment: Fragment2 - onStop()
04-11 18:12:09.327 26731 26731 I TestFragment: Fragment2 - onDestroyView()
同上,注意 Fragment2 的生命周期。
- (4) Fragment3 —> Fragment2
04-11 18:13:11.071 26731 26731 I TestFragment: Fragment2 - onCreateView()
04-11 18:13:11.104 26731 26731 I TestFragment: Fragment2 - onStart()
04-11 18:13:11.105 26731 26731 I TestFragment: Fragment2 - onResume()
04-11 18:13:11.105 26731 26731 I TestFragment: Fragment3 - onPause()
04-11 18:13:11.106 26731 26731 I TestFragment: Fragment3 - onStop()
04-11 18:13:11.106 26731 26731 I TestFragment: Fragment3 - onDestroyView()
04-11 18:13:11.111 26731 26731 I TestFragment: Fragment3 - onDestroy()
04-11 18:13:11.111 26731 26731 I TestFragment: Fragment3 - onDetach()
此时注意 Fragment2 的生命周期,执行了 onCreateView() 但未执行 onCreate(),因为它只是将界面显示出来了,并没有创建新的实例;而此时 Fragment3 退栈,真正销毁了。
- (5) Fragment2 —> Fragment1
04-11 18:16:44.583 27481 27481 I TestFragment: Fragment1 - onCreateView()
04-11 18:16:44.600 27481 27481 I TestFragment: Fragment1 - onStart()
04-11 18:16:44.601 27481 27481 I TestFragment: Fragment1 - onResume()
04-11 18:16:44.602 27481 27481 I TestFragment: Fragment2 - onPause()
04-11 18:16:44.602 27481 27481 I TestFragment: Fragment2 - onStop()
04-11 18:16:44.602 27481 27481 I TestFragment: Fragment2 - onDestroyView()
04-11 18:16:44.609 27481 27481 I TestFragment: Fragment2 - onDestroy()
04-11 18:16:44.610 27481 27481 I TestFragment: Fragment2 - onDetach()
同上,注意 Fragment1 的生命周期;此时 Fragment2 退栈,真正销毁了。
- (6) 再返回,退出 Activity
04-11 18:18:05.948 27481 27481 I TestFragment: Fragment1 - onPause()
04-11 18:18:06.313 27481 27481 I TestFragment: Fragment1 - onStop()
04-11 18:18:06.314 27481 27481 I TestFragment: Fragment1 - onDestroyView()
04-11 18:18:06.316 27481 27481 I TestFragment: Fragment1 - onDestroy()
04-11 18:18:06.316 27481 27481 I TestFragment: Fragment1 - onDetach()
- (7) 如果不使用回退栈会怎么样呢?
// 初始化
04-11 18:21:26.087 27869 27869 I TestFragment: Fragment1 - onAttach()
04-11 18:21:26.087 27869 27869 I TestFragment: Fragment1 - onCreate()
04-11 18:21:26.087 27869 27869 I TestFragment: Fragment1 - onCreateView()
04-11 18:21:26.096 27869 27869 I TestFragment: Fragment1 - onActivityCreated()
04-11 18:21:26.098 27869 27869 I TestFragment: Fragment1 - onStart()
04-11 18:21:26.101 27869 27869 I TestFragment: Fragment1 - onResume()
// Fragment1 -> Fragment2,Fragment1 onDetach() 了
04-11 18:21:28.004 27869 27869 I TestFragment: Fragment2 - onAttach()
04-11 18:21:28.005 27869 27869 I TestFragment: Fragment2 - onCreate()
04-11 18:21:28.005 27869 27869 I TestFragment: Fragment2 - onCreateView()
04-11 18:21:28.040 27869 27869 I TestFragment: Fragment2 - onStart()
04-11 18:21:28.041 27869 27869 I TestFragment: Fragment2 - onResume()
04-11 18:21:28.041 27869 27869 I TestFragment: Fragment1 - onPause()
04-11 18:21:28.041 27869 27869 I TestFragment: Fragment1 - onStop()
04-11 18:21:28.042 27869 27869 I TestFragment: Fragment1 - onDestroyView()
04-11 18:21:28.050 27869 27869 I TestFragment: Fragment1 - onDestroy()
04-11 18:21:28.050 27869 27869 I TestFragment: Fragment1 - onDetach()
// Fragment2 -> Fragment3,Fragment2 onDetach() 了
04-11 18:21:30.509 27869 27869 I TestFragment: Fragment3 - onAttach()
04-11 18:21:30.510 27869 27869 I TestFragment: Fragment3 - onCreate()
04-11 18:21:30.510 27869 27869 I TestFragment: Fragment3 - onCreateView()
04-11 18:21:30.536 27869 27869 I TestFragment: Fragment3 - onStart()
04-11 18:21:30.537 27869 27869 I TestFragment: Fragment3 - onResume()
04-11 18:21:30.537 27869 27869 I TestFragment: Fragment2 - onPause()
04-11 18:21:30.537 27869 27869 I TestFragment: Fragment2 - onStop()
04-11 18:21:30.538 27869 27869 I TestFragment: Fragment2 - onDestroyView()
04-11 18:21:30.548 27869 27869 I TestFragment: Fragment2 - onDestroy()
04-11 18:21:30.548 27869 27869 I TestFragment: Fragment2 - onDetach()
// Fragment3 -> Fragment2,Fragment3 onDetach() 了, Fragment2 重新 onCreate()
04-11 18:21:32.385 27869 27869 I TestFragment: Fragment2 - onAttach()
04-11 18:21:32.386 27869 27869 I TestFragment: Fragment2 - onCreate()
04-11 18:21:32.386 27869 27869 I TestFragment: Fragment2 - onCreateView()
04-11 18:21:32.417 27869 27869 I TestFragment: Fragment2 - onStart()
04-11 18:21:32.418 27869 27869 I TestFragment: Fragment2 - onResume()
04-11 18:21:32.419 27869 27869 I TestFragment: Fragment3 - onPause()
04-11 18:21:32.419 27869 27869 I TestFragment: Fragment3 - onStop()
04-11 18:21:32.419 27869 27869 I TestFragment: Fragment3 - onDestroyView()
04-11 18:21:32.426 27869 27869 I TestFragment: Fragment3 - onDestroy()
04-11 18:21:32.426 27869 27869 I TestFragment: Fragment3 - onDetach()
// Fragment2 -> Fragment1,Fragment2 onDetach() 了, Fragment1 重新 onCreate()
04-11 18:21:34.292 27869 27869 I TestFragment: Fragment1 - onAttach()
04-11 18:21:34.292 27869 27869 I TestFragment: Fragment1 - onCreate()
04-11 18:21:34.292 27869 27869 I TestFragment: Fragment1 - onCreateView()
04-11 18:21:34.308 27869 27869 I TestFragment: Fragment1 - onActivityCreated()
04-11 18:21:34.308 27869 27869 I TestFragment: Fragment1 - onStart()
04-11 18:21:34.309 27869 27869 I TestFragment: Fragment1 - onResume()
04-11 18:21:34.309 27869 27869 I TestFragment: Fragment2 - onPause()
04-11 18:21:34.310 27869 27869 I TestFragment: Fragment2 - onStop()
04-11 18:21:34.310 27869 27869 I TestFragment: Fragment2 - onDestroyView()
04-11 18:21:34.318 27869 27869 I TestFragment: Fragment2 - onDestroy()
04-11 18:21:34.318 27869 27869 I TestFragment: Fragment2 - onDetach()
// 退出 Activity
04-11 18:21:35.994 27869 27869 I TestFragment: Fragment1 - onStop()
04-11 18:21:35.994 27869 27869 I TestFragment: Fragment1 - onDestroyView()
04-11 18:21:36.003 27869 27869 I TestFragment: Fragment1 - onDestroy()
04-11 18:21:36.003 27869 27869 I TestFragment: Fragment1 - onDetach()
- (8) 获取回退栈数
getFragmentManager().getBackStackEntryCount()
可以得到回退栈中的当前总的个数,每添加一次回退栈该数会加1。
app 包下的 Fragment 和 v4 包下的 Fragment 的区别
- 1. 最低支持版本不同
android.app.Fragment 兼容的最低版本是 android:minSdkVersion="11" 即 3.0 版。
android.support.v4.app.Fragment 兼容的最低版本是 android:minSdkVersion="4" 即 1.6 版。
- 2. 需要导 jar 包或依赖 library
fragment android.support.v4.app.Fragment 需要引入包 android-support-v4.jar 或者 gradle 依赖。
- 3. 在 Activity 中取的方法不同
android.app.Fragment 使用 (ListFragment) getFragmentManager().findFragmentById(R.id.userList) 获得 ,继承 Activity
android.support.v4.app.Fragment 使用 (ListFragment) getSupportFragmentManager().findFragmentById(R.id.userList) 获得 ,需要继承 android.support.v4.app.FragmentActivity。
- 4. 关于这两个 Fragment 使用 <fragment> 标签的问题
app.fragment 和 v4.fragment 都是可以使用 <fragment> 标签。只是在在使用的时候如果是 app.fragment 则没有什么特殊的地方继承 Activity 即可。当 v4.fragment 使用 <fragment> 标签的时候就要特别注意了:当这个 Activity 的布局中有 <fragment> 标签的时候,这个 Activity 必须继承 FragmentActivity,否则就会报错。
Fragment 使用优化
- 1. 采用 FragmentStatePagerAdapter
FragmentStatePagerAdapter 和 FragmentPagerAdapter的主要区别是:FragmentStatePagerAdapter 会及时回收 fragment 而FragmentPagerAdapter 会把 fragment 一直放在内存当中。而且写的时候一般会用引用包裹:
private static final class TabPagerAdapter extends FragmentStatePagerAdapter {
private final WeakReference<Fragment>[] data;
@SuppressWarnings("unchecked")
TabPagerAdapter(FragmentManager fm) {
super(fm);
data = (WeakReference<Fragment>[]) Array.newInstance(WeakReference.class, getCount());
}
@Override
public Fragment getItem(int position) {
if (position < 0 || position >= getCount()) {
return new Fragment();
}
Fragment f = null;
if (!Requires.isNull(data[position])) {
f = data[position].get();
} else {
if (position == 0) {
f = HomeFragment.newInstance();
} else if (position == 1) {
f = DappFragment.newInstance();
} else if (position == 2) {
f = SettingsFragment.newInstance();
}
data[position] = new WeakReference<>(f);
}
return f;
}
@Override
public int getCount() {
return 3;
}
}
- 2. setOffscreenPageLimit()
这个设置是防止 viewpager 缓存过多的 fragment,但是不能设置成 0,因为设置 0 默认最小值 1。
- 3. 懒加载数据
主要配合 setUserVisibleHint() 使用。
- 4. Fragment 跳转使用回退栈
可见上文。
- 5. 预加载和动态加载
刚刚接触 Android 的同学或许会这么写:
FragmentManager fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
fragmentTransaction.add(ViewId,fragment);// 或者fragmentTransaction.replace(ViewId,fragment);
fragmentTransaction.commit();
基础更好一点的同学会用 show() 和 hide() 方法:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(new FirstFragment())
.show(new SecondFragment())
.commit();
这两种都可以切换 Fragment,但是面对用户大量点击来回切换,或者你的 Fragment 本来就很多,每次都这样操作,那么很快你的应用就会 OOM,就算不崩那也会异常的卡顿。因为每次在 add/replace 或者 show/hide 都会 new 一个新的实例。
(1) 预加载模式
//首先需要先实例好三个全局Fragment
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment, FirstFragment.getInstance());
ft.add(R.id.fragment, SecondFragment.getInstance());
ft.add(R.id.fragment, ThirdFragment.getInstance());
ft.hide(SecondFragment.getInstance());
ft.hide(ThirdFragment.getInstance());
ft.commit();
在加载第一个 Fragment 时就把全部 Fragment 加载好,下次使用直接调用如:
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.hide(FirstFragment.getInstance())
.show(SecondFragment.getInstance())
.commit();
但这还不是最好的方案。
(2) 动态加载
private Fragment currentFragment = new Fragment();
private FragmentTransaction switchFragment(Fragment targetFragment) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 第一次使用 switchFragment() 时 currentFragment为null,所以要判断一下
if (!targetFragment.isAdded()) {
if (currentFragment != null) {
transaction.hide(currentFragment);
}
transaction.add(R.id.fragment, targetFragment, targetFragment.getClass().getName());
} else {
transaction.hide(currentFragment).show(targetFragment);
}
currentFragment = targetFragment;
return transaction;
}
现在你的 Fragment 无论怎么切都不会出现卡顿了,因为你的所有 Fragment 只会被实例化一次!实例一次的 Fragment 会被存入内存中,下次切换会判断内存中是否含有要切换的 Fragment,如果有就直接复用,没有就 add() 一个新的。