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
  • 相关阅读:
    Orient DB 0.9.6 Beta 发布下载
    Java 近些年一直群龙无首 Google 为 Java 前途担忧
    VC和LUA混合开发之VC程序调用Lua脚本函数
    NetBeans 时事通讯(刊号 # 99 Apr 16, 2010)
    Cassandra 0.6 发布下载
    JDK 6 Update 20 发布下载JDK 6最新版本下载
    Java 近些年一直群龙无首 Google 为 Java 前途担忧
    Simple way to export SQL Server data to Text Files
    GFORTRAN
    The Stanford NLP (Natural Language Processing) Group
  • 原文地址:https://www.cnblogs.com/cubika/p/3177782.html
Copyright © 2011-2022 走看看