自學Android之UI元件:(二)Fragment的基本使用(上)
本篇為UI元件的第二篇,主要探討關於Fragment的基本使用,包括簡單的原理以及建立、修改、刪除等操作。
在本篇文章中,你將瞭解到:
1.什麼是Fragment。
2.Fragment的生命週期。
3.Fragment的基本使用。
什麼是Fragment?
由於Android是一款開源的系統,在國內外不同的廠商研發出了很多不同的機型,甚至將Android應用到了平板中,但是這意味著不同廠商出產的手機、平板的螢幕大小不一,例如,元件排版安裝5.0寸手機制作的程式,如果在4.7寸的手機上,視覺效果體驗雖說會降低,但也不至於不可接受。但是如果放到6.0寸的手機,乃至平板上,使用者的視覺體驗會大幅度降低。
這就導致了Android研發的工作中有比較大的比重的解析度匹配,而Fragment則是用來解決這一問題的。
這是因為,Fragment是依賴於Activity的,在Activity的佈局中建立一個FrameLayout元件,就可以在上面根據不同的需要顯示很多個Fragment(當然不能同時顯示)。並且Fragment有自己的生命週期、自己的佈局檔案、獨立的事件處理,極端的說,你甚至可以把一個Fragment當做一個Activity來使用。
Fragment的生命週期
前面說過,Fragment是依賴於Activity的,即必須先建立Activity,再建立Fragment,並且Fragment的生命週期細節上同Activity所有不同,但仍是可以對應參考的。先來看一下Google官方給出的Fragment與Activity生命週期對照表:
我們現在來簡單解釋一下Fragment多出來的生命週期:
onAttach():
當Fragment與Activity發生關聯時呼叫該方法。
onCreateView():
在Fragment中初始化Layout、初始化元件時使用。
onActivityCreated():
當Activity的onCreate()返回時呼叫。
onDestoryView():
與onCreateView想對應,當該Fragment的檢視被移除時呼叫。
onDetach():
與onAttach相對應,當Fragment與Activity關聯被取消時呼叫。
另外,值得注意的是,除了onCreateView()外,如果你重寫了其他方法,則必須呼叫spue使父類來實現該方法。
準備工作:
本案例中計劃使用動態新增Fragment的方式,來分別在同一個FrameLayout元件中分別顯示兩個不同的Fragment,並且完成替換、刪除、在Fragment中修改Activity的內容,以及在Activity中修改Fragment的內容等操作。
首先來看一下效果圖:
接著開始進行一些初始化等準備工作,程式碼如下:
以下程式碼在activity_main中寫入:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:text="這裡是MainActivity"
android:textSize="18sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<Button
android:id="@+id/ButtonShowFragmentNow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="顯示FragmentNow"
android:textAllCaps="false" />
<Button
android:id="@+id/ButtonShowFragmentTwo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="顯示FragmentTwo"
android:textAllCaps="false" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">
<Button
android:id="@+id/ButtonAmendFragmentTwo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="修改FragmentTwo"
android:textAllCaps="false" />
<Button
android:id="@+id/ButtonReplaceFragmentNow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="替換" />
<Button
android:id="@+id/ButtonDeleteFragmentNow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="刪除FragmentNow"
android:textAllCaps="false" />
</LinearLayout>
<TextView
android:id="@+id/TextView"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="5dp"
android:text="這就是用來被Fragment修改的"
android:textAllCaps="false"
android:textSize="18sp" />
<FrameLayout
android:id="@+id/FrameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#D02090" />
</LinearLayout>
值得注意的是,在FrameLayout中添加了背景顏色,如果沒有顯示Fragment,則顯示背景顏色,如果添加了Fragment,則顯示Fragment佈局檔案中定義的顏色。
以下程式碼在fragment_now中寫入:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#9aff9a">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ButtonFragmentNow"
android:text="修改Activity"
android:textAllCaps="false"
android:layout_gravity="center"/>
</LinearLayout>
以下程式碼在fragment_two中寫入:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#d1eeee"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/TextVIewFragmentTwo"
android:text="註定被修改"
android:layout_gravity="center"
android:textSize="30sp"/>
</LinearLayout>
開始建立Fragment:
在上面的準備工作中,建立了一個FrameLayout元件,而Fragment就會顯示在該元件中。另外建立了兩個佈局檔案,這兩個佈局檔案就分別對應兩個不同的Fragment。以下首先來看程式碼思路:
1.首先定義FragmentNow以及FragmentTwo。。
2.接著初始化所需的元件,並設定Button的監聽器
3.通過完整的程式碼顯示FragmentNow。
4.通過簡略的程式碼顯示fragmentTwo。
5.設定Fragment直接的自由替換。
6.一些其他的應用說明。
以下程式碼分別在FragmentNow/Two中寫入:
/* 程式碼執行1:
* 1.在建立Fragment時,需要繼承自v4.app.Fragment中的Fragment。
* 2.然後建立View物件,使用者獲取Fragment所需的Layout。
* 3.onCreateView是用於初始化Fragment的檢視。
* 4.Amend()用於在Activity中呼叫該方法,來修改Fragment中的內容。*/
public class FragmentTwo extends Fragment {
private View mView;
private TextView mTextView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_two, null);
return mView;
}
public void Amend(){
mTextView = (TextView) mView.findViewById(R.id.TextVIewFragmentTwo);
mTextView.setText("這是FragmentTwo中已經被修改了的值");
}
}
public class FragmentNow extends Fragment {
private View mView;
private Button mButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.fragment_now, null);
mButton = (Button) mView.findViewById(R.id.ButtonFragmentNow);
return mView;
}
/* 程式碼執行1:
* 1.1.由於初始化檢視必須在onCreateView()中完成,如果向Activity一樣在onCreate()中呼叫Button是不可以的。
* 1.2.這是因為onCreate()在onCreateView()之前就被呼叫,如果此時在onCreate()中呼叫Button,會顯示空指標。
* */
@Override
public void onStart() {
super.onStart();
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MainActivity mainActivity = (MainActivity) getActivity();
mainActivity.Amend();
}
});
}
}
以下程式碼在MainActivity中寫入:
/* 程式碼執行2:
* 1.首先將兩個Fragment類建立為物件。用於通過該物件來配合FrameLayout顯示fragment。
* 2.然後獲得各個Button的ID。
* 3.定義一個boolean型別的變數,用於替換時的迴圈。
* 4.注意別忘了在onCreate()中呼叫init()。*/
private void init() {
mFragmentNow = new FragmentNow();
mFragmentTwo = new FragmentTwo();
mButtonShowFragmentNow = (Button) findViewById(R.id.ButtonShowFragmentNow);
mButtonShowFragmentTwo = (Button) findViewById(R.id.ButtonShowFragmentTwo);
mButtonAmendFragmentTwo = (Button) findViewById(R.id.ButtonAmendFragmentTwo);
mButtonReplaceFragmentNow = (Button) findViewById(R.id.ButtonReplaceFragmentNow);
mButtonDeleteFragmentNow = (Button) findViewById(R.id.ButtonDeleteFragmentNow);
mBoolean = true;
}
以下程式碼在MainActivity的onCreate()中寫入:
mButtonShowFragmentNow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* 程式碼執行3:
* 1.1.首先通過Fragment管理器建立一個Fragment物件,
* 1.2.通過getSupportFragmentManager來返回同Activity進行關聯並互動的Fragment物件。
* 2.再將已經建立好的Fragment放置到Fragment事務佇列中。等待Activity的處理。
* 3.告知該Fragment事務它的顯示位置(在哪個元件上顯示),而Fragment所需的邏輯程式碼在哪個類中(通過物件引用)。
* 4.這時將Fragment正式提交到Fragment佇列中,使Activity進行處理。*/
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.FrameLayout, mFragmentNow);
fragmentTransaction.commit();
}
});
以下程式碼在MainActivity的onCreate()中寫入:
mButtonShowFragmentTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* 程式碼執行4:
* 1.使用精簡的方式建立Fragment。並且不使用.add,因為使用.add建立當用戶連續點選後,程式將結束執行。
* */
getSupportFragmentManager().beginTransaction()
.replace(R.id.FrameLayout, mFragmentTwo).commit();
}
});
以下程式碼在MainActivity的onCreate()中寫入:
mButtonReplaceFragmentNow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* 程式碼執行5:
* 1.替換Activity。由於只有一個Fragment顯示位置,所以只能同時顯示一個。
* 2.每次替換完成後,則修改mBoolean的值,防止陷入死迴圈。
* 3.但以上方式會存在一點瑕疵:噹噹前顯示的是mFragment1(預設為真,第一次替換mFragment1)時,第一次點選將(無效果),需要點選兩次,反之同理。
* */
if (mBoolean) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.FrameLayout, mFragmentNow).commit();
mBoolean = false;
} else {
getSupportFragmentManager().beginTransaction()
.replace(R.id.FrameLayout, mFragmentTwo).commit();
mBoolean = true;
}
}
});
以下程式碼在MainActivity的onCreate()中寫入:
mButtonAmendFragmentTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* 程式碼執行6:
* 1.在Activity中通過Fragment物件呼叫其中的方法。*/
mFragmentTwo.Amend();
}
});
mButtonDeleteFragmentNow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* 程式碼執行6:
* 1.如果你希望刪除Fragment,則使用remove方法即可,通過將Fragment物件作為引數。。*/
getSupportFragmentManager().beginTransaction()
.remove(mFragmentNow).commit();
}
});
以下程式碼在MainActivity中寫入:
/* 程式碼執行6:
* 1.同理,在Activity中建立公共方法,用於Fragment中呼叫。*/
public void Amend() {
TextView textView = (TextView) findViewById(R.id.TextView);
textView.setText("你看吧,Activity中的TextView已經被修改了。");
}
至此,Fragment中的基本使用你已經學會了,其實Fragment還有一種靜態新增的方式,不過個人認為此種方式使用不夠靈活,且也非常簡單,便沒有在文章中寫出,感興趣的話可以檢視相關文章。
現在再來看一下效果圖,你的效果是否一致?
用於操作Fragment的其他方法:
比較常用的操作Fragment的方法在前面的案例中已經有說明了,不過還有一些沒有使用到的需要嘮叨兩句。
.add()
該方法是用於建立Fragment的,但是當你建立過一次Fragment後,再點選一次,則會丟擲異常,不能建立第二次。所以Demo中就沒有使用這種方式。
.replace()
replace()其實是先刪除,再建立,如果不考慮Fragment中的臨時資料的話,可以使用這種方式來建立Fragment。
.hide()
這個方法僅僅是將Fragment隱藏起來,但並不是將其銷燬。
.show()
同樣的,是將隱藏起來的Fragment顯示出來。
後記:
至此本篇結束,不過僅僅是介紹了Fragment最基本的使用,要知道Fragment在實際使用的比例是非常大的,所以也會用比較多的篇幅來說明Fragment。