自己好久不写APK的界面部分了,平时的工作主要集中在APK的控制层和模型层,
或者干脆就深入到Android框架及更底层的部分了,因此许多界面的知识都有些遗忘了。

这次主要结合Android权威指南中的例子,回忆一下以前的知识,以博客的方式记录一下,
Android中使用android.support.v4.app.Fragment的基本方式。

一、Fragment的使用
我们知道Fragment的生命周期由嵌入的Activity管理。
Activity托管Fragment主要有如下两种方式:
1、在Activity的布局中添加fragment;
2、在Activity的代码中加入fragment。

第一种方式就是使用布局fragment。这种方式简单但不够灵活。
在Activity的布局中添加fragment,就等同于将fragment及其视图与activity的视图绑定在一起,
并且在activity的生命周期过程中,无法切换fragment视图。

第二种方式比较复杂,但也是唯一可以在运行时控制fragment的方式。
我们可以自行决定何时添加fragment、移除fragment等。

因此,为了追求真正灵活的UI设计,就必须以第二种方式,即以代码的方式添加fragment。
接下来,我们就看看以代码添加fragment的步骤。
考虑到兼容性,我们主要使用的是android支持库中的fragment,即android.support.v4.app.Fragment等相关类。

Fragment终究是需要嵌入到activity中,因此首先需要定义Activity的布局,示例如下:

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

示例Activity的布局及其简单,就是定义一个FrameLayout,作为Fragment布局的容器。

对应的Activity代码如下:

//定义一个抽象的父类public abstract class SingleFragmentActivity extends FragmentActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_fragment);        //FragmentManager管理fragment队列等        FragmentManager fm = getSupportFragmentManager();        //R.id.fragment_container是fragment容器的id        //先从Fragment队列中查找是否有容器id对应的fragment        Fragment fragment = fm.findFragmentById(R.id.fragment_container);        if (fragment == null) {            //由子类实现,创建Fragment            fragment = createFragment();            //创建一个FragmentTransaction(fragment事务)            fm.beginTransaction()                    //告诉fm, fragment应该放置的位置,同时用容器id作为fragment的标识符                    //当需要向Activity添加多个fragment时,通常要分别为每个fragment创建具有不同资源ID的容器                    .add(R.id.fragment_container, fragment)                    .commit();        }    }    protected abstract Fragment createFragment();}

从上面的代码可以看出,FragmentManager首先利用findFragmentById接口,
利用容器视图的资源ID,在fragment队列中查找对应的fragment。
当利用容器视图的资源ID找不到对应的fragment时,才创建新的fragment。

这是因为,当设备旋转或回收内存时,Android可能销毁当前的Activity。
当Activity被销毁时,它的FragmentManager会将fragment队列保存下来。

当Activity重建时,会再次调用其onCreate方法。
此时新的FragmentManager会首先获取保存的队列,然后重建fragment队列,从而恢复到原来的状态。

在SingleFragmentActivity的基础上,使用Fragment就变得很容易了。示例如下:

