Android应用篇 - Fragment 的介绍、回退栈和优化

这篇文章来总结下 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常用的三个类: 

  1. android.app.Fragment 主要用于定义 Fragment。
  2. android.app.FragmentManager 主要用于在 Activity 中操作 Fragment。
  3. 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() 一个新的。

猜你喜欢

转载自blog.csdn.net/u014294681/article/details/89214940
今日推荐