Android基础—Fragment

设计理念

Android3.0(API11)开始引入Fragment,主要是为了在大屏幕上更好的支持动态和灵活的UI设计。以平板为例,有足够大的空间容纳更多的组件,管理起来无疑过于麻烦。Fragment能够分割Activity的layout,而且具备本身的生命周期,从而使得组件独立管理成为可能。正由于fragment具备独立的生命周期,因此能够定义本身的layout和行为,从而一个fragment能够在多个activity中重复调用。因此咱们在设计fragment的时候,要尽可能追求模块化和可重用性。通常在平板和TV上广泛使用。android

举例以下:在平板上,activity使用一个fragment展现文章列表,使用另外一个fragment展现文章内容;在手机上,activity使用一个fragment展现文章列表,点击跳转到另外一个activity展现。bash

Fragment模块化和复用性
Fragment模块化和复用性

Fragment建立

fragment的生命周期和activity很是相似,都有onCreate()、onStart()、onStop()、onPause()回调方法。fragment通常至少要实现三个回调方法:app

  1. onCreate():fragment建立时调用,主要初始化一些重要的组件
  2. onCreateView():fragment绘制界面时调用(setContentView(id)),若是fragment有界面的话须要返回一个view;若是fragment没有UI,直接返回空便可
  3. onPause():用户离开fragment,通常要保存数据(用户可能不会再回来)

fragment类型

常见的有三种fragment,分别是DialogFragment、ListFragment、PreferenceFragmentide

  1. DialogFragment:展现浮动对话框
  2. ListFragment:展现一列数据
  3. PreferenceFragment:以列表形式展现首选项对象的层级关系

建立fragment

fragment通常具备本身的UI,并成为activity UI的一部分。为了给fragment建立UI,必须实现onCreateView()回调方法,并返回一个view(fragment的根layout)。模块化

若是fragment是ListFragment的子类,onCreateView()默认返回ListView,不须要重写。布局

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}复制代码

container是activity的layout,fragment的layout将会被插入其中;savedInstanceState即保存的fragment被异常销毁前的状态;inflate()有三个参数:ui

  1. id:flagment的view的资源id
  2. container:activity的layout,fragment的layout将插入其中
  3. false:false表示fragment仅为container提供layout 参数(动态添加、删除、替代fragment时使用);true表示container成为fragment layout的父view(在activity布局文件中定义fragment使用)

把fragment添加到activity中

通常来讲,fragment的layout会被嵌入成为activity view层级的一部分。有两种方式能够把fragment添加到activity的layout中:1.静态加载;2.动态加载。this

  1. 静态加载:在activity layout中声明fragment,代码以下:spa

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:layout_behavior="@string/appbar_scrolling_view_behavior"
     tools:context="com.example.chaos.myapplication.MainActivity"
     tools:showIn="@layout/activity_main">
    
     <fragment
         android:id="@+id/fragment_person_list"
         android:name="com.example.chaos.myapplication.PersonListFragment"
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1" />
    
     <fragment
         android:id="@+id/fragment_person_detail"
         android:name="com.example.chaos.myapplication.DetailFragment"
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1" />
    </LinearLayout>复制代码

    系统建立该activity的layout时,会将指定的fragment所有实例化并分别调用他们的onCreateView()回调。而后检索fragment的layout,并将onCreateView()返回的view取代<fragment>元素直接插入activity的layout中。线程

activity重启时,须要恢复fragment的数据,这就要求fragment要有个惟一的标识符(不然没法管理fragment)。有三种方式能够为fragment赋予惟一的id

  1. 使用android:id属性赋予id
  2. 使用android:tag属性赋予惟一标签
  3. 若是id和tag都没提供的话,系统模式使用container view的id

2.. 动态加载:动态添加fragment到ViewGroup中
只要在activity在运行,均可以添加fragment到activity的layout中,只须要指定ViewGroup存放fragment就行。
为了在activity中操做fragment的事务(add、remove、replace),必须使用FragmentTransaction()。代码以下:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();复制代码

添加fragment使用add()方法,指定ViewGroup和要添加的fragment,对fragmentTransaction作的任何改动都要执行commit()方法才能生效。

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();复制代码

添加一个没有UI的fragment

并不是全部fragment都必须有UI,fragment也能够用来执行后台操做,此时,无需UI也能够。该如何添加一个没有UI的fragment呢?

首先,须要先经过findFragmentByTag()找到,由于tag是惟一能标识它的(只能经过add(fragment,string)主动设置tag)。

若是findFragmentByTag()返回null的话,须要经过add(fragment,string)为fragment主动设置tag并添加。

Fragment管理