public class CrimeActivity extends SingleFragmentActivity {    private static final String EXTRA_CRIME_ID =            "stark.a.is.zhang.criminalintentapp.crime_id";    @Override    //子类只需要实现createFragment方法即可    protected Fragment createFragment() {        ...............        return CrimeFragment.newInstance(crimeId);    }    ...........}

看看CrimeFragment的实现:

public class CrimeFragment extends Fragment{    ...........    public static CrimeFragment newInstance(UUID crimeId) {        ..............        CrimeFragment crimeFragment = new CrimeFragment();        ..............        return crimeFragment;    }    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        .............    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        //加载Fragment自己的视图        View v = inflater.inflate(R.layout.fragment_crime, container, false);        .............        return v;    }}

Activity的FragmentManager负责调用队列中fragment的生命周期方法。
Activity添加fragment供FragmentManager管理时,Fragment的onAttach、onCreate以及onCreateView方法会被调用。
托管Activity的onCreate方法执行后,Fragment的onActivityCreated方法也会被调用。

FragementManager将保证fragment与Activity的运行状态保持一致。
例如,在运行态的Activity中添加Fragment时,FragmentManager将立即调用Fragment的
onAttach、onCreate、onCreateView、onActivityCreated、onStart和onResume方法,使Fragment的运行状态“追赶”上Activity。
一旦fragment的状态与Activity保持了一致,FragmentManager就会接受操作系统的指示,
按照Activity的状态,调用Fragment的其他生命周期方法。

以上是Fragment的基本用法,接下来看看Fragment之间的参数传递。

二、Fragment通过Activity进行参数传递
Fragment可以利用startActivity的方式启动另一个Activity,并在Intent中携带额外的信息。
假设另一个Activity,也是由Fragment来完成界面的布局,那么目的Fragment可以从对应的托管Activity获取传递的参数。
例如:

public class CrimeFragment extends Fragment {    .......    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //从托管Activity中获取Intent,然后再获取额外信息        UUID crimeId = (UUID) getActivity().getIntent().getSerializable(ARG_CRIME_ID);        ................    }}

这种做法比较简单,但破坏了Fragment的封装性。
Fragment与特定的Activity绑定了,不再是可复用的构建单元。

针对上述问题,一种比较好的解决方案是使用Fragment的argument。
每个fragment实例都可以附带一个Bundle对象。
该Bundle对象包含键值对,一个键值对就是一个argument。
要附加argument给fragment,需要调用Fragment.setArguments方法,
且必须在fragment创建后、添加到activity前完成。

对于上面代码中例子,可以在Fragment的构造函数中,完成argument的设置:

public class CrimeActivity extends SingleFragmentActivity {    .................    @Override    //父类在将fragment加入到FragmentManager前,先调用该方法构造Fragment    protected Fragment createFragment() {        //获取启动的Intent,从中得到消息        UUID crimeId = (UUID) getIntent().getSerializableExtra(CrimeActivity.EXTRA_CRIME_ID);        //调用Fragment提供的接口,设置arguments        return CrimeFragment.newInstance(crimeId);    }    .....}

再看看Fragment的完整实现:

public class CrimeFragment extends Fragment{    ...........    //个人觉得,CrimeFragment可以提供多种newInstance函数,以满足不同Activity的使用需求    //达到Fragment与Activity解耦的目的    public static CrimeFragment newInstance(UUID crimeId) {        //在Fragment创建时,创建bundle        Bundle args = new Bundle();        args.putSerializable(ARG_CRIME_ID, crimeId);        CrimeFragment crimeFragment = new CrimeFragment();        //利用fragment的setArguments方法,将bundle设置到arguments中        crimeFragment.setArguments(args);        return crimeFragment;    }    @Override    //fragment被Activity加入到FragmentManager后,onCreate函数被调用    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //利用getArguments函数,得到Bundle对象,于是可以获取之前存入的数据        //如果被多个Activity使用,那么获取参数时就需要对应的判断了        UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);        ................    }}

通过上述的方式,可以一定程度上实现Fragment与托管Activity的解耦的目的。

在本文的最后,稍微提一点:
Fragment可以覆盖Activity的startActivityForResult方法和onActivityResult方法,于是可以处理启动Activity后的返回值。

不过Fragment没有定义setResult方法,因此目的Fragment如果需要设置返回结果,
需要调用托管Activity的setResult方法,即在Fragment中,需要调用类似如下代码:

........... getActivity.seResult(Activity.RESULT_OK, data); ...........

三、同一个Activity托管Fragment之间的参数传递
源Fragment向目的Fragment传递参数时,与上文一样,仍然需要利用Fragment的arguments。
不过,由于是同一个Activity托管的两个Fragment,因此可以将它们之间的通信关系确定下来。
如下面的示例代码所示:

.............mDateButton.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View view) {        FragmentManager fm = getFragmentManager();        //目的Fragment为DatePickerFragment,在newInstance时,源Fragment将参数写入到其arguments中        DatePickerFragment dialog = DatePickerFragment.newInstance(mCrime.getDate());        //调用目的Fragment的setTargetFragment方法,此时就将源、目的Fragment之间的关系确定下来了        //REQUEST_DATE与startActivityForResult中的request_code一致        dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);        //显示目的Fragment        dialog.show(fm, DIALOG_DATE);    }});...............

源Fragment和目的Fragment之间的关系确定后,目的Fragment就可以通过如下方式,将消息发送给源Fragment:

...............private void sendResult(int resultCode, Date date) {     //判断是否有绑定的源Fragment     if (getTargetFragment() == null) {         return;     }     Intent intent = new Intent();     intent.putExtra(EXTRA_DATE, date);    //直接调用源Fragment的onActivityResult返回结果     getTargetFragment().onActivityResult(            getTargetRequestCode(), resultCode, intent);}..........

更多相关文章

  1. GitHub 标星 2.5K+!教你通过玩游戏的方式学习 VIM!
  2. Android(安卓)双击返回键退出程序的方法总结
  3. postDelayed方法和removeCallbacks方法的使用(1)
  4. Android中Fragmen首选项使用自定义的ListPreference的方法
  5. Android第三方登录之微信登录
  6. Android学习之Android(安卓)studio TraceView和lint工具的使用详
  7. ARouter 源码解析(零) 基本使用
  8. android 输入法(包括手写界面)
  9. Kotlin Android(安卓)UI利器之Anko Layouts

随机推荐

  1. 技术架构如何抓大放小
  2. 什么样的代码规范才能得到程序员的认可?
  3. .NET成年了,然后呢?
  4. IntelliJ IDEA 2021最新激活码(亲测有效,
  5. 手把手教你从0到1写一个简单的缓存框架
  6. 出海做to B,有没有机会?
  7. 非名校95后,拿到百度和今日头条offer,他是
  8. 职场选择,还真就不是一个人的事
  9. 快递单号查询,自动快递单号查询软件
  10. 单集群10万节点 走进腾讯云分布式调度系