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檔案做參考呢