2015年1月29日

【Android】Fragment取代TabActivity實作Tab分頁標籤

各位Android安卓開發者大家好!^^

今天小黑人要與大家分享的文章主題是"Tab分頁標籤",因為現在許多的App裡都有運用到Tab頁籤的功能,這是個可以很方便分類且快速瀏覽的一種架構簡單敘述Tab分頁標籤的架構就是:不切換Activity的狀況下藉由點選標籤項目來切換不同的顯示頁面,然而之前的Tab架構寫法是運用TabActivity來進行分頁處理,但現在新版的Android SDK版本已經不建議再繼續使用TabActivity代表著在之後新版的SDK版本裡將會淘汰TabActivity這種架構模式,所以在程式碼裡看到TabActivity都會標記成黃色警告標示且被畫上刪除線,但如果不使用TabActivity還可以做出Tab分頁標籤的架構嗎?答案是可以,藉由Fragment的架構模式可以達到Tab頁籤的功能,而且運用Fragment還可以針對系統資源更有效的控管,所以漸漸的Fragment架構模式將取代TabActivity,至於要怎麼運用Fragment來達到Tab分頁標籤相同的效果呢?那就讓我們繼續看下去吧~

1. 首先小黑人先用簡單的架構圖來說明這次範例實作的結構 :




以上根據架構圖的結構,小黑人會運用Fragment加入4Tab分頁標籤來實作這次的範例。


2. 因為AndroidManifest.xml不需要做特別的編輯設定,所以我們先針對Layout(.xml)來進行頁面框架的撰寫 :

a. main.xml :
<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"
        >

                <!-- Image圖片 -->
                <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/title_img"
                />   

                <!-- Tab標籤 -->
                <TabWidget
                android:id="@android:id/tabs"
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                />
               
                <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:visibility="gone"
                />

                <!-- 標籤內容顯示區塊 -->
                <FrameLayout
                android:id="@+id/container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                />

        </LinearLayout>

</android.support.v4.app.FragmentTabHost>

b. fragment_layout.xml :
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="35dip"
> 

        <!-- 標籤切換的內容圖片 -->
        <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        />
        
        <!-- 標籤切換的內容文字 -->
        <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textColor="@android:color/white"
        android:textSize="23sp"
        android:lineSpacingExtra="20dip"
        />
   
</RelativeLayout>

3. 接下來要開始撰寫Activity框架與Fragment分頁的部分(.java) :
(總共有5class,包含MainActivity4個分頁Fragment)

a. MainActivity :
//Activity繼承的是FragmentActivity,而不是舊版本的TabActivity
public class MainActivity extends FragmentActivity 
{
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
               
                //獲取TabHost控制元件
                FragmentTabHost mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
                //設定Tab頁面的顯示區域,帶入ContextFragmentManagerContainer ID
                mTabHost.setup(this, getSupportFragmentManager(), R.id.container);
               
                /**
                新增Tab結構說明 :
                首先帶入Tab分頁標籤的Tag資訊並可設定Tab標籤上顯示的文字與圖片,
                再來帶入Tab頁面要顯示連結的Fragment Class,最後可帶入Bundle資訊。
                **/
               
                //小黑人建立一個Tab,這個TabTag設定為one
                //並設定Tab上顯示的文字為第一堂課與icon圖片,Tab連結切換至
                //LessonOneFragment class,無夾帶Bundle資訊。
                mTabHost.addTab(mTabHost.newTabSpec("one")
                .setIndicator("第一堂課",getResources().getDrawable(R.drawable.lesson1_item))
                ,LessonOneFragment.class,null);

                //同上方Tab設定,不同處為帶入參數的差異
                mTabHost.addTab(mTabHost.newTabSpec("two")
                .setIndicator("第二堂課",getResources().getDrawable(R.drawable.lesson2_item))
                ,LessonTwoFragment.class,null);
                
