fragment_fragmenttransaction - CSDN
fragment 订阅
《Fragment》是花想创作的网络小说,发表于晋江文学网。 展开全文
《Fragment》是花想创作的网络小说,发表于晋江文学网。
信息
作    者
花想
中文名称
Fragment
主    角
亞瑟.柯克蘭,伊莉莎白。女王 [1]
连载平台
晋江文学网
连载状态
连载中 [1]
DevExpress控件应用
作品简介玫瑰枯零了,我会为你栽种新的。婚戒失去了,我会为你亲手戴上专属的。悔懊和骄傲埋在四百年前的土壤下,由泪水疯狂哀恨灌溉至死--或是我以为我会因此而死。然而,四百年後的陵墓破裂了,阳光从冬季复醒,而我看见了爱意与思念的花藤蔓生而出--CP为亚瑟与女王,女王的鬼魂(?)穿越来到现代再续前缘,架空历史向的一篇。有Doctor Who神奇串场、阿尔大发醋劲。原为一年前的即兴短文,结果後面越写越认真了到现在还未完结…… [1] 
收起全文
精华内容
参与话题
  • 一、为什么要使用Fragment 1、当我们需要动态的多界面切换的时候,就需要将UI元素和Activity融合成一个模块。在2.3中我们一般通过各种Activity中进行跳转来实现多界面的跳转和单个界面动态改变。在4.0或以上系统中...

    一、为什么要使用Fragment

    1、当我们需要动态的多界面切换的时候,就需要将UI元素和Activity融合成一个模块。在2.3中我们一般通过各种Activity中进行跳转来实现多界面的跳转和单个界面动态改变。在4.0或以上系统中就可以使用新的特性来方便的达到这个效果--Fragment类。Fragment类似一个嵌套Activity,可以定义自己的layout和自己的生命周期。

    2、 多个Fragment可以放在一个Activity中(所以上面讲到类似一个嵌套Activity),而这个类可以对这些Fragment进行配置以适应不同的屏幕尺寸(比如平板和手机)。

    二、使用Fragment

    1、Fragment 是 activity 的界面中的一部分或一种行为。可以把多个 Fragment 组合到一个 activity 中来创建一 个多面界面并且可以在多个 activity 中重用一个 Fragment。可以把 Fragment 认为模块化的一段 activity,它具 有自己的生命周期,接收它自己的事件,并可以在 activity 运行时被添加或删除。

    2、Fragment 不能独立存在,它必须嵌入到 activity 中,而且 Fragment 的生命周期直接受所在的 activity 的影 响。

    3、当向 activity 中添加一个 Fragment 时,它须置于 ViewGroup 控件中,并且需定义 Fragment 自己的界面。可 以在 layoutxml 文件中声明 Fragment,元素为:;也可以在代码中创建 Fragment,然后把它加入到 ViewGroup 控件中。然而,Fragment 不一定非要放在 activity 的界面中,它可以隐藏在后台为 actvitiy 工作。

    三、 生命周期

    通常, 应当至少实现如下的生命周期方法:

    onCreate()
    当创建fragment时, 系统调用该方法.

    在实现代码中,应当初始化想要在fragment中保持的必要组件, 当fragment被暂停或者停止后可以恢复.

    onCreateView()
    fragment第一次绘制它的用户界面的时候, 系统会调用此方法. 为了绘制fragment的UI,此方法必须返回一个View, 这个view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null.

    onPause()

    用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁.) 在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回).

    大多数程序应最少对 fragment 实现这三个方法。当然还有其它几个回调方法可应该按情况实现之。
    下图为 fragment 的生命周期(它所在的 activity 处于运行状态)。

    \

    四、如何使用Fragment

    1、添加一个用户界面
    fragment通常用来作为一个activity的用户界面的一部分,并将它的layout提供给activity.为了给一个fragment提供一 个layout,你必须实现 onCreateView()回调方法, 当到了fragment绘制它自己的layout的时候,Android系统调用它.你的此方法的实现代码必须返回一个你的fragment的 layout的根view.

    从onCreateView()返回的View, 也可以从一个layout的xml资源文件中读取并生成. 为了帮助你这么做, onCreateView() 提供了一个LayoutInflater 对象.

    举个例子, 这里有一个Fragment的子类, 从文件 frament_main.xml 加载了一个layout:

    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState) {
    		View view = inflater.inflate(R.layout.frament_main, container, false);
    		return view;
    	}
    PS
    传入onCreateView()的container参数是你的fragmentlayout将被插入的父ViewGroup(来自activity的layout) savedInstanceState 参数是一个Bundle, 如果fragment是被恢复的,它提供关于fragment的之前的实例的数据,

    inflate() 方法有3个参数:

    a、想要加载的layout的resource ID.
    b、加载的layout的父ViewGroup.传入container是很重要的, 目的是为了让系统接受所要加载的layout的根view的layout参数,由它将挂靠的父view指定.
    c、布尔值指示在加载期间, 展开的layout是否应当附着到ViewGroup (第二个参数).

    2、将fragment添加到activity
    通常地, fragment为宿主activity提供UI的一部分, 被作为activity的整个viewhierarchy的一部分被嵌入. 有2种方法你可以添加一个fragment到activity layout:


    2.1、使用XML将Fragment添加到一个Activity中
    在这种情况下,你可以像为View一样, 为fragment指定layout属性。

    PS

    1、 中的 android:name属性指定了在layout中实例化的Fragment类.当系统创建这个activity layout时,它实例化每一个在layout中指定的fragment,并调用每一个上的onCreateView()方法,来获取每一个 fragment的layout.系统将从fragment返回的 View直接插入到元素所在的地方.2、通过在xml中定义fragment的方式,我们不能在运行时移除fragment。如果我们想要通过切换fragments来跟用户有更好的互动,那么就需要在activity启动的时候定义fragment了。

    2.2、在运行时添加一个Fragment到Activity

    上面一节的在activity的布局文件(layout xml)中添加Fragment的方法我们已经知道了。现在我们将学习另外一种方式,这种方式允许我们在运行时动态的显示和隐藏fragment。为了达到在activity中动态管理Fragment,我们需要用到FragmentManager,并且通过它创建FragmentTransaction。

    activity允许移除或者替换fragment需要有如下条件:1、activity的onCreate()方法中添加初始化的fragment2、fragment放置位置的布局中必须有一个视图容器比如:res/layout/news_articles.xml文件提供了视图容器。

    <frameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


    Activity中使用getSupportFragmentManager()获取FragmentManager,之后调用beginTransaction去创建一个FragmentTransaction对象, 再调用add()方法即可添加一个fragment。 在activity中可以使用同一个FragmentTransaction对象去执行多个fragment事务,当做这样操作时,必须调用commint()方法。 下面的代码演示怎样添加一个fragment到res/layout/news_articles.xml的layout:
    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;
    
    public class MainActivity extends FragmentActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.news_articles);
    
            if (findViewById(R.id.fragment_container) != null) {
    
                if (savedInstanceState != null) {
                    return;
                }
    
                HeadlinesFragment firstFragment = new HeadlinesFragment();
    
                firstFragment.setArguments(getIntent().getExtras());
                
                // Add the fragment to the 'fragment_container' FrameLayout
                getSupportFragmentManager().beginTransaction()
                        .add(R.id.fragment_container, firstFragment).commit();
            }
        }
    }

    add()的第一个参数是fragment要放入的ViewGroup, 由resource ID指定,第二个参数是需要添加的fragment.一旦用FragmentTransaction做了改变,为了使改变生效,必须调用commit().

    PS

    现在再来说明另外一个实例,实例图如下,我要在四个标签页面切换(主页,手机,配件,购物车)

    \

    代码如下,具体就是通过影藏和显示fragment来实现切换

    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentActivity;
    import android.view.View;
    import android.view.Window;
    
    public class FramentMainActivity extends FragmentActivity {
    
    	private Fragment[] fragments;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_frament_main);
    
    		fragments = new Fragment[4];
    		fragments[0] = getSupportFragmentManager().findFragmentById(R.id.farment_main);
    		fragments[1] = getSupportFragmentManager().findFragmentById(R.id.farment_phone);
    		fragments[2] = getSupportFragmentManager().findFragmentById(R.id.farment_accessory);
    		fragments[3] = getSupportFragmentManager().findFragmentById(R.id.farment_cart);
    		getSupportFragmentManager().beginTransaction().
    		hide(fragments[1]).hide(fragments[2]).hide(fragments[3]).show(fragments[0]).commit();
    
    	}
    
    	public void mainClick(View view){
    		getSupportFragmentManager().beginTransaction().hide(fragments[1]).hide(fragments[2]).hide(fragments[3]).show(fragments[0]).commit();
    	}
    	public void phoneClick(View view){
    		getSupportFragmentManager().beginTransaction().hide(fragments[0]).hide(fragments[2]).hide(fragments[3]).show(fragments[1]).commit();
    	}
    	public void accessoryClick(View view){
    		getSupportFragmentManager().beginTransaction().hide(fragments[0]).hide(fragments[1]).hide(fragments[3]).show(fragments[2]).commit();
    	}
    	public void cartClick(View view){
    		getSupportFragmentManager().beginTransaction().hide(fragments[0]).hide(fragments[1]).hide(fragments[2]).show(fragments[3]).commit();
    	}
    }
    

    3、Frament 管理

    要管理 fragment,需使用 FragmentManager,要获取它,需在 activity 中调用方法 getFragmentManager()。 可以用 FragmentManager 来做以上事情:

    使用方法 findFragmentById()或 findFragmentByTag(),获取 activity 中已存在的 fragment

    使用方法 popBackStack()从 activity 的后退栈中弹出 fragment(这可以模拟后退键引发的动作)

    用方法 addOnBackStackChangedListerner()注册一个侦听器以监视后退栈的变化

    还可以使用 FragmentManager 打开一个 FragmentTransaction 来执行 fragment 的事务,比如添加或删除 fragment。

    在 activity 中使用 fragment 的一个伟大的好处是能跟据用户的输入对 fragment 进行添加、删除、替换以及执行 其它动作的能力。提交的一组 fragment 的变化叫做一个事务。事务通过 FragmentTransaction 来执行。还可以把每个 事务保存在 activity 的后退栈中,这样就可以让用户在 fragment 变化之间导航(跟在 activity 之间导航一样)。

    可以通过 FragmentManager 来取得 FragmentTransaction 的实例,如下:

    FragmentManagerfragmentManager = getFragmentManager();
    FragmentTransactionfragmentTransaction =fragmentManager.beginTransaction(); 

    一个事务是在同一时刻执行的一组动作(很像数据库中的事务)。可以用 add(),remove(),replace()等方法构成事务,最后使用 commit()方法提交事务。在调用 commint()之前,可以用addToBackStack()把事务添加到一个后退栈中, 这个后退栈属于所在的 activity。有了它,就可以在用户按下返回键时,返回到 fragment 执行事务之前的状态。如 下例:演示了如何用一个 fragment 代替另一个 fragment,同时在后退栈中保存被代替的 fragment 的状态。

    4、回退栈管理

    类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

    看这样一个效果图:


    点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学Android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。

    如何添加一个Fragment事务到回退栈:

    FragmentTransaction.addToBackStack(String)

    下面讲解代码:很明显一共是3个Fragment和一个Activity.

    先看Activity的布局文件:

    1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     xmlns:tools="http://schemas.android.com/tools"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent" >  
    5.   
    6.     <FrameLayout  
    7.         android:id="@+id/id_content"  
    8.         android:layout_width="fill_parent"  
    9.         android:layout_height="fill_parent" >  
    10.     </FrameLayout>  
    11.   
    12. </RelativeLayout>  
    <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" >
    
        <FrameLayout
            android:id="@+id/id_content"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" >
        </FrameLayout>
    
    </RelativeLayout>
    不同的Fragment就在这个FrameLayout中显示。

    MainActivity.java

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Activity;  
    4. import android.app.FragmentManager;  
    5. import android.app.FragmentTransaction;  
    6. import android.os.Bundle;  
    7. import android.view.Window;  
    8.   
    9. public class MainActivity extends Activity  
    10. {  
    11.   
    12.   
    13.     @Override  
    14.     protected void onCreate(Bundle savedInstanceState)  
    15.     {  
    16.         super.onCreate(savedInstanceState);  
    17.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
    18.         setContentView(R.layout.activity_main);  
    19.   
    20.         FragmentManager fm = getFragmentManager();  
    21.         FragmentTransaction tx = fm.beginTransaction();  
    22.         tx.add(R.id.id_content, new FragmentOne(),"ONE");  
    23.         tx.commit();  
    24.     }  
    25.   
    26. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.Window;
    
    public class MainActivity extends Activity
    {
    
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_main);
    
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.add(R.id.id_content, new FragmentOne(),"ONE");
    		tx.commit();
    	}
    
    }
    
    很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

    下面是FragmentOne

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Fragment;  
    4. import android.app.FragmentManager;  
    5. import android.app.FragmentTransaction;  
    6. import android.os.Bundle;  
    7. import android.view.LayoutInflater;  
    8. import android.view.View;  
    9. import android.view.View.OnClickListener;  
    10. import android.view.ViewGroup;  
    11. import android.widget.Button;  
    12.   
    13. public class FragmentOne extends Fragment implements OnClickListener  
    14. {  
    15.   
    16.     private Button mBtn;  
    17.   
    18.     @Override  
    19.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    20.             Bundle savedInstanceState)  
    21.     {  
    22.         View view = inflater.inflate(R.layout.fragment_one, container, false);  
    23.         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
    24.         mBtn.setOnClickListener(this);  
    25.         return view;  
    26.     }  
    27.   
    28.     @Override  
    29.     public void onClick(View v)  
    30.     {  
    31.         FragmentTwo fTwo = new FragmentTwo();  
    32.         FragmentManager fm = getFragmentManager();  
    33.         FragmentTransaction tx = fm.beginTransaction();  
    34.         tx.replace(R.id.id_content, fTwo, "TWO");  
    35.         tx.addToBackStack(null);  
    36.         tx.commit();  
    37.   
    38.     }  
    39.   
    40. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.Button;
    
    public class FragmentOne extends Fragment implements OnClickListener
    {
    
    	private Button mBtn;
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		View view = inflater.inflate(R.layout.fragment_one, container, false);
    		mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
    		mBtn.setOnClickListener(this);
    		return view;
    	}
    
    	@Override
    	public void onClick(View v)
    	{
    		FragmentTwo fTwo = new FragmentTwo();
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.replace(R.id.id_content, fTwo, "TWO");
    		tx.addToBackStack(null);
    		tx.commit();
    
    	}
    
    }
    

    我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

    接下来FragmentTwo

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Fragment;  
    4. import android.app.FragmentManager;  
    5. import android.app.FragmentTransaction;  
    6. import android.os.Bundle;  
    7. import android.view.LayoutInflater;  
    8. import android.view.View;  
    9. import android.view.View.OnClickListener;  
    10. import android.view.ViewGroup;  
    11. import android.widget.Button;  
    12.   
    13. public class FragmentTwo extends Fragment implements OnClickListener  
    14. {  
    15.   
    16.     private Button mBtn ;  
    17.     @Override  
    18.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    19.             Bundle savedInstanceState)  
    20.     {  
    21.         View view = inflater.inflate(R.layout.fragment_two, container, false);  
    22.         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
    23.         mBtn.setOnClickListener(this);  
    24.         return view ;   
    25.     }  
    26.     @Override  
    27.     public void onClick(View v)  
    28.     {  
    29.         FragmentThree fThree = new FragmentThree();  
    30.         FragmentManager fm = getFragmentManager();  
    31.         FragmentTransaction tx = fm.beginTransaction();  
    32.         tx.hide(this);  
    33.         tx.add(R.id.id_content , fThree, "THREE");  
    34. //      tx.replace(R.id.id_content, fThree, "THREE");  
    35.         tx.addToBackStack(null);  
    36.         tx.commit();  
    37.     }  
    38.   
    39.   
    40. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.Button;
    
    public class FragmentTwo extends Fragment implements OnClickListener
    {
    
    	private Button mBtn ;
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		View view = inflater.inflate(R.layout.fragment_two, container, false);
    		mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
    		mBtn.setOnClickListener(this);
    		return view ; 
    	}
    	@Override
    	public void onClick(View v)
    	{
    		FragmentThree fThree = new FragmentThree();
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.hide(this);
    		tx.add(R.id.id_content , fThree, "THREE");
    //		tx.replace(R.id.id_content, fThree, "THREE");
    		tx.addToBackStack(null);
    		tx.commit();
    	}
    
    
    }
    

    这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

    最后FragmentThree就是简单的Toast了:

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Fragment;  
    4. import android.os.Bundle;  
    5. import android.view.LayoutInflater;  
    6. import android.view.View;  
    7. import android.view.View.OnClickListener;  
    8. import android.view.ViewGroup;  
    9. import android.widget.Button;  
    10. import android.widget.Toast;  
    11.   
    12. public class FragmentThree extends Fragment implements OnClickListener  
    13. {  
    14.   
    15.     private Button mBtn;  
    16.   
    17.     @Override  
    18.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    19.             Bundle savedInstanceState)  
    20.     {  
    21.         View view = inflater.inflate(R.layout.fragment_three, container, false);  
    22.         mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);  
    23.         mBtn.setOnClickListener(this);  
    24.         return view;  
    25.     }  
    26.   
    27.     @Override  
    28.     public void onClick(View v)  
    29.     {  
    30.         Toast.makeText(getActivity(), " i am a btn in Fragment three",  
    31.                 Toast.LENGTH_SHORT).show();  
    32.     }  
    33.   
    34. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class FragmentThree extends Fragment implements OnClickListener
    {
    
    	private Button mBtn;
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		View view = inflater.inflate(R.layout.fragment_three, container, false);
    		mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);
    		mBtn.setOnClickListener(this);
    		return view;
    	}
    
    	@Override
    	public void onClick(View v)
    	{
    		Toast.makeText(getActivity(), " i am a btn in Fragment three",
    				Toast.LENGTH_SHORT).show();
    	}
    
    }
    

    好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

    这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!

    5、Fragment与Activity通信

    因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

    a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

    b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

    c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

    注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

    6、Fragment与Activity通信的最佳实践

    因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。

    下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应:

    首先看FragmentOne

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Fragment;  
    4. import android.os.Bundle;  
    5. import android.view.LayoutInflater;  
    6. import android.view.View;  
    7. import android.view.View.OnClickListener;  
    8. import android.view.ViewGroup;  
    9. import android.widget.Button;  
    10.   
    11. public class FragmentOne extends Fragment implements OnClickListener  
    12. {  
    13.     private Button mBtn;  
    14.   
    15.     /** 
    16.      * 设置按钮点击的回调 
    17.      * @author zhy 
    18.      * 
    19.      */  
    20.     public interface FOneBtnClickListener  
    21.     {  
    22.         void onFOneBtnClick();  
    23.     }  
    24.   
    25.     @Override  
    26.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    27.             Bundle savedInstanceState)  
    28.     {  
    29.         View view = inflater.inflate(R.layout.fragment_one, container, false);  
    30.         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
    31.         mBtn.setOnClickListener(this);  
    32.         return view;  
    33.     }  
    34.   
    35.     /** 
    36.      * 交给宿主Activity处理,如果它希望处理 
    37.      */  
    38.     @Override  
    39.     public void onClick(View v)  
    40.     {  
    41.         if (getActivity() instanceof FOneBtnClickListener)  
    42.         {  
    43.             ((FOneBtnClickListener) getActivity()).onFOneBtnClick();  
    44.         }  
    45.     }  
    46.   
    47. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.Button;
    
    public class FragmentOne extends Fragment implements OnClickListener
    {
    	private Button mBtn;
    
    	/**
    	 * 设置按钮点击的回调
    	 * @author zhy
    	 *
    	 */
    	public interface FOneBtnClickListener
    	{
    		void onFOneBtnClick();
    	}
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		View view = inflater.inflate(R.layout.fragment_one, container, false);
    		mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
    		mBtn.setOnClickListener(this);
    		return view;
    	}
    
    	/**
    	 * 交给宿主Activity处理,如果它希望处理
    	 */
    	@Override
    	public void onClick(View v)
    	{
    		if (getActivity() instanceof FOneBtnClickListener)
    		{
    			((FOneBtnClickListener) getActivity()).onFOneBtnClick();
    		}
    	}
    
    }
    

    可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。

    再看FragmentTwo

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Fragment;  
    4. import android.os.Bundle;  
    5. import android.view.LayoutInflater;  
    6. import android.view.View;  
    7. import android.view.View.OnClickListener;  
    8. import android.view.ViewGroup;  
    9. import android.widget.Button;  
    10.   
    11. public class FragmentTwo extends Fragment implements OnClickListener  
    12. {  
    13.   
    14.       
    15.     private Button mBtn ;  
    16.       
    17.     private FTwoBtnClickListener fTwoBtnClickListener ;  
    18.       
    19.     public interface FTwoBtnClickListener  
    20.     {  
    21.         void onFTwoBtnClick();  
    22.     }  
    23.     //设置回调接口  
    24.     public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)  
    25.     {  
    26.         this.fTwoBtnClickListener = fTwoBtnClickListener;  
    27.     }  
    28.     @Override  
    29.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    30.             Bundle savedInstanceState)  
    31.     {  
    32.         View view = inflater.inflate(R.layout.fragment_two, container, false);  
    33.         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
    34.         mBtn.setOnClickListener(this);  
    35.         return view ;   
    36.     }  
    37.     @Override  
    38.     public void onClick(View v)  
    39.     {  
    40.         if(fTwoBtnClickListener != null)  
    41.         {  
    42.             fTwoBtnClickListener.onFTwoBtnClick();  
    43.         }  
    44.     }  
    45.   
    46. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.Button;
    
    public class FragmentTwo extends Fragment implements OnClickListener
    {
    
    	
    	private Button mBtn ;
    	
    	private FTwoBtnClickListener fTwoBtnClickListener ;
    	
    	public interface FTwoBtnClickListener
    	{
    		void onFTwoBtnClick();
    	}
    	//设置回调接口
    	public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
    	{
    		this.fTwoBtnClickListener = fTwoBtnClickListener;
    	}
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		View view = inflater.inflate(R.layout.fragment_two, container, false);
    		mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
    		mBtn.setOnClickListener(this);
    		return view ; 
    	}
    	@Override
    	public void onClick(View v)
    	{
    		if(fTwoBtnClickListener != null)
    		{
    			fTwoBtnClickListener.onFTwoBtnClick();
    		}
    	}
    
    }
    

    与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。

    最后看Activity :

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Activity;  
    4. import android.app.FragmentManager;  
    5. import android.app.FragmentTransaction;  
    6. import android.os.Bundle;  
    7. import android.view.Window;  
    8.   
    9. import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;  
    10. import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;  
    11.   
    12. public class MainActivity extends Activity implements FOneBtnClickListener,  
    13.         FTwoBtnClickListener  
    14. {  
    15.   
    16.     private FragmentOne mFOne;  
    17.     private FragmentTwo mFTwo;  
    18.     private FragmentThree mFThree;  
    19.   
    20.     @Override  
    21.     protected void onCreate(Bundle savedInstanceState)  
    22.     {  
    23.         super.onCreate(savedInstanceState);  
    24.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
    25.         setContentView(R.layout.activity_main);  
    26.   
    27.         mFOne = new FragmentOne();  
    28.         FragmentManager fm = getFragmentManager();  
    29.         FragmentTransaction tx = fm.beginTransaction();  
    30.         tx.add(R.id.id_content, mFOne, "ONE");  
    31.         tx.commit();  
    32.     }  
    33.   
    34.     /** 
    35.      * FragmentOne 按钮点击时的回调 
    36.      */  
    37.     @Override  
    38.     public void onFOneBtnClick()  
    39.     {  
    40.   
    41.         if (mFTwo == null)  
    42.         {  
    43.             mFTwo = new FragmentTwo();  
    44.             mFTwo.setfTwoBtnClickListener(this);  
    45.         }  
    46.         FragmentManager fm = getFragmentManager();  
    47.         FragmentTransaction tx = fm.beginTransaction();  
    48.         tx.replace(R.id.id_content, mFTwo, "TWO");  
    49.         tx.addToBackStack(null);  
    50.         tx.commit();  
    51.     }  
    52.   
    53.     /** 
    54.      * FragmentTwo 按钮点击时的回调 
    55.      */  
    56.     @Override  
    57.     public void onFTwoBtnClick()  
    58.     {  
    59.         if (mFThree == null)  
    60.         {  
    61.             mFThree = new FragmentThree();  
    62.   
    63.         }  
    64.         FragmentManager fm = getFragmentManager();  
    65.         FragmentTransaction tx = fm.beginTransaction();  
    66.         tx.hide(mFTwo);  
    67.         tx.add(R.id.id_content, mFThree, "THREE");  
    68.         // tx.replace(R.id.id_content, fThree, "THREE");  
    69.         tx.addToBackStack(null);  
    70.         tx.commit();  
    71.     }  
    72.   
    73. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.Window;
    
    import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;
    import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;
    
    public class MainActivity extends Activity implements FOneBtnClickListener,
    		FTwoBtnClickListener
    {
    
    	private FragmentOne mFOne;
    	private FragmentTwo mFTwo;
    	private FragmentThree mFThree;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_main);
    
    		mFOne = new FragmentOne();
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.add(R.id.id_content, mFOne, "ONE");
    		tx.commit();
    	}
    
    	/**
    	 * FragmentOne 按钮点击时的回调
    	 */
    	@Override
    	public void onFOneBtnClick()
    	{
    
    		if (mFTwo == null)
    		{
    			mFTwo = new FragmentTwo();
    			mFTwo.setfTwoBtnClickListener(this);
    		}
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.replace(R.id.id_content, mFTwo, "TWO");
    		tx.addToBackStack(null);
    		tx.commit();
    	}
    
    	/**
    	 * FragmentTwo 按钮点击时的回调
    	 */
    	@Override
    	public void onFTwoBtnClick()
    	{
    		if (mFThree == null)
    		{
    			mFThree = new FragmentThree();
    
    		}
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.hide(mFTwo);
    		tx.add(R.id.id_content, mFThree, "THREE");
    		// tx.replace(R.id.id_content, fThree, "THREE");
    		tx.addToBackStack(null);
    		tx.commit();
    	}
    
    }
    

    代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

    7、如何处理运行时配置发生变化

    运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

    这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。

    好了,下面看一段代码:

    Activity:

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Activity;  
    4. import android.app.FragmentManager;  
    5. import android.app.FragmentTransaction;  
    6. import android.os.Bundle;  
    7. import android.view.Window;  
    8.   
    9. public class MainActivity extends Activity  
    10.   
    11. {  
    12.     private FragmentOne mFOne;  
    13.   
    14.     @Override  
    15.     protected void onCreate(Bundle savedInstanceState)  
    16.     {  
    17.         super.onCreate(savedInstanceState);  
    18.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
    19.         setContentView(R.layout.activity_main);  
    20.   
    21.         mFOne = new FragmentOne();  
    22.         FragmentManager fm = getFragmentManager();  
    23.         FragmentTransaction tx = fm.beginTransaction();  
    24.         tx.add(R.id.id_content, mFOne, "ONE");  
    25.         tx.commit();  
    26.   
    27.     }  
    28.   
    29. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.Window;
    
    public class MainActivity extends Activity
    
    {
    	private FragmentOne mFOne;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_main);
    
    		mFOne = new FragmentOne();
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction tx = fm.beginTransaction();
    		tx.add(R.id.id_content, mFOne, "ONE");
    		tx.commit();
    
    	}
    
    }
    

    Fragment

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Fragment;  
    4. import android.os.Bundle;  
    5. import android.util.Log;  
    6. import android.view.LayoutInflater;  
    7. import android.view.View;  
    8. import android.view.ViewGroup;  
    9.   
    10. public class FragmentOne extends Fragment  
    11. {  
    12.     private static final String TAG = "FragmentOne";  
    13.   
    14.     @Override  
    15.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
    16.             Bundle savedInstanceState)  
    17.     {  
    18.         Log.e(TAG, "onCreateView");  
    19.         View view = inflater.inflate(R.layout.fragment_one, container, false);  
    20.         return view;  
    21.     }  
    22.   
    23.     @Override  
    24.     public void onCreate(Bundle savedInstanceState)  
    25.     {  
    26.         // TODO Auto-generated method stub  
    27.         super.onCreate(savedInstanceState);  
    28.   
    29.         Log.e(TAG, "onCreate");  
    30.     }  
    31.   
    32.     @Override  
    33.     public void onDestroyView()  
    34.     {  
    35.         // TODO Auto-generated method stub  
    36.         super.onDestroyView();  
    37.         Log.e(TAG, "onDestroyView");  
    38.     }  
    39.   
    40.     @Override  
    41.     public void onDestroy()  
    42.     {  
    43.         // TODO Auto-generated method stub  
    44.         super.onDestroy();  
    45.         Log.e(TAG, "onDestroy");  
    46.     }  
    47.   
    48. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class FragmentOne extends Fragment
    {
    	private static final String TAG = "FragmentOne";
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		Log.e(TAG, "onCreateView");
    		View view = inflater.inflate(R.layout.fragment_one, container, false);
    		return view;
    	}
    
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		// TODO Auto-generated method stub
    		super.onCreate(savedInstanceState);
    
    		Log.e(TAG, "onCreate");
    	}
    
    	@Override
    	public void onDestroyView()
    	{
    		// TODO Auto-generated method stub
    		super.onDestroyView();
    		Log.e(TAG, "onDestroyView");
    	}
    
    	@Override
    	public void onDestroy()
    	{
    		// TODO Auto-generated method stub
    		super.onDestroy();
    		Log.e(TAG, "onDestroy");
    	}
    
    }
    

    很简单的代码,当你运行之后,不断的旋转屏幕,你会发现每旋转一次屏幕,屏幕上就多了一个FragmentOne的实例,并且后台log会打印出许多套生命周期的回调。

    类似:

    1. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
    2. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
    3. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
    4. 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView  
    5. 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView  
    6. 07-20 08:18:46.891: E/FragmentOne(1633): onCreateView  
    07-20 08:18:46.651: E/FragmentOne(1633): onCreate
    07-20 08:18:46.651: E/FragmentOne(1633): onCreate
    07-20 08:18:46.651: E/FragmentOne(1633): onCreate
    07-20 08:18:46.681: E/FragmentOne(1633): onCreateView
    07-20 08:18:46.831: E/FragmentOne(1633): onCreateView
    07-20 08:18:46.891: E/FragmentOne(1633): onCreateView
    

    这是为什么呢,因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。

    那么如何解决呢:

    其实通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建:

    默认的savedInstanceState会存储一些数据,包括Fragment的实例:通过打印可以看出:

    1. 07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]  
    07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]
    
    所以,我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例:

    1. package com.zhy.zhy_fragments;  
    2.   
    3. import android.app.Activity;  
    4. import android.app.FragmentManager;  
    5. import android.app.FragmentTransaction;  
    6. import android.os.Bundle;  
    7. import android.util.Log;  
    8. import android.view.Window;  
    9.   
    10. public class MainActivity extends Activity  
    11.   
    12. {  
    13.     private static final String TAG = "FragmentOne";  
    14.     private FragmentOne mFOne;  
    15.   
    16.     @Override  
    17.     protected void onCreate(Bundle savedInstanceState)  
    18.     {  
    19.         super.onCreate(savedInstanceState);  
    20.         requestWindowFeature(Window.FEATURE_NO_TITLE);  
    21.         setContentView(R.layout.activity_main);  
    22.   
    23.         Log.e(TAG, savedInstanceState+"");  
    24.           
    25.         if(savedInstanceState == null)  
    26.         {  
    27.             mFOne = new FragmentOne();  
    28.             FragmentManager fm = getFragmentManager();  
    29.             FragmentTransaction tx = fm.beginTransaction();  
    30.             tx.add(R.id.id_content, mFOne, "ONE");  
    31.             tx.commit();  
    32.         }  
    33.           
    34.           
    35.   
    36.     }  
    37.   
    38. }  
    package com.zhy.zhy_fragments;
    
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.Window;
    
    public class MainActivity extends Activity
    
    {
    	private static final String TAG = "FragmentOne";
    	private FragmentOne mFOne;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_main);
    
    		Log.e(TAG, savedInstanceState+"");
    		
    		if(savedInstanceState == null)
    		{
    			mFOne = new FragmentOne();
    			FragmentManager fm = getFragmentManager();
    			FragmentTransaction tx = fm.beginTransaction();
    			tx.add(R.id.id_content, mFOne, "ONE");
    			tx.commit();
    		}
    		
    		
    
    	}
    
    }
    

    现在无论进行多次旋转都只会有一个Fragment实例在Activity中。

    现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?

    其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。

    由于篇幅原因,就不贴测试代码了。

    展开全文
  • Android Fragment 真正的完全解析(上)

    万次阅读 多人点赞 2016-08-10 19:34:33
    本篇博客力求为大家说明Fragment如何产生,什么是FragmentFragment生命周期,如何静态和动态的使用FragmentFragment回退栈,Fragment事务;以及Fragment的一些特殊用途,例如:没有布局的Frag

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37970961

    自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragment谈上关系,做什么都要问下Fragment能实现不~~~哈哈,是不是有点过~~~

    本篇博客力求为大家说明Fragment如何产生,什么是Fragment,Fragment生命周期,如何静态和动态的使用Fragment,Fragment回退栈,Fragment事务;以及Fragment的一些特殊用途,例如:没有布局的Fragment有何用处?Fragment如何与Activity交互?Fragment如何创建对话框?Fragment如何与ActionBar集成等等。

    1、Fragment的产生与介绍

    Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成,更帅气的是Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment。

    2、Fragment的生命周期

    Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期。官网这张图很好的说明了两者生命周期的关系:


    可以看到Fragment比Activity多了几个额外的生命周期回调方法:
    onAttach(Activity)
    当Fragment与Activity发生关联时调用。
    onCreateView(LayoutInflater, ViewGroup,Bundle)
    创建该Fragment的视图
    onActivityCreated(Bundle)
    当Activity的onCreate方法返回时调用
    onDestoryView()
    与onCreateView想对应,当该Fragment的视图被移除时调用
    onDetach()
    与onAttach相对应,当Fragment与Activity关联被取消时调用
    注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现,
    3、静态的使用Fragment

    嘿嘿,终于到使用的时刻了~~

    这是使用Fragment最简单的一种方式,把Fragment当成普通的控件,直接写在Activity的布局文件中。步骤:

    1、继承Fragment,重写onCreateView决定Fragemnt的布局

    2、在Activity中声明此Fragment,就当和普通的View一样

    下面展示一个例子(我使用2个Fragment作为Activity的布局,一个Fragment用于标题布局,一个Fragment用于内容布局):

    TitleFragment的布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="@drawable/title_bar" >
    
        <ImageButton
            android:id="@+id/id_title_left_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="3dp"
            android:background="@drawable/showleft_selector" />
    
        <TextView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center"
            android:text="我不是微信"
            android:textColor="#fff"
            android:textSize="20sp"
            android:textStyle="bold" />
    
    </RelativeLayout>

    TitleFragment

    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.ViewGroup;
    import android.widget.ImageButton;
    import android.widget.Toast;
    
    public class TitleFragment extends Fragment
    {
    
    	private ImageButton mLeftMenu;
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		View view = inflater.inflate(R.layout.fragment_title, container, false);
    		mLeftMenu = (ImageButton) view.findViewById(R.id.id_title_left_btn);
    		mLeftMenu.setOnClickListener(new OnClickListener()
    		{
    			@Override
    			public void onClick(View v)
    			{
    				Toast.makeText(getActivity(),
    						"i am an ImageButton in TitleFragment ! ",
    						Toast.LENGTH_SHORT).show();
    			}
    		});
    		return view;
    	}
    }
    

    同理还有ContentFragment的其布局文件:

    <?xml version="1.0" encoding="utf-8"?>
    <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>

    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class ContentFragment extends Fragment
    {
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		return inflater.inflate(R.layout.fragment_content, container, false);
    	}
    
    }
    

    MainActivity

    package com.zhy.zhy_fragments;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.Window;
    
    public class MainActivity extends Activity
    {
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_main);
    	}
    
    }
    

    Activity的布局文件:

    <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/id_fragment_title"
            android:name="com.zhy.zhy_fragments.TitleFragment"
            android:layout_width="fill_parent"
            android:layout_height="45dp" />
    
        <fragment
            android:layout_below="@id/id_fragment_title"
            android:id="@+id/id_fragment_content"
            android:name="com.zhy.zhy_fragments.ContentFragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
    
    </RelativeLayout>

    是不是把Fragment当成普通的View一样声明在Activity的布局文件中,然后所有控件的事件处理等代码都由各自的Fragment去处理,瞬间觉得Activity好干净有木有~~代码的可读性、复用性以及可维护性是不是瞬间提升了~~~下面看下效果图:


    4、动态的使用Fragment

    上面已经演示了,最简单的使用Fragment的方式~下面介绍如何动态的添加、更新、以及删除Fragment

    为了动态使用Fragment,我们修改一下Actvity的布局文件,中间使用一个FrameLayout,下面添加四个按钮~~~嘿嘿~~不是微信的按钮- -!

    <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/id_fragment_title"
            android:name="com.zhy.zhy_fragments.TitleFragment"
            android:layout_width="fill_parent"
            android:layout_height="45dp" />
    
        <include
            android:id="@+id/id_ly_bottombar"
            android:layout_width="fill_parent"
            android:layout_height="55dp"
            android:layout_alignParentBottom="true"
            layout="@layout/bottombar" />
    
        <FrameLayout
            android:id="@+id/id_content"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_above="@id/id_ly_bottombar"
            android:layout_below="@id/id_fragment_title" />
    
    </RelativeLayout>

    底部四个按钮的布局就不贴了,到时看效果图就明白了~~

    下面主Activity

    package com.zhy.zhy_fragments;
    
    import android.app.Activity;
    import android.app.FragmentManager;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.view.Window;
    import android.widget.LinearLayout;
    
    public class MainActivity extends Activity implements OnClickListener
    {
    	private LinearLayout mTabWeixin;
    	private LinearLayout mTabFriend;
    
    	private ContentFragment mWeixin;
    	private FriendFragment mFriend;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.activity_main);
    
    		// 初始化控件和声明事件
    		mTabWeixin = (LinearLayout) findViewById(R.id.tab_bottom_weixin);
    		mTabFriend = (LinearLayout) findViewById(R.id.tab_bottom_friend);
    		mTabWeixin.setOnClickListener(this);
    		mTabFriend.setOnClickListener(this);
    
    		// 设置默认的Fragment
    		setDefaultFragment();
    	}
    
    	private void setDefaultFragment()
    	{
    		FragmentManager fm = getFragmentManager();
    		FragmentTransaction transaction = fm.beginTransaction();
    		mWeixin = new ContentFragment();
    		transaction.replace(R.id.id_content, mWeixin);
    		transaction.commit();
    	}
    
    	@Override
    	public void onClick(View v)
    	{
    		FragmentManager fm = getFragmentManager();
    		// 开启Fragment事务
    		FragmentTransaction transaction = fm.beginTransaction();
    
    		switch (v.getId())
    		{
    		case R.id.tab_bottom_weixin:
    			if (mWeixin == null)
    			{
    				mWeixin = new ContentFragment();
    			}
    			// 使用当前Fragment的布局替代id_content的控件
    			transaction.replace(R.id.id_content, mWeixin);
    			break;
    		case R.id.tab_bottom_friend:
    			if (mFriend == null)
    			{
    				mFriend = new FriendFragment();
    			}
    			transaction.replace(R.id.id_content, mFriend);
    			break;
    		}
    		// transaction.addToBackStack();
    		// 事务提交
    		transaction.commit();
    	}
    
    }
    

    可以看到我们使用FragmentManager对Fragment进行了动态的加载,这里使用的是replace方法~~下一节我会详细介绍FragmentManager的常用API。

    注:如果使用Android3.0以下的版本,需要引入v4的包,然后Activity继承FragmentActivity,然后通过getSupportFragmentManager获得FragmentManager。不过还是建议版Menifest文件的uses-sdk的minSdkVersion和targetSdkVersion都改为11以上,这样就不必引入v4包了。

    代码中间还有两个Fragment的子类,ContentFragment上面已经见过,FriendFragment其实类似:

    package com.zhy.zhy_fragments;
    
    import android.app.Fragment;
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class FriendFragment extends Fragment
    {
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    			Bundle savedInstanceState)
    	{
    		return inflater.inflate(R.layout.fragment_friend, container, false);
    	}
    
    }
    

    效果图:


    可以看到很好的实现了效果,其实这个效果以前的博客中也出现过,在博客:Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager,有兴趣可以看看。ps:为了代码的简洁,就不添加按钮的点击变化什么的了,主要讲解功能了~~~

    5、Fragment家族常用的API

    Fragment常用的三个类:

    android.app.Fragment 主要用于定义Fragment

    android.app.FragmentManager 主要用于在Activity中操作Fragment

    android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白~

    a、获取FragmentManage的方式:

    getFragmentManager() // v4中,getSupportFragmentManager

    b、主要的操作都是FragmentTransaction的方法

    FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务

    transaction.add() 

    往Activity中添加一个Fragment

    transaction.remove()

    从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。

    transaction.replace()

    使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~

    transaction.hide()

    隐藏当前的Fragment,仅仅是设为不可见,并不会销毁

    transaction.show()

    显示之前隐藏的Fragment

    detach()

    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。

    attach()

    重建view视图,附加到UI上并显示。

    transatcion.commit()//提交一个事务

    注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。

    上述,基本是操作Fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。

    值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。

    a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。

    b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。

    c、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集成等~~


    下一篇:Android Fragment 真正的完全解析(下)


    好了,有任何问题请留言~~





    会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。



    展开全文
  • Fragment使用

    千次阅读 2019-04-04 13:46:30
    1:如何在一个activity中添加fragment 2:如何动态的添加fragment 3:fragment与activity之间的通信 4:fragment的生命周期 1:一个完整的fragment需要有xml文件和java文件 xml文件自己随便加点东西就可以(它与一般...

    1:如何在一个activity中添加fragment
    2:如何动态的添加fragment
    3:fragment与activity之间的通信
    4:fragment的生命周期

    1:一个完整的fragment需要有xml文件和java文件
    xml文件自己随便加点东西就可以(它与一般的xml文件没啥两样);
    java文件需要继承Fragment如下图:在这里插入图片描述
    继承Fragment之后重写它的onCreateView()方法,将你的xml添加进去

    然后在activity_main中加上fragment:
    在这里插入图片描述
    这样就完成了一个Fragment的添加,按这个步骤再添加一个Fragment,运行程序就可以看到Activity中有两个Fragment了!(如果想要效果明显可以把第二个的Fragment的xml文件更换一个背景哦)

    2:动态添加Fragment:
    话不多说上代码!

    改一下activiy_main
    在这里插入图片描述
    将其中一个改为FrameLayout,属性不用改。

    好了现在来看java代码:
    transaction.replace(); //一参:是你想将fragment添加上去的布局的id 二参:是你要添加的fragment对象!!不要弄错!
    在这里插入图片描述

    前边就是一个按钮点击时间就不再介绍,主要看一下ADD方法里边的内容
    ADD方法参数中new了一个Fragment 为下面的添加做准备
    先通过getActivity获取一个fragment管理器
    然后通过管理器对象新建一个事务对象
    最后通过事务对象的replace方法加上要加的fragment和fragment对象
    再利用事务对象的commit提交一下就OK了!

    注意:transaction.addToBackStack(null);//添加返回栈,按下back后不会直接退出程序,而是返回上一个碎片

    现在的效果是你点击有这个按钮的Fragment另一个Fragment才会添加到Activtiy中。

    3:fragment与activity之间的通信:
    在acivtiy中用到fragment时用下面的方法获取fragment对象:

     Fragment_right fragment_right= (Fragment_right) getSupportFragmentManager().findFragmentById(R.id.frame_right);
    

    在fragment中用到activity用下面方法:

    MainActivity activity=(MainActivity)getActivtiy;
    

    这里的通信可以自己做个小demo,例如:fragment1点击按钮来设置fragmen2里的TextView

    4:Fragment生命周期!!!非常重要!!!!
    很重要但是也很简单,需要自己去摸清它的运作方式,这对以后编程工作影响很大,希望刚入门的小伙伴一定将其弄明白。
    在这里插入图片描述

    这里就不做过多的详细解说,你们可以去参考其他的博客,有很多写的很详细的,
    最后希望大家都能学好一门技术走向人生巅峰!
    本人初学android,写博客也是为了加深记忆,便于以后查看,如果有写的不对的地方,请为我指点迷津!

    展开全文
  • Fragment简单介绍

    万次阅读 2018-05-31 10:07:00
    Fragment简单介绍,全面的了解Fragment的使用

    一、Fragment概念以及设计原理

           Fragment是Android3.0后引入的一个新的API,他出现的初衷是为了适应大屏幕的平板电脑, 当然现在他仍然是平板APP UI设计的宠儿,而且我们普通手机开发也会加入这个Fragment,我们可以把他看成一个小型的Activity,又称Activity片段!想想,如果一个很大的界面,我们就一个布局,写起界面来会有多麻烦,而且如果组件多的话是管理起来也很麻烦!而使用Fragment我们可以把屏幕划分成几块,然后进行分组,进行一个模块化的管理!从而可以更加方便的在运行过程中动态地更新Activity的用户界面!另外Fragment并不能单独使用,他需要嵌套在Activity中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响。比如,当Activity暂停时,其中的所有片段也会暂停;当Activity被销毁时,所有片段也会被销毁。 不过,当Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除它们。 当您执行此类片段事务时,您也可以将其添加到由Activity管理的返回栈,Activity中的每个返回栈条目都是一条已发生片段事务的记录。返回栈让用户可以通过按返回按钮撤消片段事务(后退)。

    二、android.support.v4.app.Fragment和android.app.Fragment

    • android.app.Fragment 兼容的最低版本是android:minSdkVersion=”11” 即3.0版。
    • android.support.v4.app.Fragment 兼容的最低版本是android:minSdkVersion=”4” 即1.6版。

           推荐用android.support.v4.app.Fragment

    请注意:一个应用中,千万不要混合使用给自己增加不必要的麻烦。最好一个应用里面都用v4包或者app包的Fragment。

    三、Fragment生命周期函数

    Fragment周期函数

    生命周期函数 相关解释
    onAttach() 关联到Activity的时候调用。如果,需要使用Activity的引用或者使用Activity作为其他操作的上下文,将在此回调方法中实现
    onCreate() 系统创建Fragment的时候回调
    onCreateView() 当第一次绘制Fragment的UI时系统调用这个方法,该方法将返回一个View,如果Fragment不提供UI也可以返回null。注意,如果继承自ListFragment,onCreateView()默认的实现会返回一个ListView,所以不用自己实现。这个函数的Bundle参数和onCretate()函数的Bundle蚕食是同一个
    onActivityCreated() 当Activity中的onCreate方法执行完后调用。可以在这个函数里面做和Activity UI交互的操作(因为Activity的onCreate()函数之后Activity的UI已经准备好了,可以UI交互)。这个函数的Bundle参数和onCretate()函数的Bundle蚕食是同一个
    onStart() 启动Fragment的时候回调,这个时候Fragment可见
    onResume() Fragment变为活动状态获取焦点的时候是回调,这个时候Fragment已经完全展示在前台,并且可以和用户交互
    onPause() Fragemnt变成非活动状态失去焦点的时候调用,注意这个时候Fragment还是可见的,只是不能和用户交互了而已
    onStop() Fragment变成不可见的时候调用。这个时候Fragment还是活着的,只是可能别加入到了Fragment的回退栈中
    onDestroyView() Fragment中的布局被移除的时候调用
    onDestroy() Fragment被销毁的时候调用
    onDetach() Fragment和Activity解除关联的时候调用个

    >
           除了上面列出的标准周期函数之外,还有几个函数也要特别注意:

    • onViewCreated(): 是不是和onCreateView()很像,onViewCreated()是在onCreateView()函数之后执行,我们通常在onViewCreated()函数里面findViewById。
    • setUserVisibleHint():当前页面是否可见(一般ViewPager+Fragemnt配合使用会用到,懒(延时)加载的时候这个函数有大用处),因为ViewPager+Fragemnt的时候是会同时去加载前后多个Fragment的,这个时候就有些Fragment是可见的一些Fragment是不可见的。有一点要注意setUserVisibleHint()只在ViewPager+Fragment这情况下才会回调,其他静态加载和动态加载Fragment不会被调用到。
    • onHiddenChanged():hide()、show()来回切换Fragment显示的时候,Fragment只会回调onHiddenChanged()。Fragment在add()的时候不会回调onHiddenChanged()函数,这点要切记。还有,在ViewPager+Fragment使用的时候Fragment也不会回调onHiddenChanged()函数的。

    四、Fragment的使用

          在Fragment使用之前,有三几个特别重要的类要先来了解下:FragmentManager、FragmentTransaction、FragmentManager.BackStackEntry。

    • FragmentManager:FragmentManager是负责管理Fragment并将它们的视图添加到Activity视图层级结构中的一个管理类。

          FragmentManage可以做那些事情:

    1. 通过 findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或 findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段。
    2. 通过 popBackStack()(模拟用户发出的返回命令)将片段从返回栈中弹出。
    3. 通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。
    4. 开启一个事务。

          FragmentManager相关API函数解释如下:

        /**
         * 开启一个事务,用于对Fragment操作的一系列处理
         */
        public abstract FragmentTransaction beginTransaction();
    
        /**
         * 立即执行挂起的事物FragmentTransaction里面的,commit()、popBackStack()都不是立即执行的,
         * 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行.
         * 但是有时候你希望你的操作是立即执行的,在commit()只会调用该函数。
         */
        public abstract boolean executePendingTransactions();
    
        /**
         * 通过Fragment所在的布局id,查找Fragment.查找规则:
         * 1. 先在add列表里面查找。记住一定是拿最顶上的那个Fragment, (add A、add B. 这个时候就算你把B隐藏了,拿到的还是B).
         * 2. 第一步没找到的情况下,接着就去回退栈里面查找。
         */
        public abstract Fragment findFragmentById(@IdRes int id);
    
        /**
         * 通过Fragment的Tag找到Fragment(添加Fragment的时候会保证tag唯一)
         */
        public abstract Fragment findFragmentByTag(String tag);
    
        /**
         * 弹出堆栈中顶部的Fragment并且显示,类似按下返回键的操作(不是立即执行的,
         * 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行)
         */
        public abstract void popBackStack();
    
        /**
         * 弹出堆栈中顶部的Fragment并且显示(立即执行)
         */
        public abstract boolean popBackStackImmediate();
    
        /**
         * name可以为null或者相对应的BackStackEntry 的名字(在FragmentTransaction的addToBackStack()可以设置该名字),flags只有0和1(POP_BACK_STACK_INCLUSIVE)两种情况
         * 1. 如果name为null,flags为0时,弹出回退栈中最上层的那个fragment。
         * 2. 如果name为null ,flags为1时,弹出回退栈中所有fragment。
         * 3. 如果name不为null,flags为0时,那就会找到这个tag所对应的fragment,弹出该fragment以上的Fragment,
         * 4. 如果name不为null,flag为是1,弹出该fragment(包括该fragment)以上的fragment。
         */
        public abstract void popBackStack(String name, int flags);
    
        /**
         * 同上(唯一的区别就是会立即执行)
         */
        public abstract boolean popBackStackImmediate(String name, int flags);
    
        /**
         * 与popBackStack(String name, int flags)类似,id是BackStackEntry对应的id
         */
        public abstract void popBackStack(int id, int flags);
    
        /**
         * 同上
         */
        public abstract boolean popBackStackImmediate(int id, int flags);
    
        /**
         * 得到回退栈中BackStackEntry的数量
         */
        public abstract int getBackStackEntryCount();
    
        /**
         * 根据序号返回后台堆栈中的BackStackEntry对象(按照添加回退栈的顺序)
         */
        public abstract FragmentManager.BackStackEntry getBackStackEntryAt(int index);
    
        /**
         * 为添加回退堆栈添加一个监听器,用于监听堆栈的改变情况
         */
        public abstract void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener);
    
        /**
         * 移除监听堆栈的监听器
         */
        public abstract void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener);
    
        /**
         * 把一个Fragment对象放入到bundle中, 和getFragment函数对应。比如你可以在onSaveInstanceState()函数中调用该函数
         * 保存一个Fragment
         */
        public abstract void putFragment(Bundle bundle, String key, Fragment fragment);
    
        /**
         * 根据key从bundle中取出之前putFragment()的Fragment。比如你可以在onRestoreInstanceState()函数中调用该函数
         * 恢复之前保存的Fragment
         */
        public abstract Fragment getFragment(Bundle bundle, String key);
    
        /**
         * 得到加入FragmentManager中所有的Fragment(这些Fragment都是通过FragmentTransaction加入)。
         * 不包括回退栈中,以及已经detached或者removed掉的。
         */
        public abstract List<Fragment> getFragments();
    
        /**
         * 保存给定Fragment的当前实例状态,返回值得到的状态可以用Fragment的
         * setInitialSavedState()方法设置给新的Fragment实例, 作为初始状态.
         * 当如这个函数的使用也是有限制的:
         * 1. 保持状态的Fragment必须attach到FragmentManager中。
         * 2. 新创建的Fragment必须和状态对应的Fragment相同的class类型。
         * 3. 保存状态的Fragment不能依赖其他的Fragment,并且不能使用 putFragment(Bundle, String, Fragment)函数
         */
        public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
    
        /**
         * Activity的onDestroy()是否调用
         */
        public abstract boolean isDestroyed();
    
        /**
         * 注册监听FragmentManager中所有Fragment的生命周期
         */
        public abstract void registerFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks cb, boolean recursive);
    
        /**
         * 注销监听FragmentManager中所有Fragment的生命周期
         */
        public abstract void unregisterFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks cb);
    
        /**
         * 返回FragmentManager里面当前活动的主导航Fragment。
         */
        public abstract Fragment getPrimaryNavigationFragment();
    • FragmentTransaction:所有对Fragment的动态操作都是通过FragmentTransaction事务来提交执行。FragmentTransaction是一个事物(事务是在同一时刻执行的一组动作,很像数据库中的事务)。你可以用add(),remove(),replace()等方法构成事务,最后使用commit()方法提交事务。在调用commint()之前,你可以用addToBackStack()把事务添加到一个后退栈中,这个后退栈属于所在的Activity。有了它,就可以在用户按下返回键时,返回到Fragment们执行事务之前的状态。

          FragmentTransaction中对Fragment的操作大致可以分为三类:

    1. 显示操作:add()、 replace()、 show()、 attach()。
    2. 隐藏操作:remove() 、hide() 、detach()。
    3. 添加回退栈:addToBackStack()。

          只能在Activity处于可保存状态的状态时,比如running中,onPause()方法和onStop()方法中提交事务,否则会引发异常。这是因为Fragment的状态会丢失。如果要在可能丢失状态的情况下提交事务,请使用commitAllowingStateLoss()。

          FragmentTransaction相关API函数解释如下:

        /**
         * 添加一个Fragment到FragmentManager中(注意这里没写Fragment要显示在那个id上,所以这个Fragment要显示的时候是看不到的)
         * 同一个Fragment(或者相同的tag)不能多次添加否则会报IllegalStateException
         */
        public abstract FragmentTransaction add(Fragment fragment, String tag);
    
        /**
         * 添加一个Fragment到FragmentManager中(tag = null)
         */
        public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);
    
        /**
         * 添加一个Fragment到FragmentManager中(containerViewId 表示Fragment要放置在在哪个位置)
         */
        public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment, @Nullable String tag);
    
        /**
         * 替换掉指定位置(containerViewId)上所有的Fragment(记住是containerViewId上所有的)
         * containerViewId 上加入了两个Fragment A、B。如果用C来replace掉containerViewId上的Fragment。
         * 那么A,B都会被相当于调用了FragmentTransaction里面的remove()函数。
         */
        public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);
    
        /**
         * 替换掉指定位置(containerViewId)上所有的Fragment
         */
        public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment, @Nullable String tag);
    
        /**
         * 移除掉指定的Fragment
         */
        public abstract FragmentTransaction remove(Fragment fragment);
    
        /**
         * 隐藏指定的Fragment
         */
        public abstract FragmentTransaction hide(Fragment fragment);
    
        /**
         * 显示指定的Fragment(配合hide使用)
         */
        public abstract FragmentTransaction show(Fragment fragment);
    
        /**
         * 会将view与fragment分离,将此将view从view tree中删除(onPause、onStop、onDestroyView)!而且将fragment
         * 从Activity的ADD队列中移除!所以在使用detach()后,使用fragment::isAdded()
         * 返回的值是false;但此fragment实例并不会删除,此fragment的状态依然保持着使用,
         * 所以在fragmentManager中仍然可以找到,即通过FragmentManager::findViewByTag()仍然是会有值的
         */
        public abstract FragmentTransaction detach(Fragment fragment);
    
        /**
         * 显然这个方法与detach()所做的工作相反,它一方面利用fragment的onCreateView()
         * 来重建视图(onCreateView、onActivityCreate、onStart、onResume),一方面将此fragment添加到ADD队列中;这里最值得注意的地方在这里:
         * 由于是将fragment添加到ADD队列,所以只能添加到列队头部,所以attach()操作的结果是,
         * 最新操作的页面始终显示在最前面!
         */
        public abstract FragmentTransaction attach(Fragment fragment);
    
        /**
         * 设置一个当前活动的主导航Fragment。(还没有搞清楚这个东西的作用)
         */
        public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);
    
        /**
         * 当前事务是否有操作
         */
        public abstract boolean isEmpty();
    
        /**
         * 设置进入/退出的动画效果(资源文件)。这个必须位于replace、add、remove之前,否则效果不起作用。
         */
        public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter, @AnimatorRes @AnimRes int exit);
    
        /**
         * 设置进入/退出的动画效果(资源文件)。这个必须位于replace、add、remove之前,否则效果不起作用。
         * 四个参数分别表示:添加、移除、从BackStack中pop出来、进入的动画效果。
         */
        public abstract FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter,
                                                                @AnimatorRes @AnimRes int exit,
                                                                @AnimatorRes @AnimRes int popEnter,
                                                                @AnimatorRes @AnimRes int popExit);
    
        /**
         * Fragment切换时, 有一些元素(element)会保持不变, 使用该函数使这些元素切换时, 赋予动画效果。
         * 关于这部分的内容可以去搜素下Android的共享元素动画,很有意思的一个东西。
         */
        public abstract FragmentTransaction addSharedElement(View sharedElement, String name);
    
        /**
         * 设置切换效果。目前API提供:TRANSIT_NONE、 TRANSIT_FRAGMENT_OPEN、TRANSIT_FRAGMENT_CLOSE三种。
         */
        public abstract FragmentTransaction setTransition(/*@FragmentTransaction.Transit*/ int transit);
    
        /**
         * 设置切换的风格
         */
        public abstract FragmentTransaction setTransitionStyle(@StyleRes int styleRes);
    
        /**
         * 添加commit执行之前的操作到后台堆栈中(对应会生成一个FragmentManager.BackStackEntry对象)
         */
        public abstract FragmentTransaction addToBackStack(@Nullable String name);
    
        /**
         * 是否允许添加到后台堆栈,如果是不允许的状态addToBackStack()会抛异IllegalStateException常
         */
        public abstract boolean isAddToBackStackAllowed();
    
        /**
         * 设置不允许添加后台堆栈
         */
        public abstract FragmentTransaction disallowAddToBackStack();
    
        /**
         * 设置面包屑导航栏的长标题
         * (你可以认为就是保存了一个标题,然后可以通过FragmentManager.BackStackEntry 的getBreadCrumbTitle()
         * 获取到该设置的标题)
         */
        public abstract FragmentTransaction setBreadCrumbTitle(@StringRes int res);
    
        /**
         * 设置面包屑导航栏的长标题(可以看FragmentManager.BackStackEntry里面有对应的获取函数)
         */
        public abstract FragmentTransaction setBreadCrumbTitle(CharSequence text);
    
        /**
         * 设置面包屑导航栏的短标题(可以看FragmentManager.BackStackEntry里面有对应的获取函数)
         */
        public abstract FragmentTransaction setBreadCrumbShortTitle(@StringRes int res);
    
        /**
         * 设置面包屑导航栏的短标题(可以看FragmentManager.BackStackEntry里面有对应的获取函数)
         */
        public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence text);
    
        /**
         * 设置是否优化事务操作的执行,去掉一些冗余的操作,比如这么个情况,两个事务同时执行,一个事务是添加A Fragment,
         * 另一个事务是用B 去 替换掉A。如果做优化,会跳过A直接添加B.
         */
        public abstract FragmentTransaction setReorderingAllowed(boolean reorderingAllowed);
    
        /**
         * 当事务commit的时候,执行指定的Runnable
         */
        public abstract FragmentTransaction runOnCommit(Runnable runnable);
    
        /**
         * 提交事务(commit并不会立即执行的,系统会在适当的时候执行)
         */
        public abstract int commit();
    
        /**
         * 允许保存在存储状态之后 提交事务(Activity 的onSaveInstanceState()只会还可以提交事务)
         */
        public abstract int commitAllowingStateLoss();
    
        /**
         * 立即提交事务(用这个函数提交事务,不能添加到回退栈)
         */
        public abstract void commitNow();
    
        /**
         * 允许保存在存储状态之后 ,立即提交事务(用这个函数提交事务,不能添加到回退栈)
         */
        public abstract void commitNowAllowingStateLoss();
    • FragmentManager.BackStackEntry:可以认为是Fragment back stack中堆栈的一个记录,在我们自己去操作Fragment回退栈的时候会经常用到。
      FragmentManager.BackStackEntry相关API函数解释如下:
        /**
         * 回退栈对应的id(自动分配)
         */
        public int getId();
    
        /**
         * 回退栈对应名字 FragmentTransaction addToBackStack(String)的时候设置
         */
        public String getName();
    
        /**
         * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbTitle(@StringRes int res) 对应
         * FragmentTransaction addToBackStack(String) 之前调用FragmentTransaction setBreadCrumbTitle(@StringRes int res)
         */
        @StringRes
        public int getBreadCrumbTitleRes();
    
        /**
         * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbShortTitle(@StringRes int res) 对应
         */
        @StringRes
        public int getBreadCrumbShortTitleRes();
    
        /**
         * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbTitle(CharSequence text) 对应
         */
        public CharSequence getBreadCrumbTitle();
    
        /**
         * 面包屑对应的名字的资源id,和FragmentTransaction setBreadCrumbShortTitle(CharSequence text) 对应
         */
        public CharSequence getBreadCrumbShortTitle();

    4.1 静态添加Fragment

          静态Fragment使用,这个是最简单的了,就布局文件里面fragment标签,android:name就指定Fragment的路径。
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <fragment
            android:id="@+id/fragment_a"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
        android:name="com.tuacy.example.statics.StaticFragmentA"/>
    
        <fragment
            android:id="@+id/fragment_b"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:name="com.tuacy.example.statics.StaticFragmentB"/>
    
    </LinearLayout>

    4.2 动态添加Fragment

          通过FragmentManager和FragmentTransaction里面的一些函数来动态增删改Fragment。动态Fragment使用的时候,要有一个概念,Fragment要显示肯定是要依托于指定的id(容器)。可以简单的理解为每个id容器都对应一个数组,这个数组里面放的就是对这个id容器上所有的Fragment(FragmentTransaction类的add、show、hide、replace、remove、attach、detach就是对这个数组做相应的增删操作)。决定这个id容器展示给用户的是哪个Fragment就看这个数组最从后往前哪个Fragment是显示(show)的状态。       通过一个实例来瞧一瞧动态Fragment使用(FragmentTransaction类里面的add、show、hide、replace、remove、attach、detach函数的使用)。 add():就相当于往id容器里面添加Fragment,注意一点就够add进去的默认都是shw的状态。比如我们用如下的代码依次添加三个Fragmetn,A、B、C
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            transaction.add(R.id.layout_content_fragment, mFragmentA);
            transaction.add(R.id.layout_content_fragment, mFragmentB);
            transaction.add(R.id.layout_content_fragment, mFragmentC);
            transaction.commit();
    这个时候id容器展示给用户的是C,最上面第一个显示状态的是C。如图所示: ![Fragment add](https://img-blog.csdn.net/20171202090149234?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V5dXhpbmcyNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) hide()、show():就是去改变对应Fragment的显示状态(show or hide)。 ![hide b](https://img-blog.csdn.net/20171202090317822?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V5dXhpbmcyNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) ![hide c](https://img-blog.csdn.net/20171202090401197?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V5dXhpbmcyNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) remove():相当于从数组里面把指定的Fragment给移除掉了。 ![remove](https://img-blog.csdn.net/20171202090434134?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V5dXhpbmcyNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) replace():就是把指定id容器响应的数组清空掉,然后在把replace要放入的Fragment放入进去。
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            transaction.replace(R.id.layout_content_fragment, mFragmentD);
            transaction.commitNow();
    ![replace](https://img-blog.csdn.net/20171202090502797?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V5dXhpbmcyNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast) attach()、detach():detach会从容器数组中把Fragment删除,但是还会记录这个Fragment的一些状态,比如你先把这个Fragment hide掉,然后detach掉了,然后你又attach进来 这个Fragment的状态还是hide的状态。 ![attach detach](https://img-blog.csdn.net/20171202090537607?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd3V5dXhpbmcyNA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)

          FragmentTransaction中每个操作调用对应Fragment的生命周期里面的哪些函数:

    • add():onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()。
    • remove():onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()。
    • show(): onHiddenChanged() - >false。
    • hide(): onHiddenChanged() - >true。
    • attach(): 已经add的前提下detach掉在attach进来的情况 onCreateView()->onActivityCreated()->onStart()->onResume()。
    • detach(): onPause()->onStop()->onDestroyView()。
    • replace():相当于把之前所有的remove掉,在add进来。对应remove和add对Fragment的生命周期的影响。

          动态Fragment在使用过程中还要几个要注意的点:

    • 同一个Fragment的实例对象只能add一次。就算是add到同一个activity里面的不同的id容器里面也只能一次。
    • detach掉的Fragment会保存它detach之前的状态,下次attach进来还是之前的状态。比如你detach之前把Fragment hide隐藏掉了,attach进来还是隐藏的状态。
    • 没有add过的Fragment直接调用attach,和add产生的效果一致。

    4.3 ViewPager + Fragment

          ViewPager + Fragment指的是每个pager都是一个Fraagment。       关于ViewPager + Fragment使用过程中Adapter:FragmentPagerAdapter、FragmentStatePagerAdapter的区别:
    • FragmentPagerAdapter:会将每个生成的Fragment对象一直存留在内存中,所以当有大量的pager显示页时不太适用。
    • FragmentStatePagerAdapter:会销毁setOffscreenPageLimit()函数设置的limit之外的Fragment对象。

          FragmentPagerAdapter、FragmentStatePagerAdapter的选择要根据实际的情况来定,如果pager只有三四页那咱们完全可以使用FragmentPagerAdapter避免每次重新加载的问题。如果pager比较多(比如实现无限加载)咱就选用FragmentStatePagerAdapter。

          关于ViewPager + Fragment使用过程中的Fragment,涉及到Fragment懒加载的问题。懒加载说白了就是延时加载,把加载延时到Fragment对用户可见的时候。增强用户的体验。举一个很直白的例子,有三个pager对应三个Fragment,并且这三个Fragment都要做网络请求。如果不做懒加载的时候三个Fragement就要同时去请求数据。体验就不是那么的好了。懒加载就很好的规避了这个问题,当切换到哪一页的是那个Fragment才去做网络的请求加载数据。

          Fragment懒加载对懒加载的封装,就是对ragment周期函数的灵活使用。最主要的是围绕setUserVisibleHint()函数做各种变通。

          网上很多大神都给出了对Fragment加载的封装:这里列出一个经常用的封装,在里面给加了一些注释,方便大家的。

    /**
     * 懒加载Fragment
     */
    
    public abstract class LazyFragment extends Fragment {
    
        protected Context mContext         = null;
        /**
         * 判断是不是第一次resume
         */
        private   boolean isFirstResume    = true;
        /**
         * 判断是不是第一次可见(只会在setUserVisibleHint中判断和改变)
         */
        private   boolean isFirstVisible   = true;
        /**
         * 判断是不是第一次不可见(只会在setUserVisibleHint中判断和改变)
         */
        private   boolean isFirstInvisible = true;
        /**
         * 标记是否准备加载数据,因为我们不能在setUserVisibleHint马上去加载数据
         * setUserVisibleHint调用的只会,可能视图都还没有加载出来。
         */
        private   boolean isPrepared       = false;
    
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            mContext = context;
        }
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            initPrepare();
        }
    
    
        @Override
        public void onResume() {
            super.onResume();
            if (isFirstResume) {
                isFirstResume = false;
                return;
            }
            /**
             * 这里的情况是为了避免,比如你锁屏之后再解锁,这个时候也是用户可见的情况
             * 并且这种情况是不会调用setUserVisibleHint()函数的
             */
            if (getUserVisibleHint()) {
                onUserVisible();
            }
        }
    
        @Override
        public void onPause() {
            super.onPause();
            /**
             * 这里的情况是为了避免,比如你锁屏之后载解锁
             */
            if (getUserVisibleHint()) {
                onUserInvisible();
            }
        }
    
        /**
         * setUserVisibleHint 函数第一次调用肯定给的是false,第二次才是true
         */
        @Override
        public void setUserVisibleHint(boolean isVisibleToUser) {
            super.setUserVisibleHint(isVisibleToUser);
            if (isVisibleToUser) {
                if (isFirstVisible) {
                    isFirstVisible = false;
                    initPrepare();
                } else {
                    onUserVisible();
                }
            } else {
                if (isFirstInvisible) {
                    isFirstInvisible = false;
                    onFirstUserInvisible();
                } else {
                    onUserInvisible();
                }
            }
        }
    
        private synchronized void initPrepare() {
            if (isPrepared) {
                onFirstUserVisible();
            } else {
                isPrepared = true;
            }
        }
    
        /**
         * 第一次对用户可见的时候调用,在这里懒加载数据
         */
        protected abstract void onFirstUserVisible();
    
        /**
         * 第二次包括第二次对用户可见的时候调用
         */
        protected void onUserVisible() {
    
        }
    
        /**
         * 第一次对用户不可见的时候调用
         */
        protected void onFirstUserInvisible() {
    
        }
    
        /**
         * 第二次包括第二次对用户不可见的时候调用
         */
        protected void onUserInvisible() {
    
        }
    }
    
    

    4.4 Fragment回退栈的使用

          Fragment的回退栈,类似与Android系统为Activity维护一个任务栈。我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

    • 添加进回退栈:FragmentTransaction.addToBackStack(String)
    • 从回退栈中退出:FragmentManager中的popBackStack()、popBackStackImmediate()、getBackStackEntryCount()、getBackStackEntryAt()函数使用。
        /**
         * 弹出堆栈中顶部的Fragment并且显示,类似按下返回键的操作(不是立即执行的,
         * 它会被发送到主线程的任务队列当中去, 当主线程准备好执行它的时候执行)
         */
        public abstract void popBackStack();
    
        /**
         * 弹出堆栈中顶部的Fragment并且显示(立即执行)
         */
        public abstract boolean popBackStackImmediate();
    
        /**
         * name可以为null或者相对应的BackStackEntry 的名字(在FragmentTransaction的addToBackStack()可以设置该名字),flags只有0和1(POP_BACK_STACK_INCLUSIVE)两种情况
         * 1. 如果name为null,flags为0时,弹出回退栈中最上层的那个fragment。
         * 2. 如果name为null ,flags为1时,弹出回退栈中所有fragment。
         * 3. 如果name不为null,flags为0时,那就会找到这个tag所对应的fragment,弹出该fragment以上的Fragment,
         * 4. 如果name不为null,flag为是1,弹出该fragment(包括该fragment)以上的fragment。
         */
        public abstract void popBackStack(String name, int flags);
    
        /**
         * 同上(唯一的区别就是会立即执行)
         */
        public abstract boolean popBackStackImmediate(String name, int flags);
    
        /**
         * 与popBackStack(String name, int flags)类似,id是BackStackEntry对应的id
         */
        public abstract void popBackStack(int id, int flags);
    
        /**
         * 同上
         */
        public abstract boolean popBackStackImmediate(int id, int flags);
    
        /**
         * 得到回退栈中BackStackEntry的数量
         */
        public abstract int getBackStackEntryCount();
    
        /**
         * 根据序号返回后台堆栈中的BackStackEntry对象(按照添加回退栈的顺序)
         */
        public abstract FragmentManager.BackStackEntry getBackStackEntryAt(int index);
    
        /**
         * 为添加回退堆栈添加一个监听器,用于监听堆栈的改变情况
         */
        public abstract void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener);
    
        /**
         * 移除监听堆栈的监听器
         */
        public abstract void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener listener);

    为了方便大家的理解Fragment的回退栈管理,我们给出一个全面的例子。参考下文给的实例中的,回退栈管理。

    五、Fragment参数传递

          在开发过程中经常会向Fragment传递参数,在最初的时候我是通过构造函数传递参数,或者通过放出一个set方法出来传递参数,其实这两种方式是不对的,因为,当一个Fragment一旦由于什么原因(横竖屏切换)导致你的Fragment重新创建的时候,系统会把之前通过set方法设置的参数全部清掉,并且再次调用的是 Fragment中的默认构造函数(是默认构造函数,是没有参数的那个构造函数)根本就不会走到带参数的构造函数。 最正确的方式是通过 setArguments()、getArguments()来进行参数的传递和获取。

    六、Fragment状态保存和恢复

           讲到Fragment的状态保存和恢复,咱就得简单的来理一理Android里面的状态保存和恢复了。

           Android中为什么会存在状态的保存和恢复一说。因为,Android系统中每个应用的内存都比较紧张的,Android展示都是通过Activity(里面有Fragment、View),当Activity退到后台的时候(onStop状态),且长时间不用或前台Activity需要更多资源导致系统必须杀死后台进程回收内存时,系统会销毁你的Activity。这个时候虽然Activity的实例是消失了,但系统会记录它的存在,如果用户下次返回到这个Activity,系统会使用该Activity销毁时保存的状态数据重建一个新的Activity实例。这个系统保存的用来恢复到之前状态的数据叫做“实例状态”,它以键值对的形式保存在Bundle对象中。

           Android中的销毁分两种情况:

    • 正常销毁。正常销毁是指我们用户主动销毁Activity比如back返回,finish的调用,这时该实例(Activity、Fragment、View)将永久的消失,因为这些行为表示该实例不再被需要。
    • 非正常销毁。非正常销毁是指当Activity处于stopped状态且长时间不用时或前台Activity需要更多资源导致系统必须杀死后台进程回收内存时,系统会销毁你的Activity。

    只有非正常销毁的时候才会用到状态的恢复,非正常销毁实际上Activity的实例是消失了,但系统会记录它的存在,这是如果用户回到它,系统会使用该Activity销毁时保存的状态数据重建一个新的Activity实例。这个系统保存的用来恢复到之前状态的数据叫做“实例状态”,它以键值对的形式保存在Bundle对象中。

           Android中的状态的保存和恢复。我们分为两个部分:一个是状态的保存、一个是状态的恢复:

    • 状态保存:状态的保存在退到后台的时候就会触发(不管是正常的退到后台还是非正常的退到后台)。
    • 状态的恢复:状态的恢复一定是在非正常退出的在回来的时候会触发。所以状态保存的次数一定是大于等于状态恢复的次数。

           Android中的状态保存和恢复我们分为三大块:Activity状态保存和恢复、Fragment状态保存和恢复、View状态保存和恢复。其实Activity的状态保存会触发Fragment和View去调用状态保存和恢复。

    注:屏幕旋转被经常用来测试状态的保存和恢复

    6.1 Activity状态保存恢复

           Activity中onSaveInstanceState()用于保存Activity的状态、onRestoreInstanceState()用于恢复Activity保存的状态。当然也可以通过onCreate()函数来恢复保存的状态,onCreate()里面状态恢复的时候必须判断Bundle是否为null,如果为null,则是创建新的Activity,否则重建恢复Activity。如果使用onRestoreInstanceState()方法恢复状态,则不再需要对其中的Bundle进行判null,因为仅当有状态需要被恢复时,该方法才会被回调,且该方法在onStart()方法之后调用。

           Activity状态的保存和恢复着重点在我们自定义的一些数据上。哪些是要保存和恢复心里有个底,比如,你的Activity有一个播放的功能,那视频播放的进度就是要保存和恢复的了。特别,特别要注意。在重写onSaveInstanceState()、onRestoreInstanceState()的时候一定要记得调用super方法。因为,在Android中当Activity的onSaveInstanceState调用的时候,因为在super中Activity会自动收集View层级中每个View的状态。请注意只有在内部实现了View的保存/恢复状态方法的View才会被收集到。一旦onRestoreInstanceState方法被调用,Activity会把收集的数据发送回给View结构树中具有相同android:id配置的View。同时也会触发Activity关联的Fragment的状态的保存和恢复。

    onSaveInstanceState()会在Activity的onPause()方法之后调用、onRestoreInstanceState()方法会在onStart()之后调用。
    Bundle中保存的数据一定要经过序列化,只能保存1M以下的数据。

    6.2 Fragment状态保存和恢复

           Fragment的状态保存和恢复和Activity产生的时机是一样的,Activity的状态保存和恢复会触发该Activity关联的Fragment的状态保存和恢复。Fragment中在onSaveInstanceState()中保存状态,在onCreateView()、onActivityCreated()、onViewCreated()任何一个函数中恢复数据都可以。

           Fragment的使用稍稍复杂一点,Fragment要依托容器来显示,可能这个容器里面放置了好几个Fragment,这些Fragment的状态保存和恢复都是一样的。在Activity有状态保存和恢复的时候,都是会触发这个容器里面所有的Fragment的状态保存和恢复。

           当Fragment的使用过程中使用了addToBackStack()的时候,在状态保存和恢复的时候这个也是会完全恢复的。屏幕旋转之后返回的时候还是能返回上一次保存的Fragment状态的。

    setRetainInstance()函数的作用,将该Fragment转变为“持久化Fragment”,这意味着只有该Fragment的GUI(从onViewCreated()方法返回的View)会被销毁重建(就是Frament第一次创建之后,当由于内存不足要重复创建的时候不会调用onCreate()和onDestroy()函数。其他的函数还是会调用的)而所有其他的引用变量仍然保持。

    Dialog弹窗的时候也是推荐使用DialogFragment来替换,你想下比如有这么一种情况,当你弹出了Dialog的时候这个时候进行屏幕的切换,我肯定希望Dialog还存在吧而且Dialog中的输入框里面之前输入的字符还存在。DialogFragment就能完美的解决这一问题。

    6.3 View状态保存和恢复

           在开发过程中自定义View是我们经常干的事情。在自定义View的时候 就要时刻注意View状态保存恢复,Android中原生的View已经帮我们处理好了,我们不用管。我们就就管我们自定义的View。

    注意:为了让Activity在状态保存和恢复的过程中能触发调用View的状态保存和恢复,我们一定要在Activity的布局文件中给这个View id android:id,否则View的状态保存和恢复函数不会调用

           下面给出一个通用自定义View状态保存恢复的模板。

        @Nullable
        @Override
        protected Parcelable onSaveInstanceState() {
            Bundle bundle = new Bundle();
            bundle.putParcelable(BUNDLE_SUPER, super.onSaveInstanceState());
            /**
             * 这里通过bundle放置自定义的要保存的一些状态
             */
            return bundle;
        }
    
        @Override
        protected void onRestoreInstanceState(Parcelable state) {
            Bundle bundle = (Bundle) state;
            super.onRestoreInstanceState(bundle.getParcelable(BUNDLE_SUPER));
            /**
             * 这里通过bundle得到我们要恢复的一些状态
             */
        }

    本文涉及到的所有相关实例(可能参考价值不是特别的大):实例DEMO

    如发现文章的错误欢迎大家指正提出
    展开全文
  • Android组件之Fragment详解

    千次阅读 2018-05-17 11:59:39
    Fragment详解 Fragment是Android3.0开始引入的组件,主要是为了便于大屏UI的设计和实现。它有自己的生命周期,但是它的生命周期会收到加载Fragment的Activity的生命周期的约束。在开始讲解前,老规矩,首先提出问题...
  • Fragment 的两种创建方式

    千次阅读 2018-02-27 17:15:00
    一、fragment的静态创建步骤:(前提:写好自己的Fragment类,见上篇文章)在要用到fragment的Activity所对应的XML文件中添加fragment控件并为其添加name属性(android:name="包名.Fragment类名")和id...
  • Fragment

    千次阅读 2018-06-05 21:50:58
    1.Fragment是什么? fragment它的中文意思:碎片; 一个可以将activity拆分成几个完全独立封装的可重用的组件,每个组件有自己的生命周期和ui布局。 2.Fragment静态加载怎么用 静态加载 1、继承Fragment,...
  • Fragment - 用法+demo

    千次阅读 2017-03-02 16:29:21
    Fragment详解和参考文章
  • 安卓开发 Fragment的简单使用与解析

    万次阅读 2020-04-28 15:56:56
    1.Fragment的简介: Fragment是Android3.0以后引入的新的api,为了适配大屏的平板。 在普通手机开发的过程中,使用Fragment能实现一个界面的多次使用,能加快效率。Fragment可以被认为是Activity界面的一个布局,...
  • fragment里面嵌套另一个fragment

    万次阅读 2018-05-05 22:50:45
    最近换了工作,说白了,就是去填坑啦,首页改版,原来是viewpager+标签切换滑动实现,现在改成popupwindow+recyclerview实现,为了减少工时,我在原来的基础上复用了fragment,即需要实现在一个fragment里面嵌入另一...
  • 至于fragment的用法,fragment+viewpager的滑动切换代码请参考博文 Android基础:翻页控件Viewpager的使用方法,viewpager+fragment混合使用 fragment与Activity 当在使用activity嵌套fragment,...
  • Android在Fragment中嵌套(添加)Fragment

    千次阅读 2016-07-08 18:34:08
    import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; ParameterFragment parameterFragment = new Para
  • 我的Activity中有几个fragment。我想要在这个Activity中访问在其中一个fragment创建的函数 Fragment: public class RandomFragment extends Fragment { public void randomfuntion (){ } } ...
  • 做布局时,常遇到多重frament嵌套会丢失数据,这是什么原因呢?这篇文章,将会告诉你解决的办法。
  • 现在的android主界面,普遍离不开FragmentActivity和Fragment。也经常会碰到这么一个问题,从其他界面跳回主界面时,无法分辨出回到了哪个fragment里面。一般我们会在fragment·的onResume 方法里面做一些处理,如果...
  • 我做了个类似于微博布局的demo。但是运行登陆界面时,fragment就已经加载了。我希望实现的是:fragment,只在点击该fragmeng进去才开始加载数据,不点击就不要加载,同时第二次点击...。希望各位大虾们提供思路。
  • Activity得到当前fragment实例

    万次阅读 2015-11-11 17:43:55
    许多童鞋会用到fragment,利用activity去管理,而由于可能涉及到动态加载fragment或者利用java反射机制得到新的fragment去填充布局,导致很多时候我们需要去获取得到当前fragment实例。例如下例:调用switchContent...
  • 安卓在Activity中获取Fragment中的控件

    万次阅读 热门讨论 2016-05-13 13:31:03
    安卓在Activity中获取Fragment中的控件,这个需求很少用到,但在做项目的过程中,遇到了。其实也很简单: ``` 首先在Activity中实例化Fragment ExampleFragment exFragment = (Fragment)getFragmentManager()....
  • 优雅的让Fragment监听返回键

    万次阅读 多人点赞 2014-10-27 16:27:47
    Fragment是不能得到物理返回键的监听的,
  • Fragment的startActivityForResult详细解决方案

    万次阅读 热门讨论 2014-10-30 12:03:58
    由于要用到Fragment中startActivityForResult,所以一开始就直接用activity.startActivityForResult(intent, 0);发现这样用首先会跳转到FragmentActivity中的onActivityResult。 在这个的时候,确实可以处理...
1 2 3 4 5 ... 20
收藏数 186,596
精华内容 74,638
关键字:

fragment