fragment的使用以及fragment大家族解析

来源:互联网 发布:女朋友说我不懂她 知乎 编辑:程序博客网 时间:2024/04/30 21:31



fragment作为用户交互的组件之一,大家都经常接触到。对于fragment的操作不熟悉,会导致很多逻辑的错误。尤其是对它的生命周期的模糊不清,很容易导致fragment初始化失败。而通过fragment事务的操作,可以让我们写代码的时候更加模块化,所以很有必要了解FragmentTransaction这个东西。其次,fragment经常作为屏幕适配的一个解决方案,所以在后面的例子会讲解同一个屏幕使用双fragment的情况以及fragment与activity的相互交互的情况。下面逐一介绍FragmentTransaction,Fragmentmanger,Fragment,ListFragment,DialogFragment,WebViewFragment。




一、FragmentTransaction



FragmentTransaction是一个定义了一系列关于fragment操作的抽象类。在调用FragmentManager对fragment进行添加、替换等操作时 ,都是通过事务的方式来启动的。所以对于FragmentTransaction的了解有助于在开发中更好的操作fragment的添加替换的操作。对于此类,有下面几个重要的方法需要理解:

①、添加fragment事务的方法操作:

public abstract FragmentTransaction add(int containerViewId, Fragment fragment, String tag)

将fragment添加至activity里,如果fragment是含有自身的视图的话(即onCreateView返回值不为null),那么fragment就会添加到activity的视图容器里。注意执行完添加操作记得使用FragmentManager.commit方法进行事务的提交。

参数解析:

containerViewId:这是一个可选的值,表示一个用于安置fragment的视图容器的ID,当此值为0是,表示当前的fragment不会被安置到视图容器里。

fragment:要添加的fragment对象,要注意的是,这个对象必须不能是此前在activity添加过的。

tag:可选的fragment的标签名称,主要目地是在后续操作中,FragmentManager.findFragmentByTag可以根据tag来查找对应的fragment。此值可传值null,但是后续就不能通过上述方法查找fragment 了。


②、替换fragment的事务操作:

 public abstract FragmentTransaction replace(int containerViewId, Fragment fragment, String tag);


用于替换已经存在的安置于containerViewId的fragment。注意执行完操作记得使用FragmentManager.commit方法进行事务的提交。此方法等同于通过移除所有与containerViewid的相关的fragment,然后再通过add方法将当前fragment添加进去。

参数解析:

containerViewId:表示要被替代的fragment的视图容器的id,同时替换成功后,也会成为当前fragment视图容器的id。

fragment:用于替换的fragment对象。

tag:和add方法一样。


③、移除fragment的事务操作:


 public abstract FragmentTransaction remove(Fragment fragment);


从activity中移除指定的已存在的fragment,如果当前fragment正好添加进了activity的视图容器了,那么fragment的视图也会被移除。


参数解析:

fragment:将要被移除的对象。


④、隐藏fragment的事务操作:


public abstract FragmentTransaction hide(Fragment fragment);


隐藏指定的fragment对象,此方法只有在指定的fragment刚好在activity的视图容器里,才会把fragment的视图隐藏起来。


⑤、显示fragment的事务操作:


public abstract FragmentTransaction show(Fragment fragment);


显示指定的fragment,这个fragment通常是被隐藏的fragment对象并且已经添加到activity的视图容器里,这样才会重新显示被影藏的fragment视图。


⑥、移除fragment视图的事务操作:


public abstract FragmentTransaction detach(Fragment fragment);


此方法用于将fragment的视图销毁,但是fragment仍然是可用的,可通过fragmentManager重新复用(在FragmentManager里面其实保存了一个栈,存放了很多fragment事务)。


⑦、附加fragment视图的事务操作:


 public abstract FragmentTransaction attach(Fragment fragment);



此方法用于将此前被移除的fragment视图重新恢复,调用后就可以将视图重新显示出来。


⑧、设置在此事务中所有fragment的显示显现动画:


 public abstract FragmentTransaction setCustomAnimations(int enter, int exit)


其中enter表示的是fragment显现的布局动画,exit是小时的。


⑨、提交事务的操作:


public abstract int commit();


将事务操作提交,此操作不会立即出发事务的执行,而是会进行排队等待,当线程准备好了才会执行,且必须是在主线程中调用这个方法。此方法必须在包含fragment的activity保存状态之前调用,否则的话会抛出异常。因为在保存状态之后调用commit方法,此fragment的状态就无法得到保存,对于activity恢复状态(比如配置改变了)来说就会发生错误。


