Fragment的優雅實踐並雜談細節
雖然Fragment設計的初衷是為了大螢幕的平板裝置,但走著走著它目前已經廣泛應用於我們的手機裝置上了。比如下面我們常見的設計就可以使用fragment實現,先來個圖片三連:

program.png

read.png

manageMoney.png
對於這個設計有很多種方式實現,比如可以使用TabLayout+Fragment,不過我想快速且優雅的實現,所以最終藉助了一個第三方庫實現下面的佈局Table,採用了BottomNavigation+Fragment的方式。
也可以直接
implementation 'com.github.armcha:LuseenBottomNavigation:1.8.2'
那麼下面就開始本文的重點內容了
一、主頁面佈局
activity_main.xml
<?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:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:id="@+id/fr_content" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout> <com.luseen.luseenbottomnavigation.BottomNavigation.BottomNavigationView android:id="@+id/bnv_item" android:layout_width="match_parent" android:layout_height="30dp" app:bnv_colored_background="false" android:layout_alignParentBottom="true"> </com.luseen.luseenbottomnavigation.BottomNavigation.BottomNavigationView> </RelativeLayout>
主頁面的佈局很簡潔,FrameLayout就是展示fragment內容的地方,下面的BottomNavigationView就是底部導航欄控制元件。
二、實現底部導航欄佈局效果
public class MainActivity extends AppCompatActivity { @BindView(R.id.fr_content) FrameLayout frContent; @BindView(R.id.bnv_item) BottomNavigationView mBottomView; //... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); BottomNavigationItem bottomNavigationItem=new BottomNavigationItem("首頁", ContextCompat.getColor(this,R.color.mainPager),R.drawable.loudspeaker); BottomNavigationItem bottomNavigationItem1=new BottomNavigationItem("程式設計", ContextCompat.getColor(this,R.color.programCode),R.drawable.program); BottomNavigationItem bottomNavigationItem2=new BottomNavigationItem("讀書", ContextCompat.getColor(this,R.color.readPager),R.drawable.book); BottomNavigationItem bottomNavigationItem3=new BottomNavigationItem("理財", ContextCompat.getColor(this,R.color.manageMoneyPager),R.drawable.money); mBottomView.addTab(bottomNavigationItem); mBottomView.addTab(bottomNavigationItem1); mBottomView.addTab(bottomNavigationItem2); mBottomView.addTab(bottomNavigationItem3); //... }
程式碼很清晰,建立多個底部的每個item例項,再將其加入到view中。
這裡我做了一下怪[啊哈哈],使用了ButterKnife來獲取控制元件,你可以不用,按照最原始的方式即可。到這裡已經完成了一小半了,夠快吧....
三、準備一個Fragment
這裡我們建立了4個fragment,分別是首頁、程式設計、讀書和理財。我就拿理財來舉例了(最近迷上了理財這個東西),其他三個都是一樣的,或許這就是傳說中的舉一反三吧。
ManageMoneyFragment.class
public class ManageMoneyFragment extends Fragment { private static final String TAG = "ManageMoneyFragment"; @Override public void onAttach(Context context) { Log.d(TAG, "onAttach: "); super.onAttach(context); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreateView: "); return inflater.inflate(R.layout.manage_money_fragment,null); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onViewCreated: "); super.onViewCreated(view, savedInstanceState); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { Log.d(TAG, "onActivityCreated: "); super.onActivityCreated(savedInstanceState); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreate: "); super.onCreate(savedInstanceState); } @Override public void onStart() { Log.d(TAG, "onStart: "); super.onStart(); } @Override public void onResume() { Log.d(TAG, "onResume: "); super.onResume(); } @Override public void onPause() { Log.d(TAG, "onPause: "); super.onPause(); } @Override public void onStop() { Log.d(TAG, "onStop: "); super.onStop(); } @Override public void onDestroy() { Log.d(TAG, "onDestroy: "); super.onDestroy(); } @Override public void onDestroyView() { Log.d(TAG, "onDestroyView: "); super.onDestroyView(); } @Override public void onDetach() { Log.d(TAG, "onDetach: "); super.onDetach(); } }
什麼鬼,寫了這麼長一串感覺啥也沒幹啊,這不都是activity的生命週期方法嘛。哈哈,準確的這些都是Fragment的生命週期方法,寫這些是為了下面小談一些它的生命週期。
其實Fragment可以算Android裡的第五大元件了,之前,有人把View作為第五大元件,但是由於相對於四大元件來說它沒有生命週期,所以從這個角度來說,Fragment更適合稱為第五大元件。
建立一個fragment到使用者能看到的狀態經歷了以下的生命週期

建立fragment.png
同樣銷燬一個fragment會經歷如下生命週期

銷燬fragment.png
或許這張圖看起來會更清晰明瞭一點

fragment生命週期.png
相比於Activity,它的生命週期方法多了幾個,比如它最開始的方法走的是onAttach,銷燬的最後一步是onDetach方法,它們是一對方法因為fragment是需要依附Activity存在的,所以就有了這樣的兩個方法。
好,可以回來了,在上面程式碼中和我們的頁面view直接相關的是 onCreateView方法,用inflater.inflate方法把fragment的頁面佈局填充進去就好了
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.d(TAG, "onCreateView: "); return inflater.inflate(R.layout.manage_money_fragment,null); }
隨便寫了一個簡單的佈局
R.layout.manage_money_fragment
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/frag_manage_money" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="35sp" android:layout_centerInParent="true" android:text="在別人貪婪的時候,我們要恐懼; 在別人恐懼的時候,我們要貪婪;"/> </RelativeLayout>
四、依附到Activity中
這是最後一步也是最關鍵的一步,fragment是需要依附到Activity中的。
在底部的導航欄view中,是有個點選方法的,通過這個點選方法我們對每個item進行選擇,從而切換到相應的fragment
public class MainActivity extends AppCompatActivity { //當前的fragment private Fragment mCurrFragment=new Fragment(); //理財的fragment private ManageMoneyFragment mManageMoneyFragment=new ManageMoneyFragment(); //程式設計的fragment private ProgramFragment mProgramFragment=new ProgramFragment(); //閱讀的fragment private ReadFragment mReadFragment=new ReadFragment(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //....省略上面已展示的 //點選底部按鈕選擇對應的fragment mBottomView.setOnBottomNavigationItemClickListener(new OnBottomNavigationItemClickListener() { @Override public void onNavigationItemClick(int index) { switch (index){ case 0: skipToHome(); break; case 1: skipToProgram(); break; case 2: skipToRead(); break; case 3: skipToManage(); break; default: break; } } }); } //跳轉到理財fragment private void skipToManage() { switchFragment(mManageMoneyFragment); } //.....
因為在新增導航欄的item時,它的內部實現是ArrayList,所以只需要判斷它的index就可以獲取對應的item了。
選擇完後,進行fragment的展示。這裡我們使用的hide和show的方式,對於沒有新增到ArrayList的,需要先新增再展示,對於已經新增過的我們直接展示就好了,注意在展示之前要將當前展示的先隱藏。
對於隱藏這樣的方式就有個好處,我們不需要重複建立它的例項,節省了不必要消耗的效能和使用者的流量。
private void switchFragment(Fragment targetFragment){ FragmentTransaction fragmentTransaction=getSupportFragmentManager().beginTransaction(); //隱藏目前的fragment,展示目標fragment if(!targetFragment.isAdded()){ fragmentTransaction.hide(mCurrFragment) .add(R.id.fr_content,targetFragment,targetFragment.getClass().getName()) .commit(); }else { fragmentTransaction.hide(mCurrFragment) .show(targetFragment) .commit(); } mCurrFragment=targetFragment; }
到這裡,就實現了筆者最開始的素質三連圖的最後一張了。對於其他兩張圖我們只需要再新增兩個fragment,再依附到Activity中就ok了.整個過程還是很優雅的哈[蜜汁微笑QAQ]