Android開發之漫漫長途 XIII——Fragment最佳實務

來源:互聯網
上載者:User

標籤:表示   his   height   blog   hub   protect   .sh   tap   調用   

該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關知識,另外也借鑒了其他的優質部落格,在此向各位大神表示感謝,膜拜!!!

前言

上一篇文章中詳細分析了Fragment相關知識,那麼作為“小Activity”,Fragment能做什麼呢,如何使用Fragment得到最佳實務呢。Fragment的設計最初也許是為了大螢幕平板裝置的需求,不過現在Fragment已經廣泛運用到我們普通的手機裝置上。是我們幾乎在主流App中都能發現的一個功能。

熟悉Android的朋友一定都會知道,很簡單嘛,使用TabHost就OK了!但是殊不知,TabHost並非是那麼的簡單,它的可擴充性非常的差,不能隨意地定製Tab項顯示的內容,而且運行還要依賴於ActivityGroup。ActivityGroup原本主要是用於為每一個TabHost的子項管理一個單獨的Activity,但目前已經被廢棄了。為什麼呢?當然就是因為Fragment的出現了!

好了,,下面我就來實現的效果,不過在開始之前,首先你必須已經瞭解Fragment的用法了,如果你對Fragment還比較陌生的話,建議先去閱讀我前面的一篇文章Android開發之漫漫長途 XII——Fragment詳解

先建立宿主Activity

建立BestFragmentActivity

public class BestFragmentActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_best_fragment);            //下面是LuseenBottomNavigation的使用    BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigation);    BottomNavigationItem bottomNavigationItem = new BottomNavigationItem            ("首頁", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_account_balance_white_48dp);    BottomNavigationItem bottomNavigationItem1 = new BottomNavigationItem            ("分類", ContextCompat.getColor(this, R.color.secondColor), R.mipmap.ic_list_white_48dp);    BottomNavigationItem bottomNavigationItem2 = new BottomNavigationItem            ("任務", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_add_circle_outline_white_48dp);    BottomNavigationItem bottomNavigationItem3 = new BottomNavigationItem            ("購物車", ContextCompat.getColor(this, R.color.thirdColor), R.mipmap.ic_add_shopping_cart_white_48dp);    BottomNavigationItem bottomNavigationItem4 = new BottomNavigationItem            ("我的", ContextCompat.getColor(this, R.color.colorAccent), R.mipmap.ic_account_box_white_48dp);    bottomNavigationView.addTab(bottomNavigationItem);    bottomNavigationView.addTab(bottomNavigationItem1);    bottomNavigationView.addTab(bottomNavigationItem2);    bottomNavigationView.addTab(bottomNavigationItem3);    bottomNavigationView.addTab(bottomNavigationItem4);    }}

對應的布局檔案activity_best_fragment

<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:id="@+id/main_content"    android:fitsSystemWindows="true"    >    <!--Fragment之後就動態放在該布局檔案下-->    <FrameLayout        android:id="@+id/frame_content"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:scrollbars="none"        android:layout_above="@+id/bottomNavigation"        />    <!--關於底層布局我這裡使用了Github上的開源項目-->    <com.luseen.luseenbottomnavigation.BottomNavigation.BottomNavigationView        android:id="@+id/bottomNavigation"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        app:bnv_colored_background="false"        app:bnv_with_text="true"        app:bnv_shadow="false"        app:bnv_tablet="false"        app:bnv_viewpager_slide="true"        app:bnv_active_color="@color/colorPrimary"        app:bnv_active_text_size="@dimen/bottom_navigation_text_size_active"        app:bnv_inactive_text_size="@dimen/bottom_navigation_text_size_inactive"/></RelativeLayout>

關於底層布局我這裡使用了Github上的開源項目LuseenBottomNavigation,該項目地址是https://github.com/armcha/LuseenBottomNavigation讀者可自行查看

接著建立Fragment

目前Fragment作為示範使用,可以看到布局內容都非常簡單,我這裡只給出其中一個Fragment的建立過程和源碼,項目完整源碼可見文末的源碼地址。

