【Android】學(xué)習(xí)Fragment

前言:除了安卓四大組件(activityservice、content providerbroadcast receiver)之外,還有一個(gè)最重要的知識(shí)點(diǎn)是:fragment。

1. Fragment 是什么?

Fragment 也叫碎片,片段(相當(dāng)于 迷你的 Activity ,或者是 Activity 的模塊化的組件)。是 Google 在 Android 3.0 引入的,主要為了給大屏幕的 UI 提供支持的。

實(shí)際開(kāi)發(fā)中兩個(gè)作用:

  • 相當(dāng)于海報(bào)的功能,可以到處粘貼。就是把一部分業(yè)務(wù)邏輯和 UI 封裝在一起,方便靈活的用在各個(gè)地方。
  • 適配 Pad 和手機(jī)。只用一套代碼適配,方便重用,提高程序的復(fù)用性和可維護(hù)性。

Fragment 可以理解為一個(gè)迷你的 Activity 或者是 Activity 的模塊化的組件,它有自己的生命周期與顯示界面,我們可以利用多個(gè) Fragment 嵌套在 Activity 達(dá)到以下的功能,如適配平板,或適配橫豎屏幕,或者在程序運(yùn)行的過(guò)程中動(dòng)態(tài)的更改我們的 UI 界面。如下圖:


上圖是顯示應(yīng)用運(yùn)行在手機(jī)情況下,從一個(gè)列表頁(yè)面跳轉(zhuǎn)到詳細(xì)頁(yè)面的例子。如果我們不使用 Fragment 的情況,當(dāng)應(yīng)用運(yùn)行在平板上面的情況,就只能顯示放大版的手機(jī)顯示的界面,為了利用平板的特性,Android 在 3.0 版本中提供了Fragment 技術(shù),我們就可以將列表與內(nèi)容以組件的方式插入,在手機(jī)上分屏顯示兩個(gè)內(nèi)容,在平板上面就可以左右顯示兩個(gè)內(nèi)容。

你可以將片段視為 Activity 的模塊化組成部分,它具有自己的生命周期,能接收自己的輸入事件,并且你可以在 Activity 運(yùn)行時(shí)添加或刪除片段(有點(diǎn)像你可以在不同 Activity 中重復(fù)使用的“子 Activity ”)。

片段必須始終嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影響。

2. 如何使用 Fragment

2.1 創(chuàng)建 Fragment

如果需要使用 Fragment 的話,需繼承 Fragment 類,目前有兩個(gè) Fragment 類:

  • import android.support.v4.app.Fragment 提供的向下兼容類(支持3.0以下版本)
  • import android.app.Fragment 提供3.0以上版本的類(只支持3.0以上的版本)

繼承 Fragment 至少需實(shí)現(xiàn)以下方法:

  • onCreateView() 系統(tǒng)會(huì)在片段首次繪制其用戶界面時(shí)調(diào)用此方法,并將創(chuàng)建的UI界面返回。
import android.support.v4.app.Fragment;

public class FirstFragment extends Fragment {
    @Nullable
    @Override
    /* 系統(tǒng)會(huì)在片段首次繪制其用戶界面時(shí)調(diào)用此方法,并將創(chuàng)建的UI界面返回 */
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        /* 返回一個(gè)view,把布局返回去 */
        View view = inflater.inflate(R.layout.fragment_first, null);
        
        return view;
    }
}

2.2 使用 Fragment

方式1:在 Activity 的布局文件中添加一個(gè) Fragment 控件

<!-- 使用 XML 添加 fragment 子模塊 -->
<fragment android:name="com.example.testfragment.FirstFragment"
    android:id="@+id/abc"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

android:name 屬性指定要在布局中實(shí)例化的 Fragment 類。注意如果是在 xml 中聲明的 Fragment 的話,必須給這個(gè) Fragment 設(shè)置 id 或者 tag。

方式2:動(dòng)態(tài)添加一個(gè) Fragment 到 Activity 指定的控件內(nèi)部

FirstFragment firstFragment = new FirstFragment();

// 獲取到低版本兼容的 FragmentManager( getFragmentManager(); 支持3.0以上版本 )
FragmentManager fm = getSupportFragmentManager();

// 1.開(kāi)啟一個(gè) Fragment 事務(wù)(即可對(duì) Fragment 進(jìn)行操作)
FragmentTransaction transaction = fm.beginTransaction();

// 2.添加一個(gè) Fragment 到 Activity 指定的控件內(nèi)部(每調(diào)用一次 add 方法,就添加一次,會(huì)重復(fù)疊加)
// (參數(shù):布局容器 FrameLayout 的 ID,fragment 對(duì)象,動(dòng)態(tài)添加時(shí)需要設(shè)置一個(gè) id/tag)
transaction.add(R.id.content, firstFragment, "abc");

// 3.提交(對(duì) Fragment 進(jìn)行任何操作都必須提交)
transaction.commit();

2.3 傳值給 Fragment

  • 通過(guò)Fragment的方法、或者回調(diào)進(jìn)行傳值
  • 給 Fragment 傳遞參數(shù) setArguments
// 頁(yè)面之間的傳值:Bundle是傳遞數(shù)據(jù)的集合
Bundle bundle = new Bundle();
bundle.putString("title", "Fragment的標(biāo)題");
// bundle.putCharSequence("title", title);
// 通過(guò) Bundle 把參數(shù)從 Activity 中傳給 Fragment
firstFragment.setArguments(bundle);
  • 在 Fragment 中接收參數(shù) getArguments
/* 接收傳遞的參數(shù) */
Bundle bundle = getArguments();
String title = bundle.getString("title");

3. Fragment 的生命周期


創(chuàng)建的生命周期:

  • onAttach:Fragment 開(kāi)始與 Activity 關(guān)聯(lián)。
  • onCreate:系統(tǒng)會(huì)在創(chuàng)建片段時(shí)調(diào)用此方法。你應(yīng)該在實(shí)現(xiàn)內(nèi)初始化您想在片段暫?;蛲V购蠡謴?fù)時(shí)保留的必需片段組件。
  • onCreateView:系統(tǒng)會(huì)在片段首次繪制其用戶界面時(shí)調(diào)用此方法。 要想為你的片段繪制 UI,你從此方法中返回的 View 必須是片段布局的根視圖。如果片段未提供 UI,你可以返回 null。
  • onActivityCreated:Activity onCreate 完成的回調(diào)。
  • onSaveInstanceState:移除的生命周期,處理 Fragment 需要保存數(shù)據(jù)的方法。
  • onDestroyView:將 Fragment 的 View 試圖從UI中移除。
  • onDetach:將 Fragment 與 Activity 取消關(guān)聯(lián)。

4. Fragment 的操作 FragmentTransaction

我們可以使用 FragmentTransaction 來(lái)對(duì) Fragment 進(jìn)行操作,如 add、replace、attach、detach、remove、show、hide。

4.1 add() 方法

作用:將一個(gè) Fragment 添加到一個(gè)頁(yè)面,可以重復(fù)疊加
add(int containerViewId, Fragment fragment, String tag)

  • containerViewId:將 Fragment 加入的 ViewGroup 的 id
  • fragment:被操作的 Fragment
  • tag:被操作的 Fragment 的標(biāo)示,我們操作成功后可以使用這個(gè) tag 找到相應(yīng)的 Fragment
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.content, firstFragment, "abc");
transaction.commit();

4.2 replace() 方法

作用:將容器內(nèi)的 Fragemnt 移除后,再進(jìn)行添加
replace(int containerViewId, Fragment fragment, String tag)

  • containerViewId:將 Fragment 替換的 ViewGroup 的 id
  • fragment:被操作的 Fragment
  • tag:被操作的 Fragment 的標(biāo)示,我們操作成功后可以使用這個(gè) tag 找到相應(yīng)的 Fragment
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.content, firstFragment, "abc");
transaction.commit();

4.3 detach() 方法

作用:將一個(gè) Fragment 從 UI 上面解綁

 Fragment fragment =  fm.findFragmentByTag("abc");
 FragmentTransaction transaction = fm.beginTransaction();
 transaction.detach(fragment);
  • 注意解綁并不是刪除,我們還可以通過(guò)其他方法把解綁的Fragment重新綁定到UI上面。

4.4 attach() 方法

作用:將一個(gè) Fragment 重新綁定到 UI

FragmentManager fm =  getSupportFragmentManager();
Fragment fragment =  fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.attach(fragment);
transaction.commit();

4.5 remove() 方法

作用:將一個(gè) Fragment 從 Activity 刪除

FragmentManager fm =  getSupportFragmentManager();
Fragment fragment =  fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.remove(fragment);
transaction.commit();

4.6 show() 方法

作用:顯示一個(gè) Fragment(不會(huì)調(diào)用任何生命周期方法,常用這個(gè))

FragmentManager fm =  getSupportFragmentManager();
Fragment fragment =  fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.show(fragment);
transaction.commit(); 

4.7 hide() 方法

作用:隱藏一個(gè) Fragment(不會(huì)調(diào)用任何生命周期方法,常用這個(gè))

FragmentManager fm =  getSupportFragmentManager();
Fragment fragment =  fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.hide(fragment);
transaction.commit(); 

4.8 commit() 和 commitAllowingStateLoss() 方法的區(qū)別

Activity 在以下的操作下容易引起回收并觸發(fā) onSaveInstanceState()

  • 按下 HOME 鍵
  • 按下電源按鍵(關(guān)閉屏幕顯示)時(shí)
  • 屏幕方向切換
  • Activity 跳轉(zhuǎn)的時(shí)候

以上幾種情況下不能進(jìn)行狀態(tài)的提交,如果提交的話會(huì)出現(xiàn)以下錯(cuò)誤
Can not perform this action after onSaveInstanceState

出現(xiàn)這個(gè)錯(cuò)誤的時(shí)候,我們有兩個(gè)解決方法:

  • 不在 onSaveInstanceState() 后調(diào)用 commit() 方法
  • 使用 commitAllowingStateLoss() 方法

為了防止在 onSaveInstanceState 方法中調(diào)用 commit 會(huì)報(bào)錯(cuò),我們使用 commitAllowingStateLoss,可以防止?fàn)顟B(tài)丟失報(bào)錯(cuò)!

5. Fragment 與返回鍵

默認(rèn)情況下,F(xiàn)ragment 是不會(huì)響應(yīng)返回鍵的。如果需要做到類似Activity回退到上一個(gè)界面這樣的效果,必須將 FragmentTransaction 加入返回棧。

FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.content, firstFragment, "abc" + index);
// 加入返回任務(wù)棧,可通過(guò)系統(tǒng)返回按鈕控制返回
transaction.addToBackStack(null); // 任務(wù)名可為空,也可通過(guò)任務(wù)名控制多層返回

addToBackStack(String name);
name 參數(shù)代表這次 FragmentTransion 的名稱,我們可以根據(jù)這 個(gè)名稱找到相應(yīng)的操作,并進(jìn)行回退動(dòng)作。也可以傳null,代表不記錄該次操作的名稱。

如果我們將某次操作加入回退棧的話,我們有以下幾種方式進(jìn)行回退:

  • 使用返回鍵系統(tǒng)自動(dòng)回退到上一次操作前的狀態(tài)。
  • 使用 popBackStack 回退到指定的操作的狀態(tài)。

popBackStack() 回退到上一次操作前的狀態(tài) 。
popBackStack(String name, int flags) 回退到某次 name 的操作狀態(tài)。
flag 為 0 表示回退到任務(wù) name 的操作狀態(tài)的這一步。
flag 為 POP_BACK_STACK_INCLUSIVE 表示回退到任務(wù) name 的操作狀態(tài)的上一步。
popBackStack(int id, int flags) 回退某個(gè) id 的操作狀態(tài),id 為 commit() 返回的。

@Override
public void onBackPressed() {
    Log.i("11", "點(diǎn)擊了系統(tǒng)返回按鈕");
    super.onBackPressed();

    FragmentManager fm = getSupportFragmentManager();
    /* 返回到上一頁(yè) */
    // fm.popBackStack();
    /* 返回到指定頁(yè)(參數(shù):加入返回任務(wù)棧的任務(wù)名, 標(biāo)記) */
    fm.popBackStack("abc", 0);
}

6. Fragment 動(dòng)畫(huà)

默認(rèn)情況下 Fragment 顯示和隱藏是不顯示動(dòng)畫(huà)的,不過(guò) FragmentTransaction 提供了三種顯示的動(dòng)畫(huà)的方式:

  • setTransition:使用系統(tǒng)提供的默認(rèn)顯示動(dòng)畫(huà)
  • setCustomAnimations:使用自定義動(dòng)畫(huà)。
  • setTransition (int transit):可以使用系統(tǒng)提供的默認(rèn)動(dòng)畫(huà),可供選擇的有 TRANSIT_NONE,TRANSIT_FRAGMENT_OPEN,or TRANSIT_FRAGMENT_CLOSE
  • setCustomAnimations (int enter, int exit, int popenter, int popexit):能夠使用自定義動(dòng)畫(huà)。
    enter 設(shè)置Fragment 進(jìn)入動(dòng)畫(huà)
    exit 設(shè)置Fragment 退出動(dòng)畫(huà)
    popenter popback 后回滾狀態(tài)后上一個(gè) Fragment 的動(dòng)畫(huà)
    popexit popback 后回滾狀態(tài)后當(dāng)前的Fragment的動(dòng)畫(huà)

7. Fragment 間的交互(通信)

使用回調(diào)的方式,讓 FragmentActivity 充當(dāng)中間交互的橋梁。
先定義一個(gè)接口(一種規(guī)范,這里相當(dāng)于 FragmentActivity 的基類,方便多個(gè)不同的 Activity 使用),再在 FragmentActivity 中實(shí)現(xiàn)接口中定義的方法。
1》定義一個(gè)接口(OneOnClickListener.java)

package net.cbi360.testfragment;

public interface OneOnClickListener {
    public void OneOnClick(int index);
}

2》實(shí)現(xiàn)接口的方法

public class CommonActivity extends FragmentActivity implements OneOnClickListener {
    TwoFragment two;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_common);

        FragmentManager fm = getSupportFragmentManager();
        OneFragment one = (OneFragment)fm.findFragmentByTag("one");
        one.setOneOnClickListener(this);
        two = (TwoFragment)fm.findFragmentByTag("two");
    }

    public void OneOnClick(int index) {
        String msg = "點(diǎn)擊了" + index + "按鈕";
        Log.i("CommonActivity", msg);
        two.setMessage(msg);
    }
}
public class OneFragment extends Fragment {
    OneOnClickListener activity;
    int index = 0;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (null != savedInstanceState) {
            // 恢復(fù)參數(shù)的值
            index = savedInstanceState.getInt("index");
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_one, null);
        Button button = (Button)view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Log.i("OneFragment", "點(diǎn)擊了按鈕");
                index++;
                activity.OneOnClick(index);
            }
        });
        return view;
    }

//    public void setActivity(CommonActivity activity) {
//        this.activity = activity;
//
//    }

    // 解耦(減少關(guān)聯(lián)性,方便不同activity重用)
    public void setOneOnClickListener(OneOnClickListener activity) {
        this.activity = activity;

    }

//    @Override
//    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
//        super.onActivityCreated(savedInstanceState);
//        // 也可以從生命周期方法中拿到 Activity
//        activity = (OneOnClickListener)getActivity();
//    }


    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        // 保存參數(shù)的值
        outState.putInt("index", index);
    }
}
  • 解決頁(yè)面翻轉(zhuǎn)后,數(shù)據(jù)丟失問(wèn)題
/* 這個(gè)方法的作用是:當(dāng)頁(yè)面的狀態(tài)/參數(shù)值丟失時(shí)我們來(lái)進(jìn)行保存(緩存頁(yè)面參數(shù)的值) */
@Override
// 當(dāng)頁(yè)面翻轉(zhuǎn)時(shí)/按下Home鍵時(shí)/按下電源鍵時(shí)/Activity跳轉(zhuǎn)時(shí),會(huì)觸發(fā)下面這個(gè)方法
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);

    // 保存值
    outState.putInt("index", index);
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // 獲取保存的值(注意:先判斷一下是否有上次緩存的信息,有的話再獲?。?    if (null != savedInstanceState) {
        index = savedInstanceState.getInt("index");
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容