在上一篇文章《Android Fragment用法詳解(1)--靜態使用Fragment》我們講解了Fragment的最簡單的用法。這次我們來說一說Fragment復雜一丟丟的用法。在代碼中動態添加Fragment,讓其實現類似微信主頁面效果。也就是點擊底部的按鈕來動態改變中間內容頁面。我們先來看看效果圖吧。說明一下,為了方便大家復制粘貼,里面沒有任何圖片素材,都是用顏色和安卓自帶圖片來現實效果,所以有點難看哈~~畢竟我們是程序員不是美工嘛!
接下來就是源代碼啦
先看MainActivity的布局文件吧。因為布局文件是關鍵哦。
<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" > <!-- 把Fragment當作普通控件使用,這里沒啥作用,就是作為一個標題欄,不用Fragment,直接寫布局一樣的 --> <fragment android:id="@+id/fg_title" android:name="com.example.fragmentdemo.TitleFragment" android:layout_width="match_parent" android:layout_height="45dp" /> <!-- 用一個幀布局來占一個位置,目的是給fragment用 --> <FrameLayout android:id="@+id/fl_content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" > </FrameLayout> <!-- 底部三個按鈕,我們用RadioGroup來實現 --> <include layout="@layout/bar" /> </LinearLayout>
然后看看底部四個按鈕的布局吧
<?xml version="1.0" encoding="utf-8"?> <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:id="@+id/rg" android:orientation="horizontal" > <!-- android:paddingLeft="0dp"加這個是為了讓內容能夠劇中 --> <RadioButton android:id="@+id/rb_1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@drawable/bar_selector" android:button="@null" android:drawableTop="@android:drawable/star_on" android:gravity="center" android:paddingLeft="0dp" android:text="Frag_1" /> <RadioButton android:id="@+id/rb_2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@drawable/bar_selector" android:button="@null" android:drawableTop="@android:drawable/star_on" android:gravity="center" android:paddingLeft="0dp" android:text="Frag_2" /> <RadioButton android:id="@+id/rb_3" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="@drawable/bar_selector" android:button="@null" android:drawableTop="@android:drawable/star_on" android:gravity="center" android:paddingLeft="0dp" android:text="Frag_3" /> </RadioGroup>
然后就是三個按鈕的選擇器(就是控制點擊底部按鈕的時候,底部按鈕能夠變化背景顏色的選擇器)
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_checked="true" android:drawable="@android:color/holo_red_light" ></item> <item android:state_checked="false" android:drawable="@android:color/holo_blue_bright"></item> </selector>
接下來就是先上一些無關緊要的文件,哈哈。標題欄的類文件和布局文件
先看布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="45dp" android:gravity="center" android:background="#00ff00" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Fragment制作的標題欄" android:textColor="@android:color/black" android:textSize="18sp" /> </RelativeLayout>
然后是類文件
package com.example.fragmentdemo; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** *************************************************************** * * @版權 LinFeng * * @作者 LinFeng * * @版本 1.0 * * @創建日期 2016-6-6 * * @功能描述 用Fragment制作標題欄 ***************************************************************** */ public class TitleFragment extends Fragment{ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { /** * 加載布局文件然后返回view,顯示在Activity */ View view = inflater.inflate(R.layout.title, container,false); return view; } }
然后就是重量級別的MainActivity類文件了
package com.example.fragmentdemo; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.RadioButton; import android.widget.RadioGroup; public class MainActivity extends FragmentActivity implements OnClickListener { private RadioButton rb1; private RadioButton rb2; private RadioButton rb3; private RadioGroup rGroup; private Fragment f1,f2,f3; private FragmentManager manager; private FragmentTransaction transaction; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); /** * 拿到事務管理器並開啟事務 */ manager = getSupportFragmentManager(); transaction = manager.beginTransaction(); /** * 初始化按鈕 */ rb1 = (RadioButton) findViewById(R.id.rb_1); rb2 = (RadioButton) findViewById(R.id.rb_2); rb3 = (RadioButton) findViewById(R.id.rb_3); rGroup = (RadioGroup) findViewById(R.id.rg); /** * 為三個按鈕添加監聽 */ rb1.setOnClickListener(this); rb2.setOnClickListener(this); rb3.setOnClickListener(this); /** * 啟動默認選中第一個 */ rGroup.check(R.id.rb_1); f1 = new Fragment1(); transaction.replace(R.id.fl_content, f1); transaction.commit(); } @Override public void onClick(View v) { manager = getSupportFragmentManager(); transaction = manager.beginTransaction(); switch (v.getId()) { case R.id.rb_1 : /** * 為了防止重疊,需要點擊之前先移除其他Fragment */ hideFragment(transaction); f1 = new Fragment1(); transaction.replace(R.id.fl_content, f1); transaction.commit(); break; case R.id.rb_2 : hideFragment(transaction); f2 = new Fragment2(); transaction.replace(R.id.fl_content, f2); transaction.commit(); break; case R.id.rb_3 : hideFragment(transaction); f3 = new Fragment3(); transaction.replace(R.id.fl_content, f3); transaction.commit(); break; default : break; } } /* * 去除(隱藏)所有的Fragment * */ private void hideFragment(FragmentTransaction transaction) { if (f1 != null) { //transaction.hide(f1);隱藏方法也可以實現同樣的效果,不過我一般使用去除 transaction.remove(f1); } if (f2 != null) { //transaction.hide(f2); transaction.remove(f2); } if (f3 != null) { //transaction.hide(f3); transaction.remove(f3); } } }
到這里基本都結束了。哈哈,然后聰明的同學一定還注意到MainActivtiy里還有3個Fragment的類,由於這三個基本是99%相同的,所以我就貼出其中一個的代碼(其他兩個無非就是修改顯示文字,類文件名而已~~其他全部相同)
先看類文件
package com.example.fragmentdemo; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; /** *************************************************************** * * @版權 LinFeng * * @作者 LinFeng * * @版本 1.0 * * @創建日期 2016-6-6 * * @功能描述 ***************************************************************** */ /** * 這里要注意,Fragment是要引入android.support.v4.app.Fragment這個包里面的,不是那個app.fragment那個包哦 * @author LinFeng * */ public class Fragment1 extends Fragment{ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.frament1, container,false); return view; } }
然后是布局文件
<?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="#ffff00" android:gravity="center" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="25sp" android:text="FRAGMENT_1" /> </LinearLayout>
看到這里,相信大家基本都學會動態添加Fragment了吧!為什么不直接貼出項目代碼給大家下載?主要是,我不建議大家直接下載人家的項目來運行,然后閱讀,感覺你會了。但其實,如果自己寫,可能會出現各種奇葩的問題,所以,根據人家的思路,自己寫一遍,運行一下,出來的,才是你自己的。
《==================================這是一條華麗的分割線================================》
最后我們看一些關於Fragment的東西,剛剛好在人家的博客里面看到覺得不錯就COPY過來給大家看看了。
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。