《Android進階》之第五篇 Fragment 的使用


http://blog.csdn.net/lmj623565791/article/details/37970961
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,將相應的布局文件和Fragment綁定,代碼編寫控制邏輯

2、主布局文件里面添加已經控制邏輯的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/id_fragment_title"
        android:name="com.example.androidexpriment.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.example.androidexpriment.ContentFragment"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</RelativeLayout>

是不是把Fragment當成普通的View一樣聲明在Activity的布局文件中,然后所有控件的事件處理等代碼都由各自的Fragment去處理,瞬間覺得Activity好干凈有木有~~代碼的可讀性、復用性以及可維護性是不是瞬間提升了。

4、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。

5、動態的使用Fragment

上面已經演示了,最簡單的使用Fragment的方式~下面介紹如何動態的添加、更新、以及刪除Fragment

activity_main.xml

<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" 
    android:background="#ccc444">

    <FrameLayout
        android:id="@+id/id_content"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
    </FrameLayout>

</RelativeLayout>

MainActivity.java

package com.example.androidexpriment;

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按鈕的控制邏輯放在fragment中執行

下面是FragmentOne

fragment_one.xml

 

<?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:background="#ffffff"
    android:orientation="vertical" >

    <Button
        android:id="@+id/id_fragment_one_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="3dp"
        android:layout_gravity="center"/>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#333333"
        android:textSize="20sp" />

</LinearLayout>
View Code

 

FragmentOne.java

package com.example.androidexpriment;

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得到第一個界面的時候不見了。

插曲:replace()方法可以通過remove,add結合達到相同的效果。

    tx.remove(fm.findFragmentById(R.layout.fragment_one));//error
//    tx.remove(getTargetFragment());//error
    tx.remove(fm.findFragmentByTag("ONE"));//right
    tx.add(R.id.id_content,fTwo);

使用前兩種方法都會報空指針的錯誤。

java.lang.NullPointerException: Attempt to write to field 'int android.app.Fragment.mNextAnim' on a null object reference

一開始想通過查找源碼的方式解決,在BackStackRecord.java中發現幾種操作方法,mNextAnim的賦值主要在

f.mNextAnim = op.enterAnim;
f.mNextAnim = op.exitAnim;
 1  public void run() {
 2         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Run: " + this);
 3 
 4         if (mAddToBackStack) {
 5             if (mIndex < 0) {
 6                 throw new IllegalStateException("addToBackStack() called after commit()");
 7             }
 8         }
 9 
10         bumpBackStackNesting(1);
11 
12         Op op = mHead;
13         while (op != null) {
14             switch (op.cmd) {
15                 case OP_ADD: {
16                     Fragment f = op.fragment;
17                     f.mNextAnim = op.enterAnim;
18                     mManager.addFragment(f, false);
19                 } break;
20                 case OP_REPLACE: {
21                     Fragment f = op.fragment;
22                     if (mManager.mAdded != null) {
23                         for (int i=0; i<mManager.mAdded.size(); i++) {
24                             Fragment old = mManager.mAdded.get(i);
25                             if (FragmentManagerImpl.DEBUG) Log.v(TAG,
26                                     "OP_REPLACE: adding=" + f + " old=" + old);
27                             if (f == null || old.mContainerId == f.mContainerId) {
28                                 if (old == f) {
29                                     op.fragment = f = null;
30                                 } else {
31                                     if (op.removed == null) {
32                                         op.removed = new ArrayList<Fragment>();
33                                     }
34                                     op.removed.add(old);
35                                     old.mNextAnim = op.exitAnim;
36                                     if (mAddToBackStack) {
37                                         old.mBackStackNesting += 1;
38                                         if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
39                                                 + old + " to " + old.mBackStackNesting);
40                                     }
41                                     mManager.removeFragment(old, mTransition, mTransitionStyle);
42                                 }
43                             }
44                         }
45                     }
46                     if (f != null) {
47                         f.mNextAnim = op.enterAnim;
48                         mManager.addFragment(f, false);
49                     }
50                 } break;
51                 case OP_REMOVE: {
52                     Fragment f = op.fragment;
53                     f.mNextAnim = op.exitAnim;
54                     mManager.removeFragment(f, mTransition, mTransitionStyle);
55                 } break;
56                 case OP_HIDE: {
57                     Fragment f = op.fragment;
58                     f.mNextAnim = op.exitAnim;
59                     mManager.hideFragment(f, mTransition, mTransitionStyle);
60                 } break;
61                 case OP_SHOW: {
62                     Fragment f = op.fragment;
63                     f.mNextAnim = op.enterAnim;
64                     mManager.showFragment(f, mTransition, mTransitionStyle);
65                 } break;
66                 case OP_DETACH: {
67                     Fragment f = op.fragment;
68                     f.mNextAnim = op.exitAnim;
69                     mManager.detachFragment(f, mTransition, mTransitionStyle);
70                 } break;
71                 case OP_ATTACH: {
72                     Fragment f = op.fragment;
73                     f.mNextAnim = op.enterAnim;
74                     mManager.attachFragment(f, mTransition, mTransitionStyle);
75                 } break;
76                 default: {
77                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
78                 }
79             }
80 
81             op = op.next;
82         }
83 
84         mManager.moveToState(mManager.mCurState, mTransition,
85                 mTransitionStyle, true);
86 
87         if (mAddToBackStack) {
88             mManager.addBackStackState(this);
89         }
90     }