fragment的管理是由FragmentManager实现的,能够在activity中经过getFragmentManager()获取FragmentManager。FragmentManager的功能以下:

  1. 使用findFragmentById()(针对在activity layout提供一个UI的fragment)和findFragmentByTag()(针对没有UI的fragment)获取activity中已存在的fragment
  2. 调用popBackStack()将fragment从返回栈弹出(相似于用户点击back)
  3. 调用addOnBackStackChangedListener()注册监听器监听返回栈的变化

静态加载的fragment必定具备id或tag;动态加载的id必定具备container view的id或tag(fragment若是没有惟一标识符,则没法进行有效管理),动态加载一共有三个方法:

  1. add(Fragment fragment, String tag):适用于有UI和无UI的fragment
  2. add(@IdRes int containerViewId, Fragment fragment):适用于有UI的fragment
  3. add(@IdRes int containerViewId, Fragment fragment,String tag);:适用于有UI的fragment

因此没有UI的fragment,只能经过findFragmentByTag()获取;有UI的fragment能够经过id或tag获取。

也可使用FragmentManager打开FragmentTransaction,从而操做fragment的事务,如添加或移除。

Fragment事务处理

一个事务就是一系列变化,事务具备原子性、一致性、隔离性、持久性。可使用FragmentTransaction实现事务,也能够把事务放到由activity管理的返回栈中,便于用户返回以前的fragment的状态.

能够从FragmentManager获取FragmentTransaction实例

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();复制代码

FragmentTransactioni能够借由add()、remove()、replace()实现对事务的操做,并且每次操做必须实现commit()才能生效。

若是想把fragment加入到返回栈,以便用户退回到fragment原先的状态的话,须要在commit()以前调用addBackStack()方法:

DetailFragment detailFragment = new DetailFragment();
 fragmentTransaction.add(detailFragment,"");
 fragmentTransaction.addToBackStack(null);
 fragmentTransaction.commit();复制代码

为了检索返回栈中的fragment,必须监听onBackPressed()事件,不然的话若是栈中存在其余activity的话,就会直接返回到其余activity中;若是没有activity的话,就会直接退出应用。

@Override
    public void onBackPressed() {
        if (getFragmentManager().getBackStackEntryCount() > 0){
            getFragmentManager().popBackStack();
        }else {
            super.onBackPressed();
        }
    }复制代码

添加改变到Transaction中的顺序可有可无,除非:

  1. 最后调用了commit()方法
  2. 添加了多个改变到同一个container中,添加的顺序决定了container中view层级的顺序

若是没有调用addBackStack(),当remove一个fragment时,commit以后会马上销毁,用户没法返回;若是添加到返回栈中,remove一个fragment时,commit以后会处于stop状态,用户能够返回到以前的状态(resume)

commit()这个操做运行在主线程中,也就意味着调用commit()以后要排序执行,并不会马上执行事务。若是想马上执行的话,能够在commit()以前调用excutePendingTransactions(),不过通常并没有此必要。

commit()必须在onSaveInstanceState()以前调用,由于在此以后,activity不会保存数据,致使commit()调用会抛出异常;若是容许状态数据丢失的话,能够调用 commitAllowingStateLoss()。

与Activity的通讯

虽然fragment做为对象的实现和activity存在很大不一样,可是做为activity的一部分,fragment又和activity紧密联系。

  1. 从fragment中调用activity
    View listView = getActivity().findViewById(R.id.list);
  2. 从activity中调用fragment
    DetailFragment detailFragment1 = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_person_detail);复制代码

    为activity建立接口

    常见的模型:activity包含两个fragment,左边fragment显示标题列表,右边fragment显示内容。点击左侧的fragment的标题,更新右侧的fragment内容。实现方式就是在左边fragment建立接口,并在接口中实现传值方法,activity实现该接口,监听方法,而后将接受到的值传给右边的fragment。

fragment建立接口和方法:

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}复制代码

把activity转换成接口,从而实现activity监听

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}复制代码

在fragment的onListItemClick事件中调用传值方法给activity

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected(noteUri); } ... }复制代码

Fragment生命周期

和activity同样,fragment也有三种基本状态:

  1. 运行态:fragment在正在运行的activity中处于可见状态
  2. 暂停态:另外一个activity位于前台并获取焦点,fragment所在的activity仍然可见(前台activity半透明或没有所有覆盖fragment所在activity)
  3. 中止态:fragment所在activity处于stop状态,或者fragment被remove掉,可是添加到返回栈中了。处于暂停态的fragment仍然存活(系统仍然保留状态信息和成员变量),可是已再也不可见,且activity被销毁时,fragment也会被销毁。

fragment和activity生命周期的一个最大差异在于两者在返回栈中的存储。activity的返回栈是由系统管理的;fragment的返回栈是由activity管理的(当fragment被remove时,调用addBackStack())。

fragment若是须要context对象的话,能够调用getActivity(),可是这个方法要在onAttach()以后、onDetach()以前调用才有效,不然将会返回null。

fragment和activity生命周期比较
fragment和activity生命周期比较
相关文章
相关标签/搜索