习惯动态加载 fragment

实际项目中要一直用动态方法加载 fragment。 虽然动态加载方法要写的代码会多一点,但是一旦面临需求变更的时候,重构的难度要小不少。

如果使用静态加载方法,fragment 信息必须写入 layout 文件,在设计界面的时候必须知晓相应的 fragment 类 ———— 这样使得 MVC 结构中的 View 和 Controller 黏合得过于紧密,而且也显得似乎是由 View 在“控制” Controller。 静态加载的示例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <fragment
        android:name="com.example.android.tempproject.MyFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

而且,这样就意味着实际开发时,必须先确定 Controller 的类型等信息,再对应设计界面;同时,即使只更换 fragment 类但不修改界面,也必须连带着修改 layout 文件。

尤其是当 activity 之间的界面相差很小时,动态加载的方法更加方便。 比如,想要将简单的文章显式界面换成 Viewpager 实现的可翻页模式。实际的文章界面可以一直不变,甚至新的 activity 的代码只是在获取 fragment 的方式上和原有的不同。 原来的 ArticleActivity 类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ArticleActivity extends AppCompatActivity {

    ...

    public ArticleFragment createFragment() {
        ... // 实际的获取 fragment 类代码
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_article);

        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
        if (fragment == null) {
            fragment = createFragment();
            fragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment)
                    .commit();
        }
    }

    ...

}

新建的 ArticlePagerActivity 类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class ArticlePagerActivity extends AppCompatActivity {

    ...
    private List<Article> articleList;

    private void setArticleList() {
        ... // 获取文章列表的代码
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_article_pager);

        mViewPager = (ViewPager) findViewById(R.id.activity_article_pager_view_pager);

        setArticleList();

        FragmentManager fragmentManager = getSupportFragmentManager();
        mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {

            @Override
            public Fragment getItem(int position) {
                Article article = articleList.get(position);
                return ArticleFragment.createArticleFragment(article.getID());
            }

            @Override
            public int getCount() {
                return articleList.size();
            }
        });

        UUID articleId = (UUID) getIntent().getSerializableExtra(EXTRA_ARTICLE_ID);
        for (int i = 0; i < mNewsList.size(); i++) {
            if (articleList.get(i).getID().equals(articleId)) {
                mViewPager.setCurrentItem(i);
                break;
            }
        }
    }

    ...

}

然后,只需要在对应 fragment 类中加入新的静态方法,以由文章 id 生成对应的 fragment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ArticleFragment extends Fragment {
    ...
    private static final String ARG_NEWS_ID = "article_id";

    private Article article;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        UUID id = (UUID) getArguments().getSerializable(ARG_NEWS_ID);

        article = ... // 由 id 获取 article
    }

    static ArticleFragment createArticleFragment(UUID articleId) {
        Bundle args = new Bundle();
        args.putSerializable(ARG_NEWS_ID, articleId);

        ArticleFragment fragment = new ArticleFragment();
        fragment.setArguments(args);
        return fragment;
    }
}

静态加载 fragment 之间通信要借助其托管的 activity 的 intent 实现:

  1. 使用 getActivity().getIntent()
  2. 使用 intent 的 get 方法,获取相应的 extra 信息 但是,这样 fragment 必须直到可以启动相应 activity 的 intent 的详细结构,就把 fragment 和 activity 绑死,破坏了对 activity 的封装,也无法灵活更换 activity。实际的代码中 activity 作为管理 fragment 的一方应该知晓 fragment 知识,而 fragment 则不应该直到管理它的 activity 的信息。 动态加载方法则可以使用 fragment arguments 实现信息传递,保护了 activity 的封装。实际的示例代码也在上一节中展示完毕。