对 Fragment 的一些思考 (一)

2,328 阅读6分钟
原文链接: fvaryu.github.io

自从Google在Andorid 3.0推出Fragment以来,它变迅速占领Android“开发市场”,作为一个Android开发者,如果你说没用使用过Fragment那就有些说不过去了。今天我们就来聊聊这个神奇的“组件”。

1、概论

记得有次我去面试,面试官问我:对于平日使用的Fragment你如何理解的?这一问一下子蒙圈了,虽然平日经常使用但是从来没有想过这么深的问题。这里也顺便说一句,作为开发者去了解深层次的东西还是很有必要的,正所谓知其然,知其所以然。下面有张图是我在一家网站关于这个问题的提问,大家回复的见解。

对于Fragment官网的解释是:

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a “sub activity” that you can reuse in different activities).

翻译过来大概是(翻译仅供参考):

一个片段是一个行为或在一个活动用户界面的一部分。可以结合多个碎片在一个单一的活动建立一个多窗格UI和重用一个片段在多个活动。你能想到的一个片段是一个模块化的一个活动,它有自己的生命周期,收到自己的输入事件,您可以添加或删除活动运行时(就像一个“子活动”,您可以重用在不同的活动)。

其中已经很好的解释了Fragment,对他的定义也很充分。不用多说。

2、生命周期

Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。下面这幅图(来自于官网)说明了Fragment自己的生命周期问题。

这张图(来自于官网)则说明了,Fragment与Activity之间的联系。

其中:

onAttach(Activity): 当Fragment与Activity发生关联时调用

onCerate(Bundle):系统会在创建的时候调用

onCreateView(LayoutInflater, ViewGroup,Bundle):创建该Fragment的视图

onActivityCreated(Bundle):当Activity的onCreate方法返回时调用

onDestoryView():与onCreateView想对应,当该Fragment的视图被移除时调用

onDetach():与onAttach相对应,当Fragment与Activity关联被取消时调用

平日开发中不必全部重写,重点是Fragment.onCreateView()方法。

3、使用方式

Fragment的使用有两种方式:

3.1、动态添加

首先我们需要继承自Fragment定义自己的Fragment(ExampleFragment)并重写onCreateView()方法,

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // example_fragment是要显示的xml布局文件(略)
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

然后在Activity的xml布局中定义一个ViewGroup(比如此处使用FrameLayout)。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
</LinearLayout>

最后在Activity中添加,

public class FragmentActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fragment);
    ExampleFragment fragment = new ExampleFragment();
    getFragmentManager().beginTransaction()
    .add(R.id.fragment_container, fragment)
    .commit();
}

这样就可以让Fragment和Activity关联起来并显示出来。

3.2、静态添加

上面我们已经使用动态的方式添加好了一个Fragment,并显示出来了,现在我们使用第二种方式,静态添加。
同样我们需要先创建好一个自己的Fragment(ExampleFragment),以及Activity,不同之处时候我们需要在Activity对应的xml中像定义View那样定义Fragment。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment android:name="com.example.news.ExampleFragment"
            android:id="@+id/fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    <fragment>
</LinearLayout>

其他地方不需要修改即可,运行即可查看效果。

3.3、不同

两种方式都可以添加,那么他们之间有什么不同之处呢?如果你熟悉View的相关知识(XML定义和代码添加),大概就可以清楚,本质上他们并没有什么区别,但是还是有些形势上的不同,对于第二种方式,因为我们是写死在xml中的所以就丧失了灵活性,要想调整成其他的Fragment就只能修改源代码,而第一种方式是动态的,通过代码来操作方便灵活,想用什么通过逻辑判断可以任意选择要显示的Fragment,比较灵活一些,所以通常我们都是用第一种,第二种使用的方式频率比较低。我也推荐使用第一种方式。

4、管理Fragment

前面我们也比较了两种使用的方式,两种都行任君选择,下面我们说说一些关键的API。毕竟使用Fragment就是操作官方的API。

要想管理您的 Activity 中的片段,您需要使用 FragmentManager。要想获取它,请从您的 Activity 调用 getFragmentManager()。

您可以使用 FragmentManager 执行的操作包括:

  • 通过 findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段
  • 通过 popBackStack()(模拟用户发出的 Back 命令)将片段从返回栈中弹出
  • 通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器
  • 通过 add()添加一个Fragment
  • 通过 remove() 从Activity中移除一个Fragment
  • 通过 replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的结合
  • 通过 hide() 隐藏Fragment,实际上是设置依附的ViewGroup隐藏,即View.Gone
  • 通过 show() 显示隐藏的Fragment,实际上是设置依附的ViewGroup显示,即View.VISIBLE
  • 通过 commit() 真正的操作Fragment状态

还有一些其他的API,开发中用的较少所以就不一一介绍了,读者可自行实验。实践是检验真理的唯一标准。

5、与 Activity 通信

尽管 Fragment 是作为独立于 Activity 的对象实现,并且可在多个 Activity 内使用,但片段的给定实例会直接绑定到包含它的 Activity。

5.1、 Activity -> Fragment

public class FragmentActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
        ExampleFragment fragment = new ExampleFragment();
        // 创建参数
         Bundle bundle = new Bundle();
        bundle.putString("argKey", "value");
        fragment.setArguments(bundle);
        getFragmentManager().beginTransaction()
        .add(R.id.fragment_container, fragment)
        .commit();

        // 略...
        fragment.apiFuncation();
    }
}

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 获取
        Bundle bundle = getArguments();
        String arg = bundle.getString("argKey");
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

5.2、 Activity <- Fragment

在Fragment可以通过getActivity()来获取Fragment所依附的Activity实例,Activity也可以通过 findFragmentById() 或 findFragmentByTag()来获取对应的Fragment。

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 获取/操作
        Activity a = getActivity();
        a.apiFuncation();
        return inflater.inflate(R.layout.example_fragment, container, false);
    }

6、总结

到这里Fragment的基本东西已经说完了,也许你会有许多疑问,比如Framgent到底是怎样和Activity发生关系的,怎样管理的等等,不要急下一篇我们就来好好分析分析。

坚持原创技术分享,您的支持将鼓励我继续创作! 赏 lowett WeChat Pay

微信打赏