细心的读者可以发现,上面所说的所有方法都是抽象方法,也就是说并不能直接被我们所用,那是不是说需要我们自己去实现呢。其实不是的,上面提到的这个方法只是为后面做铺垫。我们真正用于操控fragment的类是FragmentManager,FragmentManager方法有一个实现类叫做FragmentManaegerImpl,这个类里面包含了一个实现了FragmentTransaction的BackStackRecord类,后续的很多操作都是调用这个类来执行fragment的事务操作的。下面介绍一下FragmentManager。


⑩、将事务压入后退栈的操作:


 public abstract FragmentTransaction addToBackStack(String name)



fragment的事务可以被压入后退栈(activity维护的一个用于保存fragmentd的事务操作的栈),在执行了commit方法之后,此事务就会被押入栈,之后,我们在按返回键返回的时候,此事务就会出栈并会还原成上次的视图状态出现在用户面前。


参数解析:

name:与当前事务相关的名字,可以为null。


二、FragmentManager


FragmentManager是一个抽象类,里面包含了大量的与Activity中的Fragment进行交互的接口,也是我们直接打交道的进行管理Fragment的类。对于这个类,有以下几点要注意:


①、开始一个Fragment事务。


public abstract FragmentTransaction beginTransaction()

此方法用于启动一系列与当前FragmentManager相关联的Fragment的编辑操作。注意Fragment事务的创建和提交都必须在activity保存状态之前调用(Acitivty.onSaveInstanceState之前),否则会报错。因为fragment的状态也是要保存的。


②、立即执行事务操作:


 public abstract boolean executePendingTransactions();


前面说过,事务调用commit方法之后,不是立即执行的,而是会在线程里等待执行。如果需要立即执行的话,就可以调用此方法。此方法必须在主线程里面调用。


③、查找Fragment:


public abstract Fragment findFragmentById(int id);


public abstract Fragment findFragmentByTag(String tag);


上述两个方法都可以用于查找Fragment,前者是通过ID查找,即containerViewId,后者是通过tag查找。通过FragmentTransaction的介绍可以知道,tag是可以为null的,也就是说如果后续我们想查找Fragment操作的话,建议在添加fragment事务的时候,指定一个唯一的tag是个好习惯。上述两个方法都是先从当前activity查找fragment,找不到的话才会从后退栈查找。


对于FragmentManger的比较重要的方法就是这些了,因为这里边的大部分实现都是围绕着FragmentTransaction来开展的,也就是说,了解了FragmentTransaction各个方法的目的就可以实现基本的操作了。下面开始了解Fragment大家族。对于Fragment来说,主要了解它的生命周期和一些需要注意的点就可以了。另外就是需要了解ListFragment,WebViewFragment,DialogFragment的特性以及使用方法。最后会通过一个综合的例子来介绍如何应用。




三、Fragment



Fragment是一种依赖于Activity的可用于与用户交互的组件,与Fragment进行交互主要通过FragmentManager来管理,获取FragmentManager的方法有activity.getFragmentManager和fragement.getFragmentManager。fragment的用途是非常广泛的,但核心用途还是作为Activity的组件来与用户进行交互。fragment是依赖于activity存在的,但是却有着自己的生命周期,尽管如此,fragment的生命周期还是建立在activity的生命周期内的。比如说,activity处于stop状态,fragment是不可能启动的。需要注意的一点事,我们继承fragment的时候,不应该含有拥有参数的构造方法,而必须拥有一个public状态的无参构造方法(因为一个类没有构造方法的话,系统会默认有一个无参构造函数的,所以我们往往不用重写构造函数)。因为系统在恢复状态的时候,会多次调用无参构造函数进行初始化fragment,如果找不到这个函数就会进行报错。下面讲解一下Fragment的生命周期:


前面说了Fragment是依赖于activity的,也就是说,除了拥有activity生命周期的方法外,也拥有自己的生命周期。Fragment中生命周期可分为两个阶段,一个是fragment从没有到与用户交互的阶段。另一个是销毁阶段。第一个阶段的生命周期是:

onAttach:一旦fragment和activity关联上了就回调。

onCreate:创建fragment的时候回调。

onCreateView:创建和返回与fragment关联的视图。

onActivityCreated:当activity的onCreate方法完成了回调。

onViewStateResotred:但所有已保存的fragment的视图恢复完毕的时候调用。

onStart:fragment变得可见,但这个需要基于activity的onStart方法。

onResume:可与用户交互,基于activity的onRresume方法。

当fragment处于销毁阶段,需要经历一下回调方法:

onPause:fragment变得不可交互,当activity的onPause方法调用或者当前的fragment被移除的时候被调用。

onStop:fragment变得不可见,当activityd的onStop方法调用或者fragment被移除。

onDestoryView:清除fragment的视图相关的资源。

onDestory:清除fragement的状态。

