首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android TabHost 實現標籤頁切換的兩種方式詳解

     編輯:Java 相關知識

前言

在早期 Android 開發中,TabHost 配合 TabActivity 是實現底部導航或頂部標籤頁切換的經典方案。雖然現在推薦使用 ViewPager + Fragment 或 BottomNavigationView,但理解 TabHost 的運作機制對維護老專案或學習 Android 視圖體系仍有幫助。本文詳細解析兩種主流實現方式:純代碼動態構建與 XML 佈局靜態定義。

方式一:繼承 TabActivity 動態添加標籤

核心類結構

  • TabActivity:已棄用的 Activity 子類,內部封裝了 TabHost。
  • TabHost:標籤頁容器,管理多個 TabSpec。
  • TabSpec:單個標籤項,包含指示器(標題/圖標)和內容。
  • Intent:用於指定標籤頁對應的 Activity 類。

關鍵代碼實現

public class MainTabActivity extends TabActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TabHost tabHost = getTabHost(); // 獲取 TabHost 實例,內部已調用 setContentView

        Resources res = getResources();

        // 標籤 1:藝術
        Intent intentArt = new Intent(this, ArtActivity.class);
        TabHost.TabSpec specArt = tabHost.newTabSpec("art")
                .setIndicator("藝術", res.getDrawable(R.drawable.tab_selector))
                .setContent(intentArt);
        tabHost.addTab(specArt);

        // 標籤 2:音樂
        Intent intentMusic = new Intent(this, MusicActivity.class);
        TabHost.TabSpec specMusic = tabHost.newTabSpec("music")
                .setIndicator("音樂", res.getDrawable(R.drawable.tab_selector))
                .setContent(intentMusic);
        tabHost.addTab(specMusic);

        // 標籤 3:體育
        Intent intentSport = new Intent(this, SportActivity.class);
        TabHost.TabSpec specSport = tabHost.newTabSpec("sport")
                .setIndicator("體育", res.getDrawable(R.drawable.tab_selector))
                .setContent(intentSport);
        tabHost.addTab(specSport);

        // 設置默認顯示第二個標籤
        tabHost.setCurrentTab(1);
    }
}

狀態選擇器

在 res/drawable/tab_selector.xml 中定義選中/未選中圖標切換:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="https://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/tab_selected" android:state_selected="true"/>
    <item android:drawable="@drawable/tab_unselected"/>
</selector>

AndroidManifest.xml 註冊

所有作為標籤內容的 Activity 必須在清單文件中聲明:

<activity android:name=".MainTabActivity" android:theme="@android:style/Theme.NoTitleBar">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>
<activity android:name=".ArtActivity"/>
<activity android:name=".MusicActivity"/>
<activity android:name=".SportActivity"/>

方式二:XML 佈局定義標籤內容

佈局文件

根佈局必須是 FrameLayout,內部放置各標籤頁的具體視圖:

<FrameLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 第一個標籤內容 -->
    <LinearLayout android:id="@+id/tab_content_one"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:text="內容一"/>
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕"/>
    </LinearLayout>

    <!-- 第二個標籤內容 -->
    <LinearLayout android:id="@+id/tab_content_two"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <AnalogClock android:layout_width="wrap_content" android:layout_height="wrap_content"/>
    </LinearLayout>

    <!-- 第三個標籤內容 -->
    <LinearLayout android:id="@+id/tab_content_three"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <RadioGroup android:layout_width="wrap_content" android:layout_height="wrap_content">
            <RadioButton android:text="選項一"/>
            <RadioButton android:text="選項二"/>
        </RadioGroup>
    </LinearLayout>
</FrameLayout>

Activity 代碼

public class TabHostXmlActivity extends TabActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TabHost tabHost = getTabHost();

        // 將佈局文件 inflate 到 TabHost 的內容容器中
        LayoutInflater.from(this).inflate(R.layout.main_tabs, tabHost.getTabContentView(), true);

        tabHost.addTab(tabHost.newTabSpec("one").setIndicator("A").setContent(R.id.tab_content_one));
        tabHost.addTab(tabHost.newTabSpec("two").setIndicator("B", getResources().getDrawable(R.drawable.icon)).setContent(R.id.tab_content_two));
        tabHost.addTab(tabHost.newTabSpec("three").setIndicator("C", getResources().getDrawable(R.drawable.icon)).setContent(R.id.tab_content_three));
    }
}

兩種方式對比

維度動態代碼方式XML 佈局方式
靈活性高,可動態決定標籤數量和內容靜態,佈局固定
分層清晰度每個標籤獨立一個 Activity,邏輯解耦好所有內容在一個 Activity,視圖層級較深
性能啟動多個 Activity 有開銷單 Activity 內切換 View,較輕量
適用場景標籤頁業務邏輯複雜、獨立性強標籤頁內容簡單、共用數據多

避坑指南

  1. TabActivity 已棄用:新專案請使用 Fragment + ViewPager 或 BottomNavigationView 實現。
  2. 主題設置:必須在 Manifest 中為主 TabActivity 設置 android:theme="@android:style/Theme.NoTitleBar",否則系統默認標題欄會遮蓋 TabHost。
  3. 圖標資源位置:Selector XML 必須放在 res/drawable/ 目錄下,而非 drawable-hdpi 等密度目錄。
  4. 內容視圖 ID:XML 方式中,setContent(R.id.xxx) 對應的 ID 必須是 FrameLayout 直接子視圖的 ID。

總結

TabHost 體現了模板方法模式:框架處理標籤切換邏輯,開發者只需提供具體內容。雖然已過時,但其「容器-規格-內容」的設計思想在現代組件中依然普遍存在。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved