美文网首页Android开发我爱编程Android学习
【Android开发基础系列】Fragment专题

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

作者: Kevin_Junbaozi | 来源:发表于2018-04-13 23:13 被阅读394次

    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

    相关文章

      网友评论

        本文标题:【Android开发基础系列】Fragment专题

        本文链接:https://www.haomeiwen.com/subject/bznhkftx.html