我們就拿第一個GoodsFragment舉例把

public class GoodsFragment extends Fragment {    private static String TAG= GoodsFragment.class.getSimpleName();    @Override    public void onAttach(Context context) {        super.onAttach(context);        Log.d(TAG,"onAttach");    }    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        Log.d(TAG,"onCreateView");        View view = inflater.inflate(R.layout.fragment_goods, null);        return view;    }    @Override    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        Log.d(TAG,"onViewCreated");    }    @Override    public void onActivityCreated(@Nullable Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        Log.d(TAG,"onActivityCreated");    }    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG,"onCreate");    }    @Override    public void onStart() {        super.onStart();        Log.d(TAG,"onStart");    }    @Override    public void onResume() {        super.onResume();        Log.d(TAG,"onResume");    }    @Override    public void onPause() {        super.onPause();        Log.d(TAG,"onPause");    }    @Override    public void onStop() {        super.onStop();        Log.d(TAG,"onStop");    }    @Override    public void onDestroyView() {        super.onDestroyView();        Log.d(TAG,"onDestroyView");    }    @Override    public void onDestroy() {        super.onDestroy();        Log.d(TAG,"onDestroy");    }    @Override    public void onDetach() {        super.onDetach();        Log.d(TAG,"onDetach");    }}

源碼非常的簡單,在onCreateView中載入布局檔案,該布局檔案也非常簡單,僅僅定義了一個幀布局,在幀布局中包含了一個TextView

<?xml version="1.0" encoding="utf-8"?><FrameLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Goods"        android:textStyle="bold"        android:textSize="30sp"        android:layout_gravity="center"/></FrameLayout>

按照上面的流程我們建立了所需的Fragment,接著該更改BestFragmentActivity的代碼,更改後的源碼如下

public class BestFragmentActivity extends AppCompatActivity{    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_best_fragment);        //底部瀏覽版面配置        BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigation);        BottomNavigationItem bottomNavigationItem = new BottomNavigationItem                ("首頁", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_account_balance_white_48dp);        BottomNavigationItem bottomNavigationItem1 = new BottomNavigationItem                ("分類", ContextCompat.getColor(this, R.color.secondColor), R.mipmap.ic_list_white_48dp);        BottomNavigationItem bottomNavigationItem2 = new BottomNavigationItem                ("任務", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_add_circle_outline_white_48dp);        BottomNavigationItem bottomNavigationItem3 = new BottomNavigationItem                ("購物車", ContextCompat.getColor(this, R.color.thirdColor), R.mipmap.ic_add_shopping_cart_white_48dp);        BottomNavigationItem bottomNavigationItem4 = new BottomNavigationItem                ("我的", ContextCompat.getColor(this, R.color.colorAccent), R.mipmap.ic_account_box_white_48dp);        bottomNavigationView.addTab(bottomNavigationItem);        bottomNavigationView.addTab(bottomNavigationItem1);        bottomNavigationView.addTab(bottomNavigationItem2);        bottomNavigationView.addTab(bottomNavigationItem3);        bottomNavigationView.addTab(bottomNavigationItem4);        //為底部瀏覽版面配置設定點擊事件        bottomNavigationView.setOnBottomNavigationItemClickListener(new OnBottomNavigationItemClickListener() {            @Override            public void onNavigationItemClick(int i) {                switch (i){                    case 0:                        switchToHome();                        break;                    case 1:                        switchToCategory();                        break;                    case 2:                        switchToTask();                        break;                    case 3:                        switchToGoodCar();                        break;                    case 4:                        switchToAbout();                        break;                }            }        });                //初始載入首頁,即GoodsFragment        switchToHome();    }        private void switchToAbout() {        getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new AboutFragment(),AboutFragment.class.getName()).commit();    }    private void switchToCategory() {        getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new CategoryFragment(),CategoryFragment.class.getName()).commit();    }    private void switchToTask() {        getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new TaskFragment(),TaskFragment.class.getName()).commit();    }    private void switchToGoodCar() {        getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new GoodCarFragment(),GoodCarFragment.class.getName()).commit();    }    private void switchToHome() {        getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new GoodsFragment(),GoodsFragment.class.getName()).commit();    }}

上面的代碼可以根據上一篇文章比較容易的寫出來,而且正常運行,可是在實際開發過程中我們不得不考慮代碼的效能問題。其實上面的代碼存在效能問題,尤其是在底部導航這種情境中,Fragment之間的來回切換,這裡使用的replace方法。關於這個方法帶來的問題以及如何進行最佳化,將在下一節詳細說明。

Fragment 效能最佳化問題FragmentTransaction

談到Fragment的效能最佳化問題,就不得不對FragmentTransaction進行深入的研究以及探討,上面使用了getSupportFragmentManager().beginTransaction()得到了FragmentTransaction對象,並依次調用其replace方法和commit方法。

  • replace(int containerViewId, Fragment fragment)、replace(int containerViewId, Fragment fragment, String tag)

該方法的作用是,類似於先remove掉視圖容器所有的Fragment,再add方法參數中的fragment,並為該Fragment設定標籤tag。

    getSupportFragmentManager().beginTransaction().add(R.id.frame_content,new AboutFragment(),AboutFragment.class.getName()).commit();    getSupportFragmentManager().beginTransaction().add(R.id.frame_content,new CategoryFragment(),CategoryFragment.class.getName()).commit();    getSupportFragmentManager().beginTransaction().add(R.id.frame_content,new TaskFragment(),TaskFragment.class.getName()).commit();    getSupportFragmentManager().beginTransaction().replace(R.id.frame_content,new GoodsFragment(),GoodsFragment.class.getName()).commit();

如上面所示代碼塊中,我們先進行了3次添加操作,之後的replace操作會移出前面添加的Fragment,再添加方法參數中指定的Frament。

  • add(int containerViewId, Fragment fragment, String tag)、 remove(Fragment fragment)
    FragmentTransaction的Add()操作是維持著一個隊列的,在這個隊列中,根據ADD進去的先後順序形成了一個鏈表,我們上面的操作在這個列表中的形式變化如所示:

  • remove(Fragment fragment) : 移除一個已經存在的Fragment.

  • show(Fragment fragment): 顯示一個以前被隱藏過的Fragment

  • hide(Fragment fragment) : 隱藏一個存在的Fragment

    註:①Fragment被hide/show,僅僅是隱藏/顯示Fragment的視圖,不會有任何生命週期方法的調用。

    ②在Fragment中重寫onHiddenChanged方法可以對Fragment的hide和show狀態進行監聽。

還有一些其他的方法這裡就不一一列舉了,有了上面所列出的方法,我們就能對Fragment有個很不錯的最佳化了。

Fragment效能問題分析與解決Fragment效能問題分析

我們上面是使用replace來切換頁面,那麼在每次切換的時候,Fragment都會重新執行個體化,重新載入一邊資料,這樣非常消耗效能和使用者的資料流量。這是因為replace操作,每次都會把container中的現有的fragment執行個體清空,然後再把指定的fragment添加進去,就就造成了在切換到以前的fragment時,就會重新執行個體會fragment。

Fragment效能問題解決

知道了問題的根源所在,那麼解決的辦法也呼之欲出了。我們不能使用replace來進行頁面的切換,那麼可使用的方法貌似只有add了,我們可以在載入的時候判斷Fragment是不是已經被添加到隊列中,如果已添加,我們就顯示(show)該Fragment,隱藏(hide)其他,如果沒有添加過呢,就添加。這樣就能做到多個Fragment切換不重新執行個體化。具體到代碼中就是這樣的

public class BestFragmentActivity extends AppCompatActivity{        //當前的Fragment    private Fragment mCurFragment = new Fragment();    //初始化其他的Fragment    private GoodsFragment mGoodsFragment = new GoodsFragment();    private GoodCarFragment mGoodCarFragment = new GoodCarFragment();    private TaskFragment mTaskFragment = new TaskFragment();    private AboutFragment mAboutFragment  = new AboutFragment();    private CategoryFragment mCategoryFragment  = new CategoryFragment();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_best_fragment);        BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigation);        BottomNavigationItem bottomNavigationItem = new BottomNavigationItem                ("首頁", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_account_balance_white_48dp);        BottomNavigationItem bottomNavigationItem1 = new BottomNavigationItem                ("分類", ContextCompat.getColor(this, R.color.secondColor), R.mipmap.ic_list_white_48dp);        BottomNavigationItem bottomNavigationItem2 = new BottomNavigationItem                ("任務", ContextCompat.getColor(this, R.color.firstColor), R.mipmap.ic_add_circle_outline_white_48dp);        BottomNavigationItem bottomNavigationItem3 = new BottomNavigationItem                ("購物車", ContextCompat.getColor(this, R.color.thirdColor), R.mipmap.ic_add_shopping_cart_white_48dp);        BottomNavigationItem bottomNavigationItem4 = new BottomNavigationItem                ("我的", ContextCompat.getColor(this, R.color.colorAccent), R.mipmap.ic_account_box_white_48dp);        bottomNavigationView.addTab(bottomNavigationItem);        bottomNavigationView.addTab(bottomNavigationItem1);        bottomNavigationView.addTab(bottomNavigationItem2);        bottomNavigationView.addTab(bottomNavigationItem3);        bottomNavigationView.addTab(bottomNavigationItem4);        bottomNavigationView.setOnBottomNavigationItemClickListener(new OnBottomNavigationItemClickListener() {            @Override            public void onNavigationItemClick(int i) {                switch (i){                    case 0:                        switchToHome();                        break;                    case 1:                        switchToCategory();                        break;                    case 2:                        switchToTask();                        break;                    case 3:                        switchToGoodCar();                        break;                    case 4:                        switchToAbout();                        break;                }            }        });        switchToHome();    }   private void switchFragment(Fragment targetFragment){        FragmentTransaction transaction = getSupportFragmentManager()                .beginTransaction();        if (!targetFragment.isAdded()) {//如果要顯示的targetFragment沒有添加過            transaction                    .hide(mCurFragment)//隱藏當前Fragment                    .add(R.id.frame_content, targetFragment,targetFragment.getClass().getName())//添加targetFragment                    .commit();        } else {//如果要顯示的targetFragment已經添加過            transaction//隱藏當前Fragment                    .hide(mCurFragment)                    .show(targetFragment)//顯示targetFragment                    .commit();        }        //更新當前Fragment為targetFragment        mCurFragment = targetFragment;    }    private void switchToAbout() {        switchFragment(mAboutFragment);    }    private void switchToCategory() {        switchFragment(mCategoryFragment);    }    private void switchToTask() {        switchFragment(mTaskFragment);    }    private void switchToGoodCar() {        switchFragment(mGoodCarFragment);    }    private void switchToHome() {        switchFragment(mGoodsFragment);    }}

這樣就達到了我們的目的,我們在來回切換的操作中,Fragment只執行個體一次,少了銷毀又重新建立等帶來的效能消耗,另我們想要在Fragment中更新資料時,我們可以在自訂Fragment中重寫其onHiddenChanged方法

@Overridepublic void onHiddenChanged(boolean hidden) {    super.onHiddenChanged(hidden);    if (hidden){       //Fragment隱藏時調用    }else {        //Fragment顯示時調用    }}
源碼地址:源碼傳送門本篇總結

我們在本篇部落格中比較詳細的給出了一個Fragment的最佳實務,我們在許多主流App中都能看到這種頂部、底部導航的效果,並且在此基礎上我們探討了使用Fragment不當的存在效能問題及最佳化。

下篇預告

下篇打算往Fragment中加點東西,ListView

此致,敬禮

Android開發之漫漫長途 XIII——Fragment最佳實務

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.