Fragment专题

Fragment是什么?

Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment, 我们可以把他看成一个小型的Activity,又称Activity片段!想想,如果一个很大的界面,我们 就一个布局,写起界面来会有多麻烦,而且如果组件多的话是管理起来也很麻烦!而使用Fragment 我们可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理!从而可以更加方便的在 运行过程中动态地更新Activity的用户界面!另外Fragment并不能单独使用,他需要嵌套在Activity 中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响,比如Activity 被destory销毁了,他也会跟着销毁!

静态使用Fragment

  1. 继承Fragmentt或者它的子类,重写onCreateView决定Fragemnt的布局。
public class FirstFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view=inflater.inflate(R.layout.fragment_first, container, false);
        TextView tv_hello= (TextView) view.findViewById(R.id.tv_hello);
        tv_hello.setText("MyFirstFragment");
        return view;
    }
}

布局代码:

<TextView
        android:id="@+id/tv_hello"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="HelloWorld" />
  1. 在Activity中使用此Fragment,就当和使用普通的View一样。
    使用Fragment的Activity的布局代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.lg.www.blog.MainActivity">
    <fragment
        android:id="@+id/fragment1"
        android:name="com.lg.www.blog.FirstFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" />
</RelativeLayout>
  • Fragment是在android3.0版本引入的,如果你使用的是3.0之前的系统,需要先导入android-support-v4的jar包才能使用Fragment功能,所在的那个Activity就要继承FragmentActivity,且Fragment的方法都要使用带Support的那个(如getSupportFragmentManager()方法);本专题里面所有的Fragment都是没有兼容3.0之前版本的。

动态使用Fragment

  1. 将静态使用时的Activity的布局代码上的fragment删除。
  2. 动态加载流程图:


    29546434.jpg
  3. 关键代码:
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FirstFragment fragment = new FirstFragment();
        getFragmentManager().beginTransaction().add(R.id.activity_main, fragment).commit();
    }
}

Fragment的生命周期图

31722863.jpg
  • Fragment需要嵌套在Activity中使用,当然也可以嵌套到另外一个Fragment中,但这个被嵌套 的Fragment也是需要嵌套在Activity中的,间接地说,Fragment还是需要嵌套在Activity中! 受寄主Activity的生命周期影响,当然他也有自己的生命周期!另外不建议在Fragment里面 嵌套Fragment因为嵌套在里面的Fragment生命周期不可控!
  • 官方文档说创建Fragment时至少需要实现三个方法:onCreate( ),onCreateView( ),OnPause( ); 不过貌似只写一个onCreateView也是可以的;除了onCreateView方法,其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
  • 停止状态的fragment仍然活着(所有状态和成员信息被系统保持着),然而,它对用户不再可见,并且如果activity被干掉,他也会被干掉.
  • 走一趟生命周期图:
    ①Activity加载Fragment的时候,依次调用下面的方法: onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart ->onResume
    ②当我们弄出一个悬浮的对话框风格的Activity,或者其他,就是让Fragment所在的Activity可见,但不获得焦点 onPause
    ③当对话框关闭,Activity又获得了焦点: onResume
    ④当我们替换Fragment,并调用addToBackStack()将他添加到Back栈中 onPause -> onStop -> onDestoryView !!注意,此时的Fragment还没有被销毁哦!!!
    ⑤当我们按下键盘的回退键,Fragment会再次显示出来: onCreateView -> onActivityCreated -> onStart -> onResume
    ⑥如果我们替换后,在事务commit之前没有调用addToBackStack()方法将 Fragment添加到back栈中的话;又或者退出了Activity的话,那么Fragment将会被完全结束, Fragment会进入销毁状态 onPause -> onStop -> onDestoryView -> onDestory -> onDetach

Fragment管理与Fragment事务

97188171.jpg
  1. Fragment常用的三个类:
    android.app.Fragment 主要用于定义Fragment
    android.app.FragmentManager 主要用于在Activity中操作Fragment
    android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白。
  2. 获取FragmentManage的方式:
    getFragmentManager() ,v4中,getSupportFragmentManager
  3. 主要的操作:都是FragmentTransaction的方法
    FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
  • transaction.add()
    往Activity中添加一个Fragment
  • transaction.remove()
    从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
  • transaction.replace()
    使用另一个Fragment替换当前的,实际上就是remove(),然后add()的合体
  • transaction.hide()
    隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
  • transaction.show()
    显示之前隐藏的Fragment
  • transaction.detach()
    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
  • transaction.attach()
    重建view视图,附加到UI上并显示。
    transatcion.commit()//提交一个事务,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。
  1. 值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
    a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。
    b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。
    c、remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach。
    d、在Activity的onCreate方法里面调用transaction.add() 方法添加Fragment时进行如下处理:
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main);  
        if(savedInstanceState == null) {  
            //只有当savedInstanceState == null才调用transaction.add() 方法
            transaction.add(...) ;
        } 
    }  