onDetach:当fragment和activity分离的时候调用。


Fragment可以作为布局的一部分来使用,比如可以在布局文件里放置两个fragment标签,分别对应两个fragment,在activity创建的时候,这个fragment就会成为activity的一部分也被创建。稍后通过一个例子来说明。

Fragment的事务可以压入acitivty的一个后退栈里面,当我们按返回键的时候,它就会重新重新弹出来并恢复之前的fragment,当栈内没有事务的时候返回键事件才会被activity拦截处理。

Fragment是和Activity紧密相关的,因此,除了不能直接操作资源等与context相关的操作之外(可以通过getContext或者getAcitivty获取相关的context进行操作)其他的和我们使用activity没什么不同。对于比较不一样的地方,除了自身的生命周期之外,就是FragmentManagerImpl所提供的方法,但是这个方法是j继承了FragmentManager的,所以等于我们也知道了它所提供的方法都是什么作用。


下面介绍一些系统实现的具有特殊用途的Fragment的子类。



四、ListFragment



ListFragment是fragment的一个子类,用于显示数据列表的(用的是ListView),并且里面封装好了handler处理时间的方法,如果单击某项数据需要有特殊行动的话,就需要重写该方法(后面会详解)。用于当作数据源的一般是Array或者Cursor。因为显示数据的是ListView,所以ListFragment的用于连接数据源与listView的adpter必须是ListAdapter的子类。ListFragment默认情况下包含一个含有ListView的布局,如果我们希望自定义布局内容,那么只要重写onCreateView方法就可以了,但是有一个条件,自定义布局必须含有ListView且id必须是@android:id/list。当ListView的数据为空时,没有数据需要显示的时候,我们可以指定任意控件作为数据为空的时候的显示控件,此视图的id必须是@android:id/empty。当数据为空时,listView会隐藏并且空数据提示控件(空数据显示的视图)会显示出来。典型的布局如下:


 <?xml version="1.0" encoding="utf-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" *         android:orientation="vertical" *         android:layout_width="match_parent" *         android:layout_height="match_parent" *         android:paddingLeft="8dp" *         android:paddingRight="8dp"> * *     <ListView android:id="@id/android:list" *               android:layout_width="match_parent" *               android:layout_height="match_parent" *               android:background="#00FF00" *               android:layout_weight="1" *               android:drawSelectorOnTop="false"/> * *     <TextView android:id="@id/android:empty" *               android:layout_width="match_parent" *               android:layout_height="match_parent" *               android:background="#FF0000" *               android:text="No data"/> * </LinearLayout>

每一项数据的布局文件可通过设置Adpter的时候设置,这个 后面来讲。主要方法盘点:

①:创建布局视图,先看一下源码:

 public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        return inflater.inflate(com.android.internal.R.layout.list_content,                container, false);    }

可以看到,如果没有自定义布局的需要,我们根本不需要重写这个方法,但是如果我们有这个需求,就必须重写这个方法,并且布局必须含有一个id为@id/android:list的listView,如果希望数据为空的时候,显示提示视图,那么只要将该视图的id设为@id/android:empty即可。

②、点击事件:

public void onListItemClick(ListView l, View v, int position, long id) {    }

如果对于点击数据项需要做出反应,需要重写上述方法。
参数解析:

l:发生点击事件的listView。
v;被点击的视图。
position:被点击的视图的小标。
id:被点击的是图的id。


③、设置数据适配器:

public void setListAdapter(ListAdapter adapter) {

注意必须是ListAdapter的子类,如果对于Adapter不是很了解,可以参考Adapter大家族详解。每项数据的样式也是通过adpter设置的。

以上就是ListFragment的解析。接下来讲解WebViewFragment。




五、WebViewFragment:




顾名思义,这是一个嵌套了webView的Fragment,并且该webView会跟随Fragment的pause而暂停,start而启动。这个类其实没啥好说的,就跟我们自己写个布局文件,里面放个webView接着在Fragment里操作一样的。不过如果使用这个类的话,可以通过getWebView方法获取WebViewFragment的Webview,然后我们就可对它进行一些设置。下面讲解DialogFragment。





六、DialogFragment:




这个Fragment含有一个Dialog对象,这个对象会根据fragment的状态来决定什么时候hide、show、dismiss。如果我们要控制dialog不要直接对它执行操作,而是应该使用这个fragment给我们提供的api。如果要继承DialogFragment,应该重写onCreateView来设置dialog的布局。如果想要完全控制dialog的样式,可以通过onCreateDialog来控制。这个类的优点在于将dialog与fragment的生命周期紧密相连起来了,但是笔者认为如果处理好dialog与fragment的关系,自己直接继承fragment是更好的选择。如果读者对此类感兴趣,可自行学习。




接下来使用一个综合运用了上述所有知识的例子来讲述具体的用法。下面这个例子是关于显示数字的。当处于竖屏模式的时候,会显示一个显示1-10的数字列表,点击其中一个就会跳转到另一个activity显示相应的数据。当时横屏模式的时候,会显示一个有两个fragment的activity ,单击左边的数据会在右边显示出来。

首先资源文件:

strings-xml:

<resources>    <string name="app_name">fragment</string>    <string-array name="number">        <item>1</item>        <item>2</item>        <item>3</item>        <item>4</item>        <item>5</item>        <item>6</item>        <item>7</item>        <item>8</item>        <item>9</item>        <item>10</item>    </string-array></resources>

布局文件分为竖屏模式和横屏模式,先看目录结构:




layout/activity_main.xml:


<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.cw.fragment.MainActivity">    <FrameLayout        android:id="@+id/container"        android:layout_width="match_parent"        android:layout_height="match_parent"></FrameLayout></LinearLayout>


layout-land/activity_main.xml:


<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal"    android:weightSum="2"    tools:context="com.cw.fragment.MainActivity">    <FrameLayout        android:id="@+id/container"        android:layout_width="0dp"        android:layout_height="match_parent"        android:layout_weight="1"></FrameLayout>    <fragment        android:id="@+id/rightFragment"        android:name="com.cw.fragment.RightFragment"        android:layout_width="0dp"        android:layout_height="match_parent"        android:layout_weight="1"        tools:layout="@layout/right_layout" /></LinearLayout>


上述的rightfragment在后面会提到。

layout/right_layout:


<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="#efefef"    android:gravity="center"    android:orientation="vertical">    <TextView        android:id="@+id/content"        android:layout_width="match_parent"        android:layout_height="30dp"        android:gravity="center"        android:text="内容" /></LinearLayout>

接着定义RightFragment:


package com.cw.fragment;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;/** * Created by Myy on 2016/7/28. */public class RightFragment extends Fragment {    private View view;    private TextView textView;    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        view = inflater.inflate(R.layout.right_layout, null);        textView = (TextView) view.findViewById(R.id.content);        return view;    }    public void setContent(int i) {        textView.setText(i + "");    }}


然后是RightActivity:


package com.cw.fragment;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;/** * //用于单Fragment * Created by Myy on 2016/7/28. */public class RightActivity extends AppCompatActivity {    private TextView textView;    private RightFragment fragment;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        fragment = new RightFragment();        getSupportFragmentManager().beginTransaction().add(R.id.container, fragment).commit();    }    @Override    protected void onStart() {        super.onStart();        int i = getIntent().getIntExtra("position", 0);        fragment.setContent(i);    }}

最后是mainActivity:


package com.cw.fragment;import android.content.Intent;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import android.support.v4.app.ListFragment;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.ListAdapter;import android.widget.ListView;import java.util.Arrays;import java.util.List;public class MainActivity extends AppCompatActivity {    private List<String> numbers = null;    //注意包的引用    private ListFragment leftFragment;    private RightFragment rightFragment;    private ListView listView;    private boolean isTwoPage = false;//用于判断是否是双fragment模式,在这里竖屏模式设为双fragment模式    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        numbers = Arrays.asList(getResources().getStringArray(R.array.number));        initListFragment();        FragmentManager manager = getSupportFragmentManager();        rightFragment = (RightFragment) manager.findFragmentById(R.id.rightFragment);//当加载的是layout-land布局的fragment才不为null        if (rightFragment != null)            isTwoPage = true;        FragmentTransaction transaction = manager.beginTransaction();        transaction.add(R.id.container, leftFragment);        transaction.commit();        // transaction.addToBackStack(null);可以将此事务添加进后退栈里面        //fragment调用activityd的方法。通过getActivity()        //acitivty调用fragment的方法。getFragmentManager().findFragmentById()    }    @Override    protected void onStart() {        super.onStart();        listView = leftFragment.getListView();        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                if (isTwoPage)                    rightFragment.setContent(position + 1);                else {                    Intent intent = new Intent(MainActivity.this, RightActivity.class);                    intent.putExtra("position", position+1);                    startActivity(intent);                }            }        });    }    private void initListFragment() {        leftFragment = new ListFragment();        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, numbers);        leftFragment.setListAdapter(adapter);    }}

上述可以看到用ListFragment添加到左边的Framelayout布局,使用了事务添加操作。


看看竖屏模式的效果:




点击:




切换竖屏模式:





点击:




如上,请读者好好体会这个例子,可以发现fragment和activity的异同以及事务使用的方法。


---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------



2 0
原创粉丝点击