依照報錯信息查找——>FragmentManager.execPendingActions 1454

注意源碼中的FragmentManager是個抽象類,真正發揮作用的是FragmentManagerImpl

final class FragmentManagerImpl extends FragmentManager

 

在Fragment中,FragmentManager fm= getFragmentManager();

在源碼中

 FragmentManagerImpl mFragmentManager;

final public FragmentManager getFragmentManager() { return mFragmentManager; }

同樣FragmentTransaction也是個抽象類

final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, Runnable {

}

FragmentTransaction tx= fm.beginTransaction();其實源碼中是這樣的

@Override
    public FragmentTransaction beginTransaction() {
        return new BackStackRecord(this);
    }

為什么

f.mNextAnim = NULL

源碼扯的很深,報錯其實很好理解,你只有曾經add過的Fragment,你才可以remove。而在主Activity     tx.add(R.id.id_content, new FragmentOne(),"ONE");,通過布局文件找fragment肯定是不可取的,因為一個布局可以例化出多個fragmetn,所以為了能找到創建的Fragment,推薦加上Tag。

接下來FragmentTwo

布局文件:

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

    <Button 
        android:id="@+id/id_fragment_two_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="20sp"
        android:textStyle="bold" />

</LinearLayout>
View Code

 

package com.example.androidexpriment;



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了:

package com.example.androidexpriment;



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通信以后,會重構上面的代碼!

2、Fragment與Activity通信

因為所有的Fragment都是依附於Activity的,所以通信起來並不復雜,大概歸納為:

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

b、如果Activity中未保存任何Fragment的引用,那么沒關系,每個Fragment都有一個唯一的TAG或者ID,可以通過getFragmentManager.findFragmentByTag()獲得任何Fragment實例,然后進行操作。

c、在Fragment中可以通過getActivity得到當前綁定的Activity的實例,然后進行操作。

注:如果在Fragment中需要Context,可以通過調用getActivity(),如果該Context需要在Activity被銷毀后還存在,則使用getActivity().getApplicationContext()。

3、Fragment與Activity通信的最佳實踐

因為要考慮Fragment的重復使用,所以必須降低Fragment與Activity的耦合,而且Fragment更不應該直接操作別的Fragment,畢竟Fragment操作應該由它的管理者Activity來決定。

下面我通過兩種方式的代碼,分別重構,FragmentOne和FragmentTwo的點擊事件,以及Activity對點擊事件的響應:

首先看FragmentOne

package com.example.androidexpriment;
 
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

package com.example.androidexpriment;

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 :

package com.example.androidexpriment;

import com.example.androidexpriment.FragmentOne.FOneBtnClickListener;
import com.example.androidexpriment.FragmentTwo.FTwoBtnClickListener;

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 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把實現邏輯都寫死啦,顯然這種代碼就沒什么復用價值,而第二種只是在Fragment了定義了一種抽象的操作,即可以響應點擊事件,至於點擊事件是什么,在主activity里面決定。

