zoukankan      html  css  js  c++  java
  • Fragment 的产生与介绍

    源代码参考:360云盘中---自己的学习资料---Android总结过的项目---FragmentDemo.rar
    
    本篇博客力求为大家说明 Fragment 如何产生,什么是 Fragment,Fragment 生命周期,如何静态和动态的使用Fragment,Fragment 回退栈,Fragment 事务;以及 Fragment的一些特殊用途,例如:没有布局的Fragment有何用处?Fragment 如何与 Activity 交互?Fragment 如何创建对话框? Fragment 如何与 ActionBar 集成等等。
    
    一、Fragment 的产生与介绍
    
    Android 运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套 App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个 App 可以同时适应手机和平板么,当然了,必须有啊。Fragment 的出现就是为了解决这样的问题。你可以把 Fragment 当成 Activity 的一个界面的一个组成部分,甚至 Activity 的界面可以完全有不同的 Fragment 组成,更帅气的是 Fragment 拥有自己的生命周期和接收、处理用户的事件,这样就不必在 Activity 写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个 Fragment。
    
    --------------------------------------------------------------------------------------------
    
    二、Fragment 的生命周期
    
    
    
    
    
    
    
    可以看到 Fragment 比 Activity 多了几个额外的生命周期回调方法:
    
    1.onAttach(Activity)
    当 Fragment 与 Activity 发生关联时调用。
    
    2.onCreateView(LayoutInflater, ViewGroup,Bundle)
    创建该 Fragment 的视图
    
    3.onActivityCreated(Bundle)
    当 Activity 的 onCreate 方法返回时调用
    
    4.onDestoryView()
    与 onCreateView 想对应,当该 Fragment 的视图被移除时调用
    
    5.onDetach()
    与 onAttach 相对应,当 Fragment 与 Activity 关联被取消时调用
    
    注意:除了 onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
    
    1. 当一个 Fragment 被创建的时候,它会经历以下状态.
    onAttach()
    onCreate()
    onCreateView()
    onActivityCreated()
    
    2. 当这个 Fragment 对用户可见的时候,它会经历以下状态。
    onStart()
    onResume()
    
    3. 当这个 Fragment 进入“后台模式”的时候,它会经历以下状态。
    onPause()
    onStop()
    
    4. 当这个 Fragment 被销毁了(或者持有它的 Activity 被销毁了),它会经历以下状态。
    onPause()
    onStop()
    onDestroyView()
    onDestroy() // 本来漏掉类这个回调,感谢xiangxue336提出。
    onDetach()
    
    5. 就像 Activitie 一样,在以下的状态中,可以使用 Bundle 对象保存一个 Fragment 的对象。
    onCreate()
    onCreateView()
    onActivityCreated()
    
    6. Fragments 的大部分状态都和 Activitie 很相似,但 Fragment 有一些新的状态。
    
    onAttached() —— 当fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。
    onCreateView() —— 当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面)。
    onActivityCreated() —— 当activity的onCreated()方法返回后调用此方法
    onDestroyView() —— 当fragment中的视图被移除的时候,调用这个方法。
    onDetach() —— 当fragment和activity分离的时候,调用这个方法。
    
    一旦 Activity 进入 resumed 状态(也就是 running 状态),你就可以自由地添加和删除 Fragment 了。因此,只有当 Activity 在 resumed 状态时,Fragment 的生命周期才能独立的运转,其它时候是依赖于 Activity 的生命周期变化的。
    
    --------------------------------------------------------------------------------------------
    三、静态使用 Fragment
    
    
    这是使用 Fragment 最简单的一种方式,把 Fragment 当成普通的控件,直接写在 Activity 的布局文件中。步骤:
    
    1. 继承 Fragment,重写 onCreateView 决定 Fragemnt 的布局
    2. 在 Activity 中声明此 Fragment,就当和普通的 View 一样
    
    下面展示一个例子(我使用 2 个 Fragment 作为 Activity 的布局,一个 Fragment 用于标题布局,一个 Fragment 用于内容布局):
    
    <!-- 静态 Fragment 标题布局 -->
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="@android:color/holo_green_light" >
    
        <ImageButton
            android:id="@+id/ibTitleLeftBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dip"
            android:background="@drawable/user" />
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_alignParentTop="true"
            android:gravity="center"
            android:text="我不是微信"
            android:textColor="#fff"
            android:textSize="20sp"
            android:textStyle="bold" />
    
    </RelativeLayout>
    
    <!-- 静态 Fragment 内容布局 -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center"
            android:text="使用 Fragment 做主面板"
            android:textSize="20sp"
            android:textStyle="bold" />
    
    </LinearLayout>
    
    /**
     *Copyright(C) 2014 Beijing Oradt Ltd.
                    For Digital Visiting Card 1.0
     *File Name:TitleFragment.java
     *Description:标题 Fragment
     *Author: Xiao JinLai
     *Date:2014-12-12下午4:07:52
     *History:
        Date:      Author:      Depiction:
     */
    public class TitleFragment extends Fragment implements OnClickListener {
    
        private ImageButton mLeftMenu;
        
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            
            View tView=inflater.inflate(R.layout.fragment_title, container ,false);
            mLeftMenu=(ImageButton) tView.findViewById(R.id.ibTitleLeftBtn);
            mLeftMenu.setOnClickListener(this);
            
            return tView;
        }
    
        @Override
        public void onClick(View v) {
    
            switch (v.getId()) {
            case R.id.ibTitleLeftBtn:
                
                break;
            }
        }
    }
    
    /**
     *Copyright(C) 2014 Beijing Oradt Ltd.
                    For Digital Visiting Card 1.0
     *File Name:ContentFragment.java
     *Description:内容 Fragment
     *Author: Xiao JinLai
     *Date:2014-12-12下午4:24:56
     *History:
        Date:      Author:      Depiction:
     */
    public class ContentFragment extends Fragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            
            return inflater.inflate(R.layout.fragment_content, container, false);
        }
    }
    
    <!-- 静态 Fragment 布局文件 -->
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <fragment
            android:id="@+id/ftTitle"
            android:name="com.xjl.fragmentdemo.TitleFragment"
            android:layout_width="fill_parent"
            android:layout_height="45dp" />
    
        <fragment
            android:id="@+id/ftContent"
            android:name="com.xjl.fragmentdemo.ContentFragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_below="@id/ftTitle" />
    
    </RelativeLayout>
    
    /**
     * @ClassName: StaticFragementActivity
     * @author Xiao JinLai
     * @Date 2014-12-13 下午8:38:52
     * @Description:静态 Fragement
     */
    public class StaticFragementActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            
            setContentView(R.layout.activity_static_fragment);
        }
    }
    
    是不是把 Fragment 当成普通的 View 一样声明在 Activity 的布局文件中,然后所有控件的事件处理等代码都由各自的Fragment去处理,瞬间觉得Activity好干净有木有~~代码的可读性、复用性以及可维护性是不是瞬间提升了。
    下面是效果图
    
    --------------------------------------------------------------------------------------------
    四、动态使用 Fragment
    
    上面已经演示了,最简单的使用 Fragment 的方式,下面介绍如何动态的添加、更新、以及删除 Fragment
    
    为了动态使用 Fragment,我们修改一下 Actvity 的布局文件,中间使用一个 FrameLayout,下面添加四个按钮,嘿嘿,不是微信的按钮!
    
    <!-- 动态 Fragment 布局文件 -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <FrameLayout
            android:id="@+id/flContent"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" >
        </FrameLayout>
    
       <include layout="@layout/bottom_bar"/>
    
    </LinearLayout>
    
    <!-- 底部四个按钮布局 -->
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/rlTabBottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@drawable/bottom_bar" >
    
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="55dp" >
    
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:descendantFocusability="beforeDescendants"
                android:gravity="center"
                android:orientation="vertical" >
    
                <ImageButton
                    android:id="@+id/ibTabBottomWeixin"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="#0000"
                    android:clickable="false"
                    android:src="@drawable/tab_weixin_pressed" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="微信" />
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:descendantFocusability="beforeDescendants"
                android:gravity="center"
                android:orientation="vertical" >
    
                <ImageButton
                    android:id="@+id/ibTabBottomFriend"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="#0000"
                    android:clickable="false"
                    android:src="@drawable/tab_find_frd_normal" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="朋友" />
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:descendantFocusability="beforeDescendants"
                android:gravity="center"
                android:orientation="vertical" >
    
                <ImageButton
                    android:id="@+id/ibTabBottomAddress"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="#0000"
                    android:clickable="false"
                    android:src="@drawable/tab_address_normal" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="通讯录" />
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:descendantFocusability="beforeDescendants"
                android:gravity="center"
                android:orientation="vertical" >
    
                <ImageButton
                    android:id="@+id/ibTabBottomSetting"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:background="#0000"
                    android:clickable="false"
                    android:src="@drawable/tab_settings_normal" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="设置" />
            </LinearLayout>
        </LinearLayout>
    
    </RelativeLayout>
    
    /**
     * @ClassName: StaticFragementActivity
     * @author Xiao JinLai
     * @Date 2014-12-13 下午8:38:52
     * @Description:动态 Fragement 主 Activity
     */
    public class TrendsFragementActivity extends Activity implements
            OnClickListener {
    
        private WeiXinFragement mWeiXinFragement;
        private FriendFragement mFriendFragement;
        private AddressFragement mAddressFragement;
        private SettingsFragement mSettingsFragement;
    
        /**
         * 底部四个按钮
         */
        private ImageButton mTabBtnWeixin;
        private ImageButton mTabBtnFrd;
        private ImageButton mTabBtnAddress;
        private ImageButton mTabBtnSettings;
    
        /**
         * 用于对 Fragment 进行管理
         */
        private FragmentManager mFragmentManager;
    
        @SuppressLint("NewApi")
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_trends_fragment);
    
            initViews();
    
            mFragmentManager = getFragmentManager();
    
            setTabSelection(0);
        }
    
        private void initViews() {
    
            mTabBtnWeixin = (ImageButton) findViewById(R.id.ibTabBottomWeixin);
            mTabBtnFrd = (ImageButton) findViewById(R.id.ibTabBottomFriend);
            mTabBtnAddress = (ImageButton) findViewById(R.id.ibTabBottomAddress);
            mTabBtnSettings = (ImageButton) findViewById(R.id.ibTabBottomSetting);
    
            mTabBtnWeixin.setOnClickListener(this);
            mTabBtnFrd.setOnClickListener(this);
            mTabBtnAddress.setOnClickListener(this);
            mTabBtnSettings.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
    
            switch (v.getId()) {
    
            case R.id.ibTabBottomWeixin:
    
                setTabSelection(0);
                break;
            case R.id.ibTabBottomFriend:
    
                setTabSelection(1);
                break;
            case R.id.ibTabBottomAddress:
    
                setTabSelection(2);
                break;
            case R.id.ibTabBottomSetting:
    
                setTabSelection(3);
                break;
            }
        }
    
        /**
         * 根据传入的index参数来设置选中的tab页。
         * 
         */
        @SuppressLint("NewApi")
        private void setTabSelection(int index) {
    
            // 重置按钮
            resetTabBtn();
    
            // 开启一个Fragment事务
            FragmentTransaction tFTransaction = mFragmentManager.beginTransaction();
    
            // 先隐藏掉所有的 Fragment,以防止有多个 Fragment 显示在界面上的情况
            hideFragments(tFTransaction);
    
            switch (index) {
            case 0:
    
                // 当点击了消息tab时,改变控件的图片和文字颜色
                mTabBtnWeixin.setImageResource(R.drawable.tab_weixin_pressed);
    
                if (mWeiXinFragement == null) {
    
                    // 如果MessageFragment为空,则创建一个并添加到界面上
                    mWeiXinFragement = new WeiXinFragement();
    
                    tFTransaction.add(R.id.flContent, mWeiXinFragement);
    
                } else {
    
                    // 如果MessageFragment不为空,则直接将它显示出来
                    tFTransaction.show(mWeiXinFragement);
                }
                break;
            case 1:
    
                // 当点击了消息tab时,改变控件的图片和文字颜色
                mTabBtnFrd.setImageResource(R.drawable.tab_find_frd_pressed);
    
                if (mFriendFragement == null) {
    
                    // 如果MessageFragment为空,则创建一个并添加到界面上
                    mFriendFragement = new FriendFragement();
                    tFTransaction.add(R.id.flContent, mFriendFragement);
    
                } else {
    
                    // 如果MessageFragment不为空,则直接将它显示出来
                    tFTransaction.show(mFriendFragement);
                }
                break;
    
            case 2:
                // 当点击了动态tab时,改变控件的图片和文字颜色
                mTabBtnAddress.setImageResource(R.drawable.tab_address_pressed);
    
                if (mAddressFragement == null) {
    
                    // 如果NewsFragment为空,则创建一个并添加到界面上
                    mAddressFragement = new AddressFragement();
                    tFTransaction.add(R.id.flContent, mAddressFragement);
                } else {
    
                    // 如果NewsFragment不为空,则直接将它显示出来
                    tFTransaction.show(mAddressFragement);
                }
                break;
    
            case 3:
    
                // 当点击了设置tab时,改变控件的图片和文字颜色
                mTabBtnSettings.setImageResource(R.drawable.tab_settings_pressed);
    
                if (mSettingsFragement == null) {
    
                    // 如果SettingFragment为空,则创建一个并添加到界面上
                    mSettingsFragement = new SettingsFragement();
                    tFTransaction.add(R.id.flContent, mSettingsFragement);
                } else {
    
                    // 如果 SettingFragment 不为空,则直接将它显示出来
                    tFTransaction.show(mSettingsFragement);
                }
                break;
            }
    
            tFTransaction.commit();
        }
    
        /**
         * 清除掉所有的选中状态。
         */
        private void resetTabBtn() {
    
            mTabBtnWeixin.setImageResource(R.drawable.tab_weixin_normal);
            mTabBtnFrd.setImageResource(R.drawable.tab_find_frd_normal);
            mTabBtnAddress.setImageResource(R.drawable.tab_address_normal);
            mTabBtnSettings.setImageResource(R.drawable.tab_settings_normal);
        }
    
        /**
         * 将所有的Fragment都置为隐藏状态。
         * 
         * @param transaction
         *            用于对Fragment执行操作的事务
         */
        @SuppressLint("NewApi")
        private void hideFragments(FragmentTransaction transaction) {
    
            if (mWeiXinFragement != null) {
    
                transaction.hide(mWeiXinFragement);
            }
            if (mFriendFragement != null) {
    
                transaction.hide(mFriendFragement);
            }
            if (mAddressFragement != null) {
    
                transaction.hide(mAddressFragement);
            }
            if (mSettingsFragement != null) {
    
                transaction.hide(mSettingsFragement);
            }
    
        }
    }
    
    可以看到我们使用 FragmentManager 对 Fragment 进行了动态的加载,这里使用的是 replace 方法。下一节我会详细介绍 FragmentManager 的常用 API。
    
    注:如果使用 Android3.0 以下的版本,需要引入v4的包,然后 Activity 继承 FragmentActivity,然后通过 getSupportFragmentManager获得 FragmentManager。不过还是建议版Menifest文件的uses-sdk的 minSdkVersion 和 targetSdkVersion 都改为 11 以上,这样就不必引入 v4 包了。
    
    代码中间还有 4 个 Fragment 的子类。这四个 Fragment 类似,先贴两个看看。
    
    /**
     * @ClassName: WeiXinFragement
     * @author Xiao JinLai
     * @Date 2014-12-21 下午8:09:01
     * @Description:微信 Fragement
     */
    public class WeiXinFragement extends Fragment {
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            
            return inflater.inflate(R.layout.fragement_weixin, container, false);
        }
    }
    
    /**
     * @ClassName: WeiXinFragement
     * @author Xiao JinLai
     * @Date 2014-12-21 下午8:09:01
     * @Description:朋友 Fragement
     */
    @SuppressLint("NewApi")
    public class FriendFragement extends Fragment {
    
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            
            return inflater.inflate(R.layout.fragement_friend, container, false);
        }
    }
    
    --------------------------------------------------------------------------------------------
    五、Fragment 家族常用的API
    
    1.Fragment常用的三个类:
    
    android.app.Fragment 主要用于定义 Fragment
    
    android.app.FragmentManager 主要用于在 Activity 中操作 Fragment
    
    android.app.FragmentTransaction 保证一些列 Fragment 操作的原子性,熟悉事务这个词,一定能明白
    
    2.获取FragmentManage的方式:
    
    getFragmentManager() // v4中,getSupportFragmentManager
    
    3.主要的操作都是 FragmentTransaction 的方法:
    
    FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
    
    4.往 Activity 中添加一个 Fragment:
    
    transaction.add() 
    
    5.从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
    transaction.remove() 
    
    6.使用另一个 Fragment 替换当前的,实际上就是 remove() 然后 add() 的合体
    transaction.replace()
    
    7.隐藏当前的 Fragment,仅仅是设为不可见,并不会销毁
    transaction.hide()
    
    8.显示之前隐藏的 Fragment
    transaction.show()
    
    9.将此 Fragment 从 Activity 中分离,会销毁其布局,但不会销毁该实例
    detach()
    
    10.将从Activity中分离的Fragment,重新关联到该Activity,重新创建其视图层次
    attach()
    
    11.注意:常用Fragment的哥们,可能会经常遇到这样 Activity 状态不一致:State loss 这样的错误。主要是因为:commit 方法一定要在 Activity.onSaveInstance() 之前调用。
    transatcion.commit()//提交一个事务
    
    --------------------------------------------------------------------------------------------
    上述,基本是操作 Fragment 的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。
    
    值得注意的是:如果你喜欢使用 Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
    
    1.比如:我在 FragmentA 中的 EditText 填了一些数据,当切换到 FragmentB 时,如果希望会到 A 还能看到数据,则适合你的就是 hide 和 show;也就是说,希望保留用户操作的面板,你可以使用 hide 和 show,当然了不要使劲在那 new 实例,进行下非 null 判断。
    
    2.再比如:我不希望保留用户操作,你可以使用remove(),然后 add();或者使用 replace() 这个和 remove,add 是相同的效果。
    
    3.remove 和 detach 有一点细微的区别,在不考虑回退栈的情况下,remove 会销毁整个 Fragment 实例,而detach 则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前 Activity 一直存在,那么在不希望保留用户操作的时候,你可以优先使用 detach。
    
    上述已经介绍完成了Fragment常用的一些方法,相信看完,大家一定清楚了Fragment的产生理由,以及如何使用Fragment,再根据API的讲解,也能明白,曾经为何觉得Fragment会出现一些列乱七八槽的问题,终究是因为没有弄清楚其生命周期。
    
    由于篇幅原因,剩下的内容留到下一篇了。在下一篇,会介绍:
    
    1、如何管理 Fragment 回退栈
    2、Fragment 如何与 Activity 交互
    3、Fragment 与 Activity 交互的最佳实践
    4、没有视图的 Fragment 的用处
    5、使用 Fragment 创建对话框
    6、如何与 ActionBar,MenuItem 集成等
  • 相关阅读:
    target runtime apache v6.0 not defined解决
    java.lang.AbstractMethodError: javax.servlet.jsp.JspFactory.getJspApplicationContext(Ljavax/servlet/ServletContext;)Ljavax/servlet/jsp/JspApplicationContext;
    The valid characters are defined in RFC 7230 and RFC 3986问题
    invalid END header解决方法
    You have more than one version of ‘org.apache.commons.logging.Log’ visible, which is not allowed问题解决
    Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    在eclipse中import java web项目时遇到的一些问题并将该项目通过tomcat发布
    java byte转string 涉及到字节流中有中文
    spring+mybatis框架搭建时遇到Mapped Statements collection does not contain value for...的错误
    试试看读一下Zepto源码
  • 原文地址:https://www.cnblogs.com/zx-blog/p/11836348.html
Copyright © 2011-2022 走看看