zoukankan      html  css  js  c++  java
  • Android的Fragment介绍

    前言

        fragment是从android3.0开始提出来的,用来支持大屏幕设备的ui设计。通过将activity划分为多个fragment,不仅提高了设计的灵活性,而且可以在程序运行时改变它们的特征,比如动态的修改,替换已有的fragment等等。

        fragment的角色是相当于activity中ui的一个子集或者说是activity中的一个模块,可以通过组合多个fragment来构造一个多个面板的界面。由于fragment嵌入在activity中,因此它的生命周期受到activity的影响,一旦activity停掉,它里面所有的fragment也都会被摧毁。

        可以在activity的布局文件中通过声明<fragment>标签来填充它的布局,同时也可以通过代码将它加到已经存在的ViewGroup中,因为fragment布局实际上就是ViewGroup的子树。

    新建一个Fragment

        首先需要了解的是fragment的生命周期,如下图所示:

    fragment_lifecycle

    可以看出调用的先后顺序是:

    • onCreate  这里可以用来初始化一些当fragment停掉时候你仍想保存的组件
    • onCreateView  这里可以用来绘制fragment的用户界面,因此必须返回一个view
    • onPause  当fragment被移走或者替换时调用,可以在这里commit changes

    一般可以继承fragment类来创建自己的fragment,同时亦有几个有用的fragment基类供我们继承:

    1. DialogFragment  也就是浮动在Activity上面的fragment,这种对话框可以供用户返回,便于管理,是google推荐的做法。
    2. ListFragment  就是包含ListView的Fragment,用来显示列表等。
    3. PreferenceFragment  可以用来显示用户选项的设置。

    一般来说,要创建Fragment,第一步就是创建一个Fragment的子类,并在onCrateView方法中填充布局:

    public static class ExampleFragment extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            //resourceID,ViewGroup, whether the inflated layout should be 
            //attached to the ViewGroup during inflation
            return inflater.inflate(R.layout.example_fragment, container, false);
        }
    }

    第二步就是将fragment添加到activity中,可以在activity的布局文件中声明,如:

    <?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>

    这样一来,当创建activity的布局时,就会初始化每一个fragment,并且调用它们的onCreateView方法,获取fragment的布局,然后插入到ViewGroup下面。

    除了使用布局文件之外,还可以用程序进行添加:

    FragmentManager fragmentManager = getFragmentManager()
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();

    前两行用来获取碎片事务FragmentTransaction,后三行用来添加一个fragment,add方法的第一个参数是要放置的ViewGroup,第二个参数是要添加的fragment。

    Fragment的管理

    通过fragmentManager可以做一下事情:

    • 获取fragment,通过findFragmentById或者findFragmentByTag
    • 将Fragment从栈中移除,通过popBackStack方法
    • 为back stack注册监听器

    相信这里的细节还有很多,暂时先不介绍。

    Fragment的事务

        fragment的一大亮点就是可以动态增加,删除,替换fragment,而这是通过fragmentTransaction来实现的。一旦获取了事务的实例,就可以通过调用add,remove,replace方法完成上述功能,接下来通过commit方法提交事务就可以了。

    // 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();

        除此之外还可以调用addToBackStack方法将fragment添加到back stack中,用户按下返回键时,就会跳到back stack顶部的fragment。调不调用它的区别在于,如果没有它,fragment被remove后就彻底销毁了,再也不能恢复,如果调用了它,fragment就不会销毁而是stop,这样就可以恢复,因为fragment的状态都被保存在了back stack上面。

        commit方法的作用是调度到activity的主线程中执行,并且事务的提交必须要在onSaveInstanceState之前。

    和Activity的通信

        fragment也是可以和它所在的activity通信的,比如可以通过getActivity()方法得到所在的activity,相应的,activity也可以调用FragmentManager的findFragmentById方法获取某一个fragment的引用。

        有时候需要在fragment和activity之间共享事件,如activity有一个fragment,是用来显示文章的列表的,当用户点击了某一项,activity需要检测到这个事件,同时更改另一个fragment从而显示出相应的文章内容。这时可以这么做,在fragment中定义事件监听的接口,然后让activity实现这个接口。然后,这个fragment就可以调用接口中的某些方法来通知activity了。fragment中可以在onAttach里面将activity强制转化为接口的实现类,从而确保能成功调用接口中的方法。

    public static class FragmentA extends ListFragment {
    
        OnArticleSelectedListener mListener;
        ...
        // Container Activity must implement this interface
        public interface OnArticleSelectedListener {
            public void onArticleSelected(Uri articleUri);
        }
        ...
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            try {
                mListener = (OnArticleSelectedListener) activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
            }
        }
        ...
        @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);
        }
        ...
    }

    例子

       该例子来源于api demo,用到了很典型的两个fragment,一个是TitlesFragment,用来显示一个列表,另一个是DetailsFragment,用来显示选中item的详细信息。此外,该程序还考虑到了屏幕的横竖屏问题,如果是竖屏的话,没有足够的地方显示第二个Fragment,就单独启动一个Activity。源码如下:

    /**
     * Demonstration of using fragments to implement different activity layouts.
     * This sample provides a different layout (and activity flow) when run in
     * landscape.
     */
    public class FragmentLayout extends Activity {
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            
            setContentView(R.layout.fragment_layout);
        }
    
    
        /**
         * This is a secondary activity, to show what the user has selected
         * when the screen is not large enough to show it all in one activity.
         */
    
        public static class DetailsActivity extends Activity {
    
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
    
                if (getResources().getConfiguration().orientation
                        == Configuration.ORIENTATION_LANDSCAPE) {
                    // If the screen is now in landscape mode, we can show the
                    // dialog in-line with the list so we don't need this activity.
                    finish();
                    return;
                }
    
                if (savedInstanceState == null) {
                    // During initial setup, plug in the details fragment.
                    DetailsFragment details = new DetailsFragment();
                    details.setArguments(getIntent().getExtras());
                    getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
                }
            }
        }
    
    
        /**
         * This is the "top-level" fragment, showing a list of items that the
         * user can pick.  Upon picking an item, it takes care of displaying the
         * data to the user as appropriate based on the currrent UI layout.
         */
    
        public static class TitlesFragment extends ListFragment {
            boolean mDualPane;
            int mCurCheckPosition = 0;
    
            @Override
            public void onActivityCreated(Bundle savedInstanceState) {
                super.onActivityCreated(savedInstanceState);
    
                // Populate list with our static array of titles.
                setListAdapter(new ArrayAdapter<String>(getActivity(),
                        android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));
    
                // Check to see if we have a frame in which to embed the details
                // fragment directly in the containing UI.
                View detailsFrame = getActivity().findViewById(R.id.details);
                mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
    
                if (savedInstanceState != null) {
                    // Restore last state for checked position.
                    mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
                }
    
                if (mDualPane) {
                    // In dual-pane mode, the list view highlights the selected item.
                    getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
                    // Make sure our UI is in the correct state.
                    showDetails(mCurCheckPosition);
                }
            }
    
            @Override
            public void onSaveInstanceState(Bundle outState) {
                super.onSaveInstanceState(outState);
                outState.putInt("curChoice", mCurCheckPosition);
            }
    
            @Override
            public void onListItemClick(ListView l, View v, int position, long id) {
                showDetails(position);
            }
    
            /**
             * Helper function to show the details of a selected item, either by
             * displaying a fragment in-place in the current UI, or starting a
             * whole new activity in which it is displayed.
             */
            void showDetails(int index) {
                mCurCheckPosition = index;
    
                if (mDualPane) {
                    // We can display everything in-place with fragments, so update
                    // the list to highlight the selected item and show the data.
                    getListView().setItemChecked(index, true);
    
                    // Check what fragment is currently shown, replace if needed.
                    DetailsFragment details = (DetailsFragment)
                            getFragmentManager().findFragmentById(R.id.details);
                    if (details == null || details.getShownIndex() != index) {
                        // Make new fragment to show this selection.
                        details = DetailsFragment.newInstance(index);
    
                        // Execute a transaction, replacing any existing fragment
                        // with this one inside the frame.
                        FragmentTransaction ft = getFragmentManager().beginTransaction();
                        if (index == 0) {
                            ft.replace(R.id.details, details);
                        } else {
                            ft.replace(R.id.a_item, details);
                        }
                        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                        ft.commit();
                    }
    
                } else {
                    // Otherwise we need to launch a new activity to display
                    // the dialog fragment with selected text.
                    Intent intent = new Intent();
                    intent.setClass(getActivity(), DetailsActivity.class);
                    intent.putExtra("index", index);
                    startActivity(intent);
                }
            }
        }
    
    
        /**
         * This is the secondary fragment, displaying the details of a particular
         * item.
         */
    
        public static class DetailsFragment extends Fragment {
            /**
             * Create a new instance of DetailsFragment, initialized to
             * show the text at 'index'.
             */
            public static DetailsFragment newInstance(int index) {
                DetailsFragment f = new DetailsFragment();
    
                // Supply index input as an argument.
                Bundle args = new Bundle();
                args.putInt("index", index);
                f.setArguments(args);
    
                return f;
            }
    
            public int getShownIndex() {
                return getArguments().getInt("index", 0);
            }
    
            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                    Bundle savedInstanceState) {
                if (container == null) {
                    // We have different layouts, and in one of them this
                    // fragment's containing frame doesn't exist.  The fragment
                    // may still be created from its saved state, but there is
                    // no reason to try to create its view hierarchy because it
                    // won't be displayed.  Note this is not needed -- we could
                    // just run the code below, where we would create and return
                    // the view hierarchy; it would just never be used.
                    return null;
                }
    
                ScrollView scroller = new ScrollView(getActivity());
                TextView text = new TextView(getActivity());
                int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                        4, getActivity().getResources().getDisplayMetrics());
                text.setPadding(padding, padding, padding, padding);
                scroller.addView(text);
                text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
                return scroller;
            }
        }
    
    }
    View Code
  • 相关阅读:
    OPPO R9sPlus MIFlash线刷TWRP Recovery ROOT详细教程
    OPPO R11 R11plus系列 解锁BootLoader ROOT Xposed 你的手机你做主
    努比亚(nubia) M2青春版 NX573J 解锁BootLoader 并进入临时recovery ROOT
    华为 荣耀 等手机解锁BootLoader
    青橙 M4 解锁BootLoader 并刷入recovery ROOT
    程序员修炼之道阅读笔03
    冲刺8
    典型用户模板分析
    学习进度八
    冲刺7
  • 原文地址:https://www.cnblogs.com/cubika/p/3177782.html
Copyright © 2011-2022 走看看