Android Fragment的用法(一)
https://www.cnblogs.com/guop/p/5072572.html
1、碎片是什麼
碎片(Fragment)是一種可以嵌入在活動當中的UI片段,它能讓程式更加合理和充分地利用大螢幕的空間,因而在平板上應用的非常廣泛。雖然碎片對你來說應該是個全新的概念,但我相信你學習起來應該毫不費力,因為它和活動實在是太像了,同樣都能包含佈局,同樣都有自己的生命週期。你甚至可以將碎片理解成一個迷你型的活動,雖然這個迷你型的活動有可能和普通的活動是一樣大的。
那麼究竟要如何使用碎片才能充分地利用平板螢幕的空間呢?想象我們正在開發一個新聞應用,其中一個介面使用ListView展示了一組新聞的標題,當點選了其中一個標題,就開啟另一個介面顯示新聞的詳細內容。如果是在手機中設計,我們可以將新聞標題列表放在一個活動中,將新聞的詳細內容放在另一個活動中,如圖1所示。
圖1
可是如果在平板上也這麼設計,那麼新聞標題列表將會被拉長至填充滿整個平板的螢幕,而新聞的標題一般都不會太長,這樣將會導致介面上有大量的空白區域,如圖2所示。
圖2
因此,更好的設計方案是將新聞標題列表介面和新聞詳細內容介面分別放在兩個碎片中,然後在同一個活動裡引入這兩個碎片,這樣就可以將螢幕空間充分地利用起來了,如圖3所示。
圖3
2、碎片的使用方式
新建一個FragmentTest專案,然後開始我們的碎片探索之旅吧。
新建一個左側碎片佈局left_fragment.xml,程式碼如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Button" /> </LinearLayout>
這個佈局非常簡單,只放置了一個按鈕,並讓它水平居中顯示。然後新建右側碎片佈局right_fragment.xml,程式碼如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00ff00" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20sp" android:text="This is right fragment" /> </LinearLayout>
可以看到,我們將這個佈局的背景色設定成綠色,並放置了一個TextView用於顯示一段文字。接著新建一個LeftFragment類,繼承自Fragment。注意,這裡可能會有兩個不同包下的Fragment供你選擇,建議使用android.app.Fragment,因為我們的程式是面向Android 4.0以上系統的,另一個包下的Fragment主要是用於相容低版本的Android系統。LeftFragment的程式碼如下所示:
public class LeftFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.left_fragment, container, false); return view; } }
這裡僅僅是重寫了Fragment的onCreateView()方法,然後在這個方法中通過LayoutInflater的inflate()方法將剛才定義的left_fragment佈局動態載入進來,整個方法簡單明瞭。接著我們用同樣的方法再新建一個RightFragment,程式碼如下所示:
public class RightFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.right_fragment, container, false); return view; } }
新建another_right_fragment.xml,程式碼如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffff00" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20sp" android:text="This is another right fragment" /> </LinearLayout>
這個佈局檔案的程式碼和right_fragment.xml中的程式碼基本相同,只是將背景色改成了黃色,並將顯示的文字改了改。然後新建AnotherRightFragment作為另一個右側碎片,程式碼如下所示:
public class AnotherRightFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.another_right_fragment, container, false); return view; } }
程式碼同樣非常簡單,在onCreateView()方法中載入了剛剛建立的another_right_fragment佈局。這樣我們就準備好了另一個碎片,接下來看一下如何將它動態地新增到活動當中。修改activity_main.xml,程式碼如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 靜態載入Fragment --> <fragment android:id="@+id/left_fragment" android:name="com.example.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/right_layout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" >
<!-- 可以在這個容器中動態載入Fragment -->
<fragment android:id="@+id/right_fragment" android:name="com.example.fragmenttest.RightFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> </LinearLayout>
可以看到,現在將右側碎片放在了一個FrameLayout中,這是Android中最簡單的一種佈局,它沒有任何的定位方式,所有的控制元件都會擺放在佈局的左上角。由於這裡僅需要在佈局裡放入一個碎片,因此非常適合使用FrameLayout。
之後我們將在程式碼中替換FrameLayout裡的內容,從而實現動態新增碎片的功能。修改MainActivity中的程式碼,如下所示:
public class MainActivity extends Activity implements OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: AnotherRightFragment fragment = new AnotherRightFragment(); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager. beginTransaction(); transaction.replace(R.id.right_layout, fragment); transaction.commit(); break; default: break; } } }
可以看到,首先我們給左側碎片中的按鈕註冊了一個點選事件,然後將動態新增碎片的邏輯都放在了點選事件裡進行。結合程式碼可以看出,動態新增碎片主要分為5步。
- 建立待新增的碎片例項。
- 獲取到FragmentManager,在活動中可以直接呼叫getFragmentManager()方法得到。
- 開啟一個事務,通過呼叫beginTransaction()方法開啟。
- 向容器內加入碎片,一般使用replace()方法實現,需要傳入容器的id和待新增的碎片例項。
- 提交事務,呼叫commit()方法來完成。
這樣就完成了在活動中動態新增碎片的功能,執行程式,可以看到啟動介面如圖4所示,然後點選一下按鈕,效果如圖5所示。
圖4
圖5
3、在碎片中模擬返回棧
我們成功實現了向活動中動態新增碎片的功能,不過你嘗試一下就會發現,通過點選按鈕添加了一個碎片之後,這時按下Back鍵程式就會直接退出。如果這裡我們想模仿類似於返回棧的效果,按下Back鍵可以回到上一個碎片,該如何實現呢?
其實很簡單,FragmentTransaction中提供了一個addToBackStack()方法,可以用於將一個事務新增到返回棧中,修改MainActivity中的程式碼,如下所示:
public class MainActivity extends Activity implements OnClickListener { …… @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: AnotherRightFragment fragment = new AnotherRightFragment(); FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction transaction = fragmentManager. beginTransaction(); transaction.replace(R.id.right_layout, fragment); transaction.addToBackStack(null); transaction.commit(); break; default: break; } } }
這裡我們在事務提交之前呼叫了FragmentTransaction的addToBackStack()方法,它可以接收一個名字用於描述返回棧的狀態,一般傳入null即可。現在重新執行程式,並點選按鈕將AnotherRightFragment新增到活動中,然後按下Back鍵,你會發現程式並沒有退出,而是回到了RightFragment介面,再次按下Back鍵程式才會退出。
4、碎片和活動之間進行通訊
雖然碎片都是嵌入在活動中顯示的,可是實際上它們的關係並沒有那麼親密。你可以看出,碎片和活動都是各自存在於一個獨立的類當中的,它們之間並沒有那麼明顯的方式來直接進行通訊。如果想要在活動中呼叫碎片裡的方法,或者在碎片中呼叫活動裡的方法,應該如何實現呢?
為了方便碎片和活動之間進行通訊,FragmentManager提供了一個類似於findViewById()的方法,專門用於從佈局檔案中獲取碎片的例項,程式碼如下所示:
RightFragment rightFragment = (RightFragment) getFragmentManager().findFragmentById(R.id.right_fragment);
呼叫FragmentManager的findFragmentById()方法,可以在活動中得到相應碎片的例項,然後就能輕鬆地呼叫碎片裡的方法了。
掌握瞭如何在活動中呼叫碎片裡的方法,那在碎片中又該怎樣呼叫活動裡的方法呢?其實這就更簡單了,在每個碎片中都可以通過呼叫getActivity()方法來得到和當前碎片相關聯的活動例項,程式碼如下所示:
MainActivity activity = (MainActivity) getActivity();
有了活動例項之後,在碎片中呼叫活動裡的方法就變得輕而易舉了。另外當碎片中需要使用Context物件時,也可以使用getActivity()方法,因為獲取到的活動本身就是一個Context物件了。
這時不知道你心中會不會產生一個疑問,既然碎片和活動之間的通訊問題已經解決了,那麼碎片和碎片之間可不可以進行通訊呢?
說實在的,這個問題並沒有看上去的複雜,它的基本思路非常簡單,首先在一個碎片中可以得到與它相關聯的活動,然後再通過這個活動去獲取另外一個碎片的例項,這樣也就實現了不同碎片之間的通訊功能,因此這裡我們的答案是肯定的。