Android 筆記:在Android Studio上製作分頁效果(FragmentTabHost 使用方法)
這次的筆記內容是利用 FragmentTabHost 和 Fragment 來製作分頁的效果。
因為最近想要製作一些多功能的 App,想把各種功能透過多個分頁來實現,因此來學習如何製作分頁效果。
主要的介面是利用 FragmentTabHost 來配置, FragmentTabHost 來當作容器,並放入許多 Fragment 來當作分頁。
我常提醒我的學弟或是同學,如果有可以請 IDE 幫忙做完的工作就竟量讓 IDE 幫忙完成,所以我個人的習慣是:如果要創建Activity時都會選擇他們的預設選項,讓 IDE 幫我把 xml 和 java 檔一起建出來。
但這次,Activity 中沒有 FragmentTabHost 的預設選項,因此預設時做出來的 xml 檔需要大幅修改。
先來看一下主頁 FragmentTabHost 的 Layout 程式:
讀者可以使用預設做出來的 xml 檔進行修改,可以轉到 Text 模式,然後將程式碼通通刪除,直接手動複製貼上上述程式碼或者手動打出自己的個性化 Layout。
在上面程式中你可以看到在主要的結構為 FragmentTabHost ,而在 FragmentTabHost 中固定要有以下三種子物件:一個 TabWidget 和 兩個 FrameLayout。
其中 realtabcontent 的 FrameLayout 為顯示每個分頁用的區塊,而至於 tabcontent 為一定要有的一個 FrameLayout ,但在本次筆記中卻不為用到。至於為什麼要有這個 FrameLayout 筆者也不知道,以下有找到一個大陸網友的資源網站有說明為什麼會有兩個 FrameLayout 但筆者也是看不太懂,如果有高手讀者經過歡迎指教。
而下面的程式中 FragmentTabHost 的 id 為引用系統的 android.R.id.tabhost (至於為什麼呢?這我也不知道,如果有厲害的讀者可以為我解答歡迎在下面留言喔!感恩!),因此這邊這一行在 id 中的設定為固定的。
廢話不多說,先來看程式碼吧:
這段程式其實很單純,就是直接取得 FragmentTabHost 物件,並將分頁插入其中。
這邊要比較注意的地方是 FragmentTabHost 的 id 為系統的引用值,因此固定使用 android.R.id.tabhost。
接著, setup() 方法的功能為初始化設定 FragmentTabHost 物件。這是一定要有的程式,在取得了 FragmentTabHost 實體後一定要執行的一行方法。其中第一個參數為所屬的 Context 物件,因為該 FragmentTabHost 是屬於 MainActivity 的,因此該參數直接輸入 this 即可。第二個參數為 FragmentManager 物件,該物件為系統物件,因此要取得的方法就是使用 Activity 內建的方法: getSupportFragmentManager,就可以直接向系統取得 FragmentManager 的實體。而 FragmentManager 物件對於 Fragment 而言相當重要,他是管理所有 Fragment 的生成與消滅還有排序的重要物件。第三個參數為要用來顯示各 Fragment 的 FrameLayout。
而 addTab 也很淺顯易懂,就是加入分頁,以下為介紹 addTab 的說明:
tag 的概念為該 tab 的 id ,事後若還有需要取用到該 tab 就必須用這個 tag 去追蹤它。
而 setIndicator 方法中為設置該 Tab 的顯示方法,有兩種顯示方法:1. 只使用文字顯示,2. 使用文字和圖示(icon)來顯示。而本次筆記中只使用文字來顯示,若要使用文字+圖示,則 setIndicator 中就需要引用兩個參數,第一個依舊是要顯示文字,而第二個則是要放入的圖片的id。
我有兩個 Fragment ,先貼上我的 Fragment 程式碼:
AppleFragment.java
BananaFragment.java
在 BananaFragment.java 中我們可以看到,直接在 Activity 剛形成的瞬間(onActivityCreated),執行與畫面上的物件做連結的程式即可。與我們常在 Activity 的 onCreate 中做的事相同。
其中 onAttach 為 Fragment 中最早執行的一個方法。其執行時機為當 Activity 有要與 Fragment 進行連接,達成直屬關係時,就會執行,可以想像成 Fragment 開始工作時的第一個方法。但是注意,在 onAttach 時畫面並沒有準備好,若在此時執行任何與畫面物件連接的程式(ex: findViewById)時會發生錯誤。
若要執行 findViewById,請於 onCreateView 中或之後再執行為最佳,顧名思義,此方法執行過後畫面才會正式生成。
OK! 到目前為止就算正式完成了,接著叫執行看看吧!
因為最近想要製作一些多功能的 App,想把各種功能透過多個分頁來實現,因此來學習如何製作分頁效果。
主要的介面是利用 FragmentTabHost 來配置, FragmentTabHost 來當作容器,並放入許多 Fragment 來當作分頁。
步驟一:製作 FragmentTabHost 的 xml 檔
我常提醒我的學弟或是同學,如果有可以請 IDE 幫忙做完的工作就竟量讓 IDE 幫忙完成,所以我個人的習慣是:如果要創建Activity時都會選擇他們的預設選項,讓 IDE 幫我把 xml 和 java 檔一起建出來。
但這次,Activity 中沒有 FragmentTabHost 的預設選項,因此預設時做出來的 xml 檔需要大幅修改。
先來看一下主頁 FragmentTabHost 的 Layout 程式:
<android.support.v4.app.FragmentTabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TabWidget android:id="@android:id/tabs" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0"/> <!-- 此 FrameLayout 不知為何會存在,若有高手知道歡迎指教! --> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0"/> <!-- 以下為顯示的區塊 --> <FrameLayout android:id="@+id/realtabcontent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout> </android.support.v4.app.FragmentTabHost>
讀者可以使用預設做出來的 xml 檔進行修改,可以轉到 Text 模式,然後將程式碼通通刪除,直接手動複製貼上上述程式碼或者手動打出自己的個性化 Layout。
在上面程式中你可以看到在主要的結構為 FragmentTabHost ,而在 FragmentTabHost 中固定要有以下三種子物件:一個 TabWidget 和 兩個 FrameLayout。
其中 realtabcontent 的 FrameLayout 為顯示每個分頁用的區塊,而至於 tabcontent 為一定要有的一個 FrameLayout ,但在本次筆記中卻不為用到。至於為什麼要有這個 FrameLayout 筆者也不知道,以下有找到一個大陸網友的資源網站有說明為什麼會有兩個 FrameLayout 但筆者也是看不太懂,如果有高手讀者經過歡迎指教。
而下面的程式中 FragmentTabHost 的 id 為引用系統的 android.R.id.tabhost (至於為什麼呢?這我也不知道,如果有厲害的讀者可以為我解答歡迎在下面留言喔!感恩!),因此這邊這一行在 id 中的設定為固定的。
步驟二:製作 FragmentTabHost 的 java 檔
廢話不多說,先來看程式碼吧:
package eric.mytabtest3; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTabHost; public class MainActivity extends FragmentActivity { private FragmentTabHost mTabHost; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //透過 id 取得 Layout 上的 FragmentTabHost 物件 mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost); //初始化 FragmentTabHost,第二個參數中為需要從系統取得 Fragment 的管理員。 //第三個參數為要用來顯示 Fragment 的 FrameLayout 。 (Fragment 都會使用 FrameLayout 來掛載。) //第一個參數為所在的 Activity ,因為自己(MainActivity)就是主畫面,因此直接放入 this 即可。 mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); //加入分頁 apple 到 TabHost 中 mTabHost.addTab(mTabHost.newTabSpec("apple").setIndicator("Apple"), AppleFragment.class, null); //加入分頁 banana 到 TabHost 中 mTabHost.addTab(mTabHost.newTabSpec("banana").setIndicator("Banana"), BananaFragment.class, null); } }
這段程式其實很單純,就是直接取得 FragmentTabHost 物件,並將分頁插入其中。
這邊要比較注意的地方是 FragmentTabHost 的 id 為系統的引用值,因此固定使用 android.R.id.tabhost。
接著, setup() 方法的功能為初始化設定 FragmentTabHost 物件。這是一定要有的程式,在取得了 FragmentTabHost 實體後一定要執行的一行方法。其中第一個參數為所屬的 Context 物件,因為該 FragmentTabHost 是屬於 MainActivity 的,因此該參數直接輸入 this 即可。第二個參數為 FragmentManager 物件,該物件為系統物件,因此要取得的方法就是使用 Activity 內建的方法: getSupportFragmentManager,就可以直接向系統取得 FragmentManager 的實體。而 FragmentManager 物件對於 Fragment 而言相當重要,他是管理所有 Fragment 的生成與消滅還有排序的重要物件。第三個參數為要用來顯示各 Fragment 的 FrameLayout。
而 addTab 也很淺顯易懂,就是加入分頁,以下為介紹 addTab 的說明:
參數一:mTabHost.newTabSpec("apple").setIndicator("Apple")此參數要求為一個 TabHost.TabSpec 物件,而可以透過 FragmentTabHost 的動態方法 newTabSpec 製作出一個 TabHost.TabSpec 物件,其中 newTabSpec 只需要一個參數 string tag 。
tag 的概念為該 tab 的 id ,事後若還有需要取用到該 tab 就必須用這個 tag 去追蹤它。
而 setIndicator 方法中為設置該 Tab 的顯示方法,有兩種顯示方法:1. 只使用文字顯示,2. 使用文字和圖示(icon)來顯示。而本次筆記中只使用文字來顯示,若要使用文字+圖示,則 setIndicator 中就需要引用兩個參數,第一個依舊是要顯示文字,而第二個則是要放入的圖片的id。
參數二:AppleFragment.class這個參數為要設丁該分頁的 Fragment 類別,很簡單,將希望顯示在該分頁的 Fragment 的類別放入該參數即可。
參數三:null這個參數為一個 Bundle 物件,應該是便於個分頁間傳遞資料用的,但目前我們不需要,所以將該參數放入 null 物件。
步驟三、製作 Fragment
我有兩個 Fragment ,先貼上我的 Fragment 程式碼:
1. Apple 分頁的 AppleFragmentApple 分頁圖示
AppleFragment.java
package eric.mytabtest3; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class AppleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_apple, container, false); } }fragment_apple.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".AppleFragment"> <!-- TODO: Update blank fragment layout --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_blank_fragment" /> </LinearLayout>這篇 Fragment 的部分大概看看就好,我並沒有加入功能,只是一個單純有個 TextView 的介面,什麼事都不能做。目的只是區別與 BananaFragment 的不同。
2. Banana 分頁的 BananaFragmentBanana 分頁圖示
BananaFragment.java
package eric.mytabtest3; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class BananaFragment extends Fragment { MainActivity tmpA; TextView tvShow; EditText etEnter; Button btnEnter; @Override public void onAttach(Context context) { super.onAttach(context); tmpA = (MainActivity)context; } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_banana, container, false); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { tvShow = (TextView) getView().findViewById(R.id.tvShow); etEnter = (EditText) getView().findViewById(R.id.etEnter); btnEnter = (Button) getView().findViewById(R.id.btnEnter); btnEnter.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { tvShow.setText(etEnter.getText()); } }); super.onActivityCreated(savedInstanceState); } }fragment_banana.xml
<?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="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="顯示在此" android:id="@+id/tvShow" android:layout_gravity="center_horizontal" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> <EditText android:layout_width="250dp" android:layout_height="wrap_content" android:id="@+id/etEnter" android:layout_below="@+id/tvShow" android:layout_centerHorizontal="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Enter" android:id="@+id/btnEnter" android:layout_below="@+id/etEnter" android:layout_centerHorizontal="true" /> </RelativeLayout>可以在 java 檔中看到, Fragment 的製作過程其實跟 Activity 很像,而且不用在意主頁 MainActivity,主頁 MainActivity 中的 FragmentTabHost 只是成為容器的用途,負責承載 Fragment ,而 Fragment 的形成可自行定義,並不用在 MainActivity 加入任何程式。非常方便。
在 BananaFragment.java 中我們可以看到,直接在 Activity 剛形成的瞬間(onActivityCreated),執行與畫面上的物件做連結的程式即可。與我們常在 Activity 的 onCreate 中做的事相同。
其中 onAttach 為 Fragment 中最早執行的一個方法。其執行時機為當 Activity 有要與 Fragment 進行連接,達成直屬關係時,就會執行,可以想像成 Fragment 開始工作時的第一個方法。但是注意,在 onAttach 時畫面並沒有準備好,若在此時執行任何與畫面物件連接的程式(ex: findViewById)時會發生錯誤。
若要執行 findViewById,請於 onCreateView 中或之後再執行為最佳,顧名思義,此方法執行過後畫面才會正式生成。
OK! 到目前為止就算正式完成了,接著叫執行看看吧!
感謝各位讀者!!!!
加油加油!!
回覆刪除自己整理一些學習心得,能與人分享真的是一件很棒的事~
謝啦~~ 我會加油的!!
刪除不好意思
回覆刪除請問版主能不能直接分享github檔案做參考呢