这样做的目的是为了避免重复实例化一个相同的新的Fragment并重复添加到Activity上,因为当某些原因(屏幕旋转,长时间处于后台等)Activity重新调用onCreate恢复页面时,它会从savedInstanceState中取出原来保存的数据(包括Fragment的实例)进行恢复,这个时候就不必重新new了,Android系统会自动把保存的数据取出并还原的。

Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

  • 添加到回退栈的方法:FragmentTransaction.addToBackStack(String);
    eg:FragmentTransaction.addToBackStack(null);
  • 根Fragment(第一个添加到Activity的Fragment)一般不添加到回退栈当中,因为如果根Fragment绑定的Activity上面没有任何UI控件的话,点击Back键将返回上一次的保存的Fragment,这时候由于这个Fragment已经是最根部Fragment了,就会返回到空白的Activity界面了。
  • 使用replace方法切换Fragment时(replace是remove和add的合体),如果不添加事务到回退栈,前一个Fragment实例会被销毁。调用FragmentTransaction.addToBackStack(null)将当前的事务添加到回退栈后,前一个Fragment实例不会被销毁,但是视图层次依然会被销毁,即会调用前一个Fragment的onDestoryView方法和替换Fragment的onCreateView方法。

Fragment与Activity的交互

45971686.jpg
  1. 组件获取
  • Fragment获得Activity中的组件: getActivity().findViewById(R.id.list);
  • Fragment中获得Context的方法:getActivity();
  • Activity获得Fragment中的组件(根据id和tag都可以):
    getFragmentManager().findFragmentById(R.id.fragment1);
    getFragmentManager().findFragmentByTag("fragment1") ;
  1. 数据传递
    ①Activit传递数据给Fragment:
    在Activity中创建Bundle数据包,调用Fragment实例的setArguments(bundle) 从而将Bundle数据包传给Fragment,然后Fragment中调用getArguments获得 Bundle对象,然后进行解析就可以了。
    ②Fragment传递数据给Activity
    在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口, Fragment就可以通过回调接口传数据了,其实就是接口回调。
    Step 1:定义一个回调接口:(Fragment中)
/*接口*/  
public interface CallBack{  
    /*定义一个获取信息的方法*/  
    public void getResult(String result);  
}  

Step 2:接口回调(Fragment中)

 /*本地私有回调接口,调用出通过回调接口方法传入*/
    private CallBack mCallback;
    /*设置回调接口*/
    public void getData(CallBack callBack){
        this.mCallback=callBack;
    }
    /*将需要传递给Activity数据的地方将数据msg传递过去*/
    mCallback.getResult(msg);

Step 3:使用接口回调方法读数据(Activity中)

/* 使用接口回调的方法获取数据 */  
fragment.getData(new FirstFragment.CallBack() {
            @Override
            public void getResult(String result) {
                //将回传数据进行处理
            }
});
  • 在Fragment定义接口的抽象方法时,你要传什么类型的数据参数就设置什么类型;
    ③Fragment与Fragment之间的数据互传
    其实这很简单,找到要接受数据的fragment对象,直接调用setArguments传数据进去就可以了 通常的话是replace时,即fragment跳转的时候传数据的,那么只需要在初始化要跳转的Fragment 后调用他的setArguments方法传入数据即可!
    如果是两个Fragment需要即时传数据,而非跳转的话,就需要先在Activity获得f1传过来的数据, 再传到f2了,就是以Activity为媒介。

使用Fragment.setArguments(Bundle bundle)来传递参数

首先我们来看下AndroidStudio提供的创建BlankFragment的工厂方法代码:

 /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment BlankFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static BlankFragment newInstance(String param1, String param2) {
        BlankFragment fragment = new BlankFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

从中我们可以看到官方建议我们用这种方法创建Fragment,这样可以传递参数给Fragment。但是为什么不推荐我们使用带参的构造方法传递参数呢?
原因就是因为当设备配置参数发生变化,如横竖屏切换时,系统会重新恢复(会从savedInstanceState中取出原来保存的数据,包括Activity、Fragment的实例进行恢复)创建Activity,同时也会重新构建它所管理的Fragment(默认调用无参的那个构造方法进行创建),原先的Fragment构造方法传递过来的参数将会全部丢失,但是通过Fragment.setArguments(Bundle bundle)方法设置的bundle会通过savedInstanceState保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递参数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容