• Android -- Fragment


    生命周期                                                                              

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

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

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

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

    1

    使用                                                                                   

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

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

    onCreate()

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

    onCreateView()

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

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

    onPause()

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

    • 实现Fragment的UI

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

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

    public 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);
        }
    }

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

    inflate()方法的三个参数:

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

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

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

    把Fragment加入Activity                                                             

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

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

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

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

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

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <fragment android:name="com.example.news.ArticleListFragment"
                android:id="@+id/list"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
        <fragment android:name="com.example.news.ArticleReaderFragment"
                android:id="@+id/viewer"
                android:layout_weight="2"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
    </LinearLayout>

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

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

    有三种方法为Fragment提供ID:

      android:id属性:唯一的id

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

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

    • 加载方式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 = new ExampleFragment();
    
    fragmentTransaction.add(R.id.fragment_container, fragment);
    
    fragmentTransaction.commit();

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

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

    FragmentManager                                                                    

    为了管理Activity中的fragments,需要使用FragmentManager. 为了得到它,需要调用Activity中的getFragmentManager()方法。

    因为FragmentManager的API是在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()

    Transactions                                                                          

    使用Fragment时,可以通过用户交互来执行一些动作,比如增加、移除、替换等。所有这些改变构成一个集合,这个集合被叫做一个transaction。

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

    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 = new ExampleFragment();
    FragmentTransaction transaction = getFragmentManager().beginTransaction();
    
    // Replace whatever is in the fragment_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,可以通过返回键来恢复。

    commit                                                                                

    调用commit()方法并不能立即执行transaction中包含的改变动作,commit()方法把transaction加入activity的UI线程队列中。但是,如果觉得有必要的话,可以调用executePendingTransactions()方法来立即执行commit()提供的transaction。这样做通常是没有必要的,除非这个transaction被其他线程依赖。

    注意:你只能在activity存储它的状态(当用户要离开activity时)之前调用commit(),如果在存储状态之后调用commit(),将会抛出一个异常。这是因为当activity再次被恢复时commit之后的状态将丢失。如果丢失也没关系,那么使用commitAllowingStateLoss()方法。

    Fragment和Activity的交互                                                          

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

    View listView = 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);

    创建事件回调                                                                         

    一些情况下,可能需要fragment和activity共享事件,一个比较好的做法是在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 new ClassCastException(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);
        }
        ...
    }

    Back Stack                                                                            

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

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

    我是天王盖地虎的分割线                                                             

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

  • 相关阅读:
    Linux应用程序的地址布局
    C/C++中的内存四区!
    编程语言的选择重不重要?对普通人而言,反其道而行更可靠!
    【C语言程序设计】C语言求最大公约数(详解版)!
    【C语言程序设计】C语言求最小公倍数(详解版)!
    【C语言程序设计】C语言求定积分!腻不腻害!
    你是因为什么而当程序员啊!网友们众说纷纭,从装b到被逼,理由层出不穷!
    华为程序员爆料:主动离职,公司竟也给n+1,到手15万,华为真良心!
    【C语言程序设计】C语言整数逆序输出程序!
    copy 和 mutablecopy
  • 原文地址:https://www.cnblogs.com/yydcdut/p/3921297.html
Copyright © 2020-2023  润新知