上面兩種通信方式都是值得推薦的,隨便選擇一種自己喜歡的。Activity擔任的是Fragment間類似總線一樣的角色,應當由它決定Fragment如何操作。另外雖然Fragment不能響應Intent打開,但是Activity可以,Activity可以接收Intent,然后根據參數判斷顯示哪個Fragment。

package com.example.androidexpriment;

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;
    public interface FOneBtnClickListener{
        void onFOneBtnClick();
    }
    
    
    //得到該Fragment對應的Activity
    @Override
    public void onClick(View v) {
        if(getActivity() instanceof FOneBtnClickListener) {
            ((FOneBtnClickListener)getActivity()).onFOneBtnClick();
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_one,null);
            mbtn= (Button) (view).findViewById(R.id.id_fragment_one_btn);
            mbtn.setOnClickListener(this);
            return view;
    }
    
}*/


public class FragmentOne extends Fragment implements OnClickListener{

    private Button mbtn;
    private FOneBtnClickListener foneBtnClickListener;
    
    public interface FOneBtnClickListener{
        void onFOneBtnClick();
    }
    
    public void setFoneBtnClickListenr(FOneBtnClickListener f){
        foneBtnClickListener = f;
    }
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        foneBtnClickListener.onFOneBtnClick();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view  = inflater.inflate(R.layout.fragment_one, null);
        mbtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
        mbtn.setOnClickListener(this);
        return view;
    }
    
}
4、如何處理運行時配置發生變化

運行時配置發生變化,最常見的就是屏幕發生旋轉,如果你不知道如何處理屏幕變化可以參考:Android 屏幕旋轉 處理 AsyncTask 和 ProgressDialog 的最佳方案

這里提一下:很多人覺得強制設置屏幕的方向就可以了,但是有一點,當你的應用被至於后台(例如用戶點擊了home),長時間沒有返回的時候,你的應用也會被重新啟動。比如上例:如果你把上面的例子你至於FragmentThree界面,然后處於后台狀態,長時間后你會發現當你再次通過home打開時,上面FragmentThree與FragmentOne疊加在一起,這就是因為你的Activity重新啟動,在原來的FragmentThree上又繪制了一個FragmentOne。

好了,下面看一段代碼:

Activity:

package com.example.androidexpriment;

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 FragmentOne mFOne;  
    private static final String TAG = "FragmentOne";
    @Override  
    protected void onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState); 
        Log.e(TAG, savedInstanceState+"");
        requestWindowFeature(Window.FEATURE_NO_TITLE);  
        setContentView(R.layout.activity_main);  
    /*  if(savedInstanceState == null) {
          mFOne = new FragmentOne();  
          FragmentManager fm = getFragmentManager();  
          FragmentTransaction tx = fm.beginTransaction();  
          tx.add(R.id.id_content, mFOne, "ONE");  
          tx.commit();  
      }*/
       
         mFOne = new FragmentOne();  
          FragmentManager fm = getFragmentManager();  
          FragmentTransaction tx = fm.beginTransaction();  
          tx.add(R.id.id_content, mFOne, "ONE");  
          tx.commit();  
    }  
  
}  

Fragment

package com.example.androidexpriment;

import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
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  
{  
    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");  
    }  
  
}  

很簡單的代碼,當你運行之后,不斷的旋轉屏幕,你會發現每旋轉一次屏幕,后台log會打印出兩套生命周期的回調。相當與有兩個Fragment


這是為什么呢,因為當屏幕發生旋轉,Activity發生重新啟動,默認的Activity中的Fragment也會跟着Activity重新創建;這樣造成當旋轉的時候,本身存在的Fragment會重新啟動,然后當執行Activity的onCreate時,又會再次實例化一個新的Fragment,這就是出現的原因。兩個

 

那么如何解決呢:

其實通過檢查onCreate的參數Bundle savedInstanceState就可以判斷,當前是否發生Activity的重新創建,代碼上面已經有啦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM