1. 程式人生 > >Android之Fragment回退棧詳解

Android之Fragment回退棧詳解

前言:本文將結合開發中的實際需求,來講解一下Fragment中的回退棧 對於Activity,當按返回鍵時,能夠返回到上一個Activity,但是,當我們Fragment到Activity中時,如果不做任何處理,當按返回鍵時,當前Fragment都會全部退出,如果想要擁有Activity逐漸退出的效果,我們需要應用一下Fragment中的回退棧.

視訊地址:
程式碼地址:

案例效果

這裡寫圖片描述這裡寫圖片描述

案例描述

大家可以自行開啟京東,你會發現,如果你點選了分類,發現,購物車,我的,按鈕,再按返回鍵的話,會先回到首頁,然後再退出應用.這裡應用的就是Fragment的回退棧功能.,下面我將帶領大家瞭解一下回退棧的實現邏輯

方法介紹

  • addToBackStack(tag); 將Fragment新增到回退棧中
  • popBackStack(); 清除回退棧中棧頂的Fragment
  • popBackStack(String tag, int i );
    • 如果i=0,回退到該tag所對應的Fragment層
    • 如果i=FragmentManager.POP_BACK_STACK_INCLUSIVE,回退到該tag所對應的Fragment的上一層
  • popBackStackImmediate 立即清除回退棧中棧頂Fragment
  • getBackStackEntryCount(); 獲取回退棧中Fragment的個數
  • getBackStackEntryAt(int index) 獲取回退棧中該索引值下的Fragment

逐層退出回退棧效果程式碼實現

佈局程式碼,在佈局中,寫了一個FrameLayout,用來放置Fragment的容器;寫了一個RadioGroup,用來放置下邊的幾個指示按鈕

<LinearLayout 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:orientation="vertical" >
<FrameLayout android:id="@+id/framelayout" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <RadioGroup android:id="@+id/radioGroup" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ff0000" android:orientation="horizontal" > <RadioButton android:id="@+id/rb_home" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_weight="1" android:button="@drawable/home_selector" android:checked="true" android:gravity="center_horizontal" /> <RadioButton android:id="@+id/rb_cart" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:button="@drawable/cart_selector" /> <RadioButton android:id="@+id/rb_category" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:button="@drawable/category_selector" /> <RadioButton android:id="@+id/rb_personal" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:button="@drawable/personal_selector" /> </RadioGroup> </LinearLayout> </LinearLayout>

MainActivity中的程式碼

  • 初始化,在onCreate方法中
    • 初始化控制元件,並設定監聽
    • 新增一個HomeFragment
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        // 獲取Fragment管理者
        fragmentManager = getSupportFragmentManager();
        // 先預設新增fragment1
        addFragment(new Fragment1(), "fragment1");
    }
  • 初始化控制元件給RadioButton設定監聽
private void initView() {
        radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
        rb_cart = (RadioButton) findViewById(R.id.rb_cart);
        rb_category = (RadioButton) findViewById(R.id.rb_category);
        rb_home = (RadioButton) findViewById(R.id.rb_home);
        rb_personal = (RadioButton) findViewById(R.id.rb_personal);
        // 對每一個RadioButton都設定點選事件,注意,在這裡並沒有對radioGroup設定checkChangeListener
        rb_cart.setOnClickListener(this);
        rb_category.setOnClickListener(this);
        rb_home.setOnClickListener(this);
        rb_personal.setOnClickListener(this);

    }

*點選按鈕時,替換Fragment

/**
     * 根據點選的按鈕---依次替換Fragment
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.rb_home:
            addFragment(new Fragment1(), "fragment1");
            break;
        case R.id.rb_cart:
            addFragment(new Fragment2(), "fragment2");
            break;
        case R.id.rb_category:
            addFragment(new Fragment3(), "fragment3");
            break;
        case R.id.rb_personal:
            addFragment(new Fragment4(), "fragment4");
            break;
        default:
            break;
        }
    }
  • 新增Fragment的時候,同時將Fragment放到回退棧中
public void addFragment(Fragment fragment, String tag) {
        // 開啟事務
        FragmentTransaction beginTransaction = fragmentManager
                .beginTransaction();
        // 執行事務,新增Fragment
        beginTransaction.add(R.id.framelayout, fragment, tag);
        // 新增到回退棧,並定義標記
        beginTransaction.addToBackStack(tag);
        // 提交事務
        beginTransaction.commit();

    }
  • 監聽Activity中的返回鍵,判斷當前回退棧中的Fragment個數,如果回退棧中有大於一個,就一個個清除Fragment,如果只剩一個,說明只剩首頁Fragment所對應的Fragment,就finish();
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // 判斷當前按鍵是返回鍵
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            // 獲取當前回退棧中的Fragment個數
            int backStackEntryCount = fragmentManager.getBackStackEntryCount();
            // 判斷當前回退棧中的fragment個數,
            if (backStackEntryCount > 1) {
                // 立即回退一步
                fragmentManager.popBackStackImmediate();
                // 獲取當前退到了哪一個Fragment上,重新獲取當前的Fragment回退棧中的個數
                BackStackEntry backStack = fragmentManager
                        .getBackStackEntryAt(fragmentManager
                                .getBackStackEntryCount() - 1);
                // 獲取當前棧頂的Fragment的標記值
                String tag = backStack.getName();
                // 判斷當前是哪一個標記
                if ("fragment1".equals(tag)) {
                    // 設定首頁選中
                    rb_home.setChecked(true);
                } else if ("fragment2".equals(tag)) {
                    // 設定購物車的tag
                    rb_cart.setChecked(true);
                } else if ("fragment3".equals(tag)) {
                    rb_category.setChecked(true);
                } else if ("fragment4".equals(tag)) {
                    rb_personal.setChecked(true);
                }
            } else {
                //回退棧中只剩一個時,退出應用
                finish();
            }
        }
        return true;
    }

}

退出所有隻剩首頁而Fragment的程式碼

其他程式碼和上邊一致,只需要修改一下退出的邏輯.需要判斷當前回退棧中有多少個Fragment,使用While迴圈逐個退出.

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // 判斷當前按鍵是返回鍵
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            // 獲取當前回退棧中的Fragment個數
            int backStackEntryCount = fragmentManager.getBackStackEntryCount();
            // 回退棧中至少有多個fragment,棧底部是首頁
            if (backStackEntryCount > 1) {
                // 如果回退棧中Fragment個數大於一.一直退出
                while (fragmentManager.getBackStackEntryCount() > 1) {
                    fragmentManager.popBackStackImmediate();
                    //選中第一個介面
                    rb_home.setChecked(true);
                }
            } else {
                finish();
            }

        }
        return true;
    }
}