                //同上方Tab設定,不同處為帶入參數的差異
                mTabHost.addTab(mTabHost.newTabSpec("three")
                .setIndicator("第三堂課",getResources().getDrawable(R.drawable.lesson3_item))
                ,LessonThreeFragment.class, null);
                
                //同上方Tab設定,不同處為帶入參數的差異
                mTabHost.addTab(mTabHost.newTabSpec("four")
                .setIndicator("第四堂課",getResources().getDrawable(R.drawable.lesson4_item))
                ,LessonFourFragment.class,null);
        }
       
        /**
        方法權限設定為Public目的是可以讓Fragment取得內容 。
        */
       
        //Tab - Lesson One的文字內容
        public String getLessonOneText()
        {
                return "小黑人的Android教室\n- 第一堂課 -";
        }
       
        //Tab - Lesson Two的文字內容
        public String getLessonTwoText()
        {
                return "小黑人的Android教室\n- 第二堂課 -";
        }
         
        //Tab - Lesson Three的文字內容
        public String getLessonThreeText()
        {
                return "小黑人的Android教室\n- 第三堂課 -";
        }
         
        //Tab - Lesson Four的文字內容
        public String getLessonFourText()
        {
                return "小黑人的Android教室\n- 第四堂課 -";
        }
}

b. 第一個Tab分頁 - LessonOneFragment
//Tab分頁class繼承Fragment
public class LessonOneFragment extends Fragment
{
        //顯示文字內容
        private String text = "";

        @Override
        public void onAttach(Activity activity)
        {
                super.onAttach(activity);
               
                //取得MainActivity的方法,將文字放入text字串
                MainActivity mMainActivity = (MainActivity) activity;
                text = mMainActivity.getLessonOneText();
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
                //導入Tab分頁的Fragment Layout
                return inflater.inflate(R.layout.fragment_layout, container, false);
        }
       
        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
                super.onActivityCreated(savedInstanceState);
               
                //取得TextView元件並帶入text字串
                TextView mText = (TextView) getView().findViewById(R.id.text);
                mText.setText(text);
           
                //取得ImageView元件並帶入指定圖片
                ImageView mImg = (ImageView) getActivity().findViewById(R.id.img);
                mImg.setImageResource(R.drawable.lesson1_img);
        }
}

c. 第二個Tab分頁 - LessonTwoFragment
//Tab分頁class繼承Fragment
public class LessonTwoFragment extends Fragment
{
        //顯示文字內容
        private String text = "";

        @Override
        public void onAttach(Activity activity)
        {
                super.onAttach(activity);

                //取得MainActivity的方法,將文字放入text字串
                MainActivity mMainActivity = (MainActivity) activity;
                text = mMainActivity.getLessonTwoText();
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
                //導入Tab分頁的Fragment Layout
                return inflater.inflate(R.layout.fragment_layout, container, false);
        }
       
        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
                super.onActivityCreated(savedInstanceState);
               
                //取得TextView元件並帶入text字串
                TextView mText = (TextView) getView().findViewById(R.id.text);
                mText.setText(text);
           
                //取得ImageView元件並帶入指定圖片
                ImageView mImg = (ImageView) getActivity().findViewById(R.id.img);
                mImg.setImageResource(R.drawable.lesson2_img);
        }
}

d. 第三個Tab分頁 - LessonThreeFragment
//Tab分頁class繼承Fragment
public class LessonThreeFragment extends Fragment
{
        //顯示文字內容
        private String text = "";

        @Override
        public void onAttach(Activity activity)
        {
                super.onAttach(activity);

                //取得MainActivity的方法,將文字放入text字串
                MainActivity mMainActivity = (MainActivity) activity;
                text = mMainActivity.getLessonThreeText();
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
                //導入Tab分頁的Fragment Layout
                return inflater.inflate(R.layout.fragment_layout, container, false);
        }
       
        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
                super.onActivityCreated(savedInstanceState);
               
                //取得TextView元件並帶入text字串
                TextView mText = (TextView) getView().findViewById(R.id.text);
                mText.setText(text);
           
                //取得ImageView元件並帶入指定圖片
                ImageView mImg = (ImageView) getActivity().findViewById(R.id.img);
                mImg.setImageResource(R.drawable.lesson3_img);
        }
}

e. 第四個Tab分頁 - LessonFourFragment
//Tab分頁class繼承Fragment
public class LessonFourFragment extends Fragment
{
        //顯示文字內容
        private String text = "";

        @Override
        public void onAttach(Activity activity)
        {
                super.onAttach(activity);

                //取得MainActivity的方法,將文字放入text字串
                MainActivity mMainActivity = (MainActivity) activity;
                text = mMainActivity.getLessonFourText();
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
                //導入Tab分頁的Fragment Layout
                return inflater.inflate(R.layout.fragment_layout, container, false);
        }
       
        @Override
        public void onActivityCreated(Bundle savedInstanceState)
        {
                super.onActivityCreated(savedInstanceState);
               
                //取得TextView元件並帶入text字串
                TextView mText = (TextView) getView().findViewById(R.id.text);
                mText.setText(text);
           
                //取得ImageView元件並帶入指定圖片
                ImageView mImg = (ImageView) getActivity().findViewById(R.id.img);
                mImg.setImageResource(R.drawable.lesson4_img);
        }
}

以上5Class撰寫完成後就可以實踐Tab頁籤的功能,其中範例裡的4個分頁Fragment Class其實版面架構都是一樣的,只是變換不同的顯示文字與圖片,小黑人只是用簡單的方式來表達Tab分頁標籤的效果,但依照不同的開發需求可以變更FragmentLayout與功能,大家可以試試看使用Fragment來呈現Tab分頁標籤的架構。

小黑人Tab分頁標籤 - 範例畫面如下 :





謝謝大家,如有任何問題都可以和小黑人一起交流討論!

☆小黑人☆

12 則留言:

  1. 請問大大可以能分享源碼嗎

    回覆刪除
    回覆
    1. 您好,很抱歉這麼久才回覆您,
      好的,小黑人將範例程式碼整理好後就發佈與大家分享。

      感謝您的提問!

      刪除
  2. 原來如此,那麼如果要置底的話 是在xml裡的widget裡加上alignbottom="true"嗎?
    還有請問一下可不可以做成focus就會變換圖片效果呢

    回覆刪除
  3. 不好意思!
    想請教一下
    使用android.support.v4.app.FragmentTabHost
    畫面無法顯示
    是因為v4.jar檔的原因嗎?
    但我有匯入卻還是無法使用。

    回覆刪除
    回覆
    1. 顯示以下訊息
      No tab known for tag null

      刪除
    2. 我發現他雖然顯示
      No tab known for tag null
      但是code打完run以後
      依然可以成功耶!
      但是我想將tab改到底部卻無法
      針對寬高的layout做修改
      似乎也不會變動
      不知道發生了什麼事情

      刪除
  4. 請教一下,我使用setIndicatorr叫出來的分頁按鈕看不到圖示,請問這部分有機會解決嗎?
    customize整個indicator看起來好複雜....

    回覆刪除
  5. 請問一下,我的實作的TabHost都會直接停止運作,試很多種方法了,程式碼跟xml都沒有錯,是哪裡出問題了嗎?

    回覆刪除
  6. 請問我可以不在Activity做
    而是在fragment分頁做嗎?

    回覆刪除
  7. Hi 板主你好

    請問如何實現一個
    在分頁1時,彈出一個dialog,按下確定後,會跳到分頁3

    謝謝

    回覆刪除
  8. 您好,想請問 您的標籤按下去會換圖(標籤本身換圖),這段程式碼該怎麼寫呢?
    因上方沒有看到,先感謝!!

    回覆刪除
  9. 謝謝你~
    老師辛苦了!!!

    回覆刪除

謝謝大家支持,有任何問題都可以和小黑人一起討論!