【Android开发基础系列】Fragment专题

1 Android Fragment基本介绍

1.1 Fragment

  Android是在Android 3.0 (API level 11)开始引入Fragment的。

  可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。

  可以把Fragment设计成可以在多个Activity中复用的模块。

  当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。

  如图:


1.2 Fragment的生命周期

  因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。

  如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。

  但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。

  当这样进行fragment transaction(转换)的时候,可以把fragment放入Activity的back stack中,这样用户就可以进行返回操作。

1.3 Fragment的使用相关

  使用Fragment时,需要继承Fragment或者Fragment的子类(DialogFragment, ListFragment, PreferenceFragment, WebViewFragment),所以Fragment的代码看起来和Activity的类似。

1.3.1 使用Support Library

  Support Library是一个提供了API库函数的JAR文件,这样就可以在旧版本的Android上使用一些新版本的APIs。

  比如android-support-v4.jar.它的完整路径是:

  /extras/android/support/v4/android-support-v4.jar.

  它就提供了Fragment的APIs,使得在Android 1.6 (API level 4)以上的系统都可以使用Fragment。为了确定没有在旧版本系统上使用新版本的APIs,需要如下导入语句:

  importandroid.support.v4.app.Fragment;

  import android.support.v4.app.FragmentManager;

  同时应该将上述的包拷入libs项目下的libs文件夹,然后在项目的Properties中添加:右键单击项目,选Properties,左边选Java Build Path,然后Add External JARs…,添加android-support-v4.jar.

        当创建包含FragmentActivity时,如果用的是Support Library,那么继承的就应该是FragmentActivity而不是Activity

1.3.2 必须实现的三个回调函数

  onCreate()

  系统在创建Fragment的时候调用这个方法,这里应该初始化相关的组件,一些即便是被暂停或者被停止时依然需要保留的东西。

  onCreateView()

  当第一次绘制Fragment的UI时系统调用这个方法,必须返回一个View,如果Fragment不提供UI也可以返回null。

  注意,如果继承自ListFragment,onCreateView()默认的实现会返回一个ListView,所以不用自己实现。

  onPause()

  当用户离开Fragment时第一个调用这个方法,需要提交一些变化,因为用户很可能不再返回来。

1.3.3 实现Fragment的UI

  提供Fragment的UI,必须实现onCreateView()方法。

  假设Fragment的布局设置写在example_fragment.xml资源文件中,那么onCreateView()方法可以如下写:

public static class ExampleFragment extends Fragment

{

    @Override

    public ViewonCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

    {

        // Inflate the layout for this fragment

        return inflater.inflate(R.layout.example_fragment, container, false);

    }

}

  onCreateView()中container参数代表该Fragment在Activity中的父控件;savedInstanceState提供了上一个实例的数据。

  inflate()方法的三个参数:

  第一个是resource ID,指明了当前的Fragment对应的资源文件;

  第二个参数是父容器控件;

  第三个布尔值参数表明是否连接该布局和其父容器控件,在这里的情况设置为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一个View Group。

1.4 把Fragment加入Activity

  当Fragment被加入Activity中时,它会处在对应的View Group中。

  Fragment有两种加载方式:一种是在Activity的layout中使用标签声明;另一种方法是在代码中把它加入到一个指定的ViewGroup中。

  另外,Fragment它可以并不是Activity布局中的任何一部分,它可以是一个不可见的部分。这部分内容先略过。

1.4.1 加载方式1:通过Activity的布局文件将Fragment加入Activity

  在Activity的布局文件中,将Fragment作为一个子标签加入即可。

  如:

  其中android:name属性填上你自己创建的fragment的完整类名。

        当系统创建这个Activity的布局文件时,系统会实例化每一个fragment,并且调用它们的onCreateView()方法,来获得相应fragment的布局,并将返回值插入fragment标签所在的地方。

  有三种方法为Fragment提供ID:

  android:id属性:唯一的id

  android:tag属性:唯一的字符串

  如果上面两个都没提供,系统使用容器view的ID。

1.4.2 加载方式2:通过编程的方式将Fragment加入到一个ViewGroup中

  当Activity处于Running状态下的时候,可以在Activity的布局中动态地加入Fragment,只需要指定加入这个Fragment的父View Group即可。

  首先,需要一个FragmentTransaction实例:

FragmentManager fragmentManager = getFragmentManager()

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

  (注,如果import android.support.v4.app.FragmentManager;那么使用的是:FragmentManager fragmentManager = getSupportFragmentManager();

  之后,用add()方法加上Fragment的对象:

ExampleFragment fragment = newExampleFragment();

fragmentTransaction.add(R.id.fragment_container, fragment);

fragmentTransaction.commit();

  其中第一个参数是这个fragment的容器,即父控件组。

  最后需要调用commit()方法使得FragmentTransaction实例的改变生效。

1.5 实例

  练习的例子:

  写一个类继承自Fragment类,并且写好其布局文件(本例中是两个TextView),在Fragment类的onCreateView()方法中加入该布局。

  之后用两种方法在Activity中加入这个fragment:

  第一种是在Activity的布局文件中加入标签;

  第二种是在Activity的代码中使用FragmentTransaction的add()方法加入fragment。

  贴出代码:

  自己定义的fragment类:

package com.example.learningfragment;


import android.os.Bundle;

import android.support.v4.app.Fragment;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class ExampleFragment extends Fragment

{

    //三个一般必须重载的方法

    @Override

    public void onCreate(Bundle savedInstanceState)

    {

        // TODO Auto-generated method stub

        super.onCreate(savedInstanceState);

        System.out.println("ExampleFragment--onCreate");

    }


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)

    {

       System.out.println("ExampleFragment--onCreateView");

       return inflater.inflate(R.layout.example_fragment_layout, container, false);

    }


    @Override

    public void onPause()

    {

        // TODO Auto-generated method stub

        super.onPause();

        System.out.println("ExampleFragment--onPause");

    }


    @Override

    public void onResume()

    {

        // TODO Auto-generated method stub

        super.onResume();

        System.out.println("ExampleFragment--onResume");

    }


    @Override

    public void onStop()

    {

        // TODO Auto-generated method stub

        super.onStop();

        System.out.println("ExampleFragment--onStop");

    }

}

  fragment的布局文件:

 主Activity:

package com.example.learningfragment;


import android.os.Bundle;

import android.support.v4.app.FragmentActivity;

import android.support.v4.app.FragmentManager;

import android.support.v4.app.FragmentTransaction;


public class LearnFragment extends FragmentActivity

{

    @Override

    public void onCreate(Bundle savedInstanceState)

    {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_learn_fragment);


        //在程序中加入Fragment

        FragmentManager fragmentManager = getSupportFragmentManager();

        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        ExampleFragment fragment = newExampleFragment();

        fragmentTransaction.add(R.id.linear, fragment);

        fragmentTransaction.commit();

    }

}

        Activity的布局文件:


  运行结果如下:

  可以看到第二种方式加入fragment的时候,指定了父容器(一个线性布局)的id,其中已经有一个Button 3,所以fragment加在其后。

2 Fragment和Activity比较

Fragment和Activity比较

http://www.cnblogs.com/mengdd/archive/2013/01/11/2856374.html

2.1 Fragment和Activity的交互

  一个Fragment的实例总是和包含它的Activity直接相关。

  fragment可以通过getActivity()方法来获得Activity的实例,然后就可以调用一些例如findViewById()之类的方法。

  如:

  ViewlistView =getActivity().findViewById(R.id.list);

        但是注意调用getActivity()时,fragment必须和activity关联(attached to an activity),否则将会返回一个null

  相似的,activity也可以获得一个fragment的引用,从而调用fragment中的方法。

  获得fragment的引用要用FragmentManager,之后可以调用findFragmentById()或者findFragmentByTag().

  比如:

  ExampleFragment fragment = (ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);

2.2 创建事件回调

        一些情况下,可能需要fragmentactivity共享事件,一个比较好的做法是在fragment里面定义一个回调接口,然后要求宿主activity实现它。

  当activity通过这个接口接收到一个回调,它可以同布局中的其他fragment分享这个信息。

  例如,一个新闻显示应用在一个activity中有两个fragment,一个fragment A显示文章题目的列表,一个fragment B显示文章。

  所以当一个文章被选择的时候,fragment A必须通知activity,然后activity通知fragment B,让它显示这篇文章。

  这个情况下,在fragment A中声明一个这样的接口OnArticleSelectedListener:

public static class FragmentA extends ListFragment {

   ...

    //Container Activity must implement this interface

    public interface OnArticleSelectedListener {

       public void onArticleSelected(Uri articleUri);

    }

   ...

}

  之后包含这个fragment的activity实现这个OnArticleSelectedListener接口,用覆写的onArticleSelected()方法将fragment A中发生的事通知fragment B。

  为了确保宿主activity实现这个接口,fragment A的onAttach()方法(这个方法在fragment被加入到activity中时由系统调用)中通过将传入的activity强制类型转换,实例化一个OnArticleSelectedListener对象:

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 newClassCastException(activity.toString() +" must implement OnArticleSelectedListener");

        }

    }

    ...

}

  如果activity没有实现这个接口,fragment将会抛出ClassCastException异常,如果成功了,mListener将会是activity实现OnArticleSelectedListener接口的一个引用,所以通过调用OnArticleSelectedListener接口的方法,fragment A可以和activity共享事件。

  比如,如果fragment A是ListFragment的子类,每一次用户点击一个列表项目,系统调用fragment中的onListItemClick()方法,在这个方法中可以调用onArticleSelected()方法与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);

    }

    ...

2.3 处理Fragment的生命周期

2.3.1 三种停留状态

  管理fragment的生命周期和管理activity的生命周期类似,和activity一样,fragment可以在三种状态下停留:

 Resumed

  fragment在running的activity中可见。

 Paused

  另一个activity在前景运行,并且享有焦点,但是这个fragment所在的activity仍然可见(前景activity部分遮挡或者是半透明的)。

 Stopped

  fragment不可见。可能是因为宿主activity处于stopped状态,或者fragment被remove掉,然后加在了back stack中。

  一个处于stopped状态的activity还是存活状态的,所有的状态和成员信息会被系统保持。但是,它不再被用户可见,并且如果宿主activity被kill掉,它也会被kill掉。

2.3.2 数据存储和恢复

  和Activity类似,可以用Bundle类对象保存fragment的状态,当activity的进程被kill之后,需要重建activity时,可以用于恢复fragment的状态。

  存储时利用onSaveInstanceState()回调函数,恢复时是在onCreate(),onCreateView(),或者onActivityCreated()里。

2.3.3 BackStack

  activity和fragment生命周期最重要的不同之处是它们如何存储在各自的back stack中。

  Activity停止时,是存在一个由系统维护的back stack中,但是当fragment停止(被remove)时,需要程序员显示地调用addToBackStack(),并且fragment是存在一个由宿主activity掌管的back

stack中。

2.3.4 Fragment和Activity的生命周期

  宿主activity的声明周期直接影响到fragment的生命周期,比如activity生命周期的回调函数调用时,所有在其中的fragment的相同的回调函数会同时被调用。

  Fragment还有一些额外的生命周期回调函数:

    onAttach()

  当fragment和activity被关联时调用。

     onCreateView()

  当创建fragment的UI被初始化时调用。

     onActivityCreated()

  当activity的onCreate()方法返回时调用。

    onDestroyView()

  当fragment的UI被移除的时候调用。

    onDetach()

  当fragment和activity去关联时调用。

如图:

  从这个图上可以看出activity的状态决定了fragment可能接收到的回调函数

  比如说,当activity接收到它的onCreate()回调函数,那么这个activity中的fragment最多接收到了onActivityCreated()。

  当activity处于Resumed状态时,可以自由地添加和移除fragment,也即是说,只有activity在Resumed状态时,fragment的状态可以独立改变。

  但是,当activity离开Resumed状态,fragment的生命周期被activity控制。

2.4 参考资料

  API Guides: Fragments

  http://developer.android.com/guide/components/fragments.html


3 管理Fragments

管理Fragments

http://www.cnblogs.com/mengdd/archive/2013/01/09/2853254.html


3.1 FragmentManager

        为了管理Activity中的fragments,需要使用FragmentManager.

       为了得到它,需要调用Activity中的getFragmentManager()方法。

       因为FragmentManagerAPI是在Android 3.0,也即API level 11开始引入的,所以对于之前的版本,需要使用support library中的FragmentActivity,并且使用getSupportFragmentManager()方法。

        FragmentManager可以做的工作有:

  得到Activity中存在的fragment:

  使用findFragmentById()或findFragmentByTag()方法。

  将fragment弹出back stack:

  popBackStack():将back stack中最后一次的fragment转换弹出。如果没有可以出栈的东西,返回false。

  这个函数是异步的:它将弹出栈的请求加入队列,但是这个动作直到应用回到事件循环才会执行。

  为back stack加上监听器:

  addOnBackStackChangedListener()

3.2 Performing Fragment Transactions

3.2.1 Fragment跳转简介

  使用Fragment时,可以通过用户交互来执行一些动作,比如增加、移除、替换等。

  所有这些改变构成一个集合,这个集合被叫做一个transaction。

  可以调用FragmentTransaction中的方法来处理这个transaction,并且可以将transaction存进由activity管理的back stack中,这样用户就可以进行fragment变化的回退操作。

  可以这样得到FragmentTransaction类的实例:

FragmentManager fragmentManager = getFragmentManager();

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();


每个transaction是一组同时执行的变化的集合。

  用add(), remove(), replace()方法,把所有需要的变化加进去,然后调用commit()方法,将这些变化应用。

  在commit()方法之前,你可以调用addToBackStack(),把这个transaction加入back stack中去,这个back stack是由activity管理的,当用户按返回键时,就会回到上一个fragment的状态。

  比如下面的代码就是用一个新的fragment取代之前的fragment,并且将前次的状态存储在back stack中。

// Create new fragment and transaction

Fragment newFragment = newExampleFragment();

FragmentTransaction transaction = getFragmentManager().beginTransaction();


// Replace whatever is in thefragment_container view with this fragment,

// and add the transaction to the back stack

transaction.replace(R.id.fragment_container,newFragment);

transaction.addToBackStack(null);


// Commit the transaction

transaction.commit();

  在这个例子中,newFragment将取代在R.id.fragment_container容器中的fragment,如果没有,将直接添加新的fragment。

  通过调用addToBackStack(),commit()的一系列转换作为一个transaction被存储在back stack中,用户按Back键可以返回上一个转换前的状态。

  当你移除一个fragment的时候,如果commit()之前没有调用addToBackStack(),那个fragment将会是destroyed;如果调用了addToBackStack(),这个fragment会是stopped,可以通过返回键来恢复。

3.2.2 关于commit()方法

  调用commit()方法并不能立即执行transaction中包含的改变动作,commit()方法把transaction加入activity的UI线程队列中。

  但是,如果觉得有必要的话,可以调用executePendingTransactions()方法来立即执行commit()提供的transaction。

  这样做通常是没有必要的,除非这个transaction被其他线程依赖。

  注意:你只能在activity存储它的状态(当用户要离开activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常。

  这是因为当activity再次被恢复时commit之后的状态将丢失。如果丢失也没关系,那么使用commitAllowingStateLoss()方法。

3.3 实例程序

  写了个小程序实践了一下fragment的管理,程序不是很完善,就是试试基本用法,先按第一个按钮添加一个fragment,第二个按钮将其替换,第三个按钮将第二个按钮添加的fragment删除。

  相关代码:

    第一个fragment

ExampleFragment.java

  它的布局:

example_fragment_layout.xml


 第二个fragment:

NewFragment.java

new_fragment_layout.xml

    Activity

LearnFragment.java

activity_learn_fragment.xml

 资源:

strings.xml

        程序运行截图:

3.4 参考资料

  API Guides:Fragments

  http://developer.android.com/guide/components/fragments.html

  FragmentManager类文档:

  http://developer.android.com/reference/android/app/FragmentManager.html

  FragmentTransaction类文档

  http://developer.android.com/reference/android/app/FragmentTransaction.html


4 开发技巧

4.1 常用技巧

4.1.1 FragmentActivity获取当前显示的Fragment

public Fragment getVisibleFragment(){

    FragmentManager fragmentManager = MainActivity.this.getSupportFragmentManager();

    List fragments = fragmentManager.getFragments();

    for(Fragment fragment : fragments){

        if(fragment !=null && fragment.isVisible())

            return fragment;

    }

    return null;

}


4.1.2 Fragment加载到Activity当中

Android系列之Fragment(一)----Fragment加载到Activity当中

http://www.2cto.com/kf/201409/335073.html


5 参考链接

Android Fragment基本介绍

http://www.cnblogs.com/mengdd/archive/2013/01/08/2851368.html


Fragment类文档:

http://developer.android.com/reference/android/app/Fragment.html


Training:Building a Dynamic UI with Fragments

http://developer.android.com/training/basics/fragments/index.html


Fragments Develop Guide:

http://developer.android.com/guide/components/fragments.html


[Android界面] FragmentActivity获取当前显示的Fragment

http://www.eoeandroid.com/thread-272595-2-1.html


Android系列之Fragment(一)----Fragment加载到Activity当中

http://www.2cto.com/kf/201409/335073.html

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

推荐阅读更多精彩内容

  • Fragment概述 Fragment是Activity中用户界面的一个行为或者说是一部分。主要是支持大屏幕上动态...
    wangling90阅读 11,514评论 5 76
  • 第一件事,找到目标公司投简历 第二件事,超级个体跟上进度的留言 第三件事,开始准备辞职信 小确幸,粉粉的大衣保暖又...
    咸菜粉皮Lizzy阅读 175评论 0 0
  • 100天转瞬即逝,回顾自己的100天目标,执行的不是太好。 目标1、完成3本书的阅读 这个目标没有完成,现在为止仅...
    简单湮阅读 380评论 0 0
  • 据全民星探app独家报道的张铁林私生女的新闻引起广泛关注,私生女张某和母亲侯女士即将起诉,已准备与张铁林对簿公堂,...
    蓝传说阅读 327评论 0 0