1. 程式人生 > >19. 使用選項卡導航

19. 使用選項卡導航

問題

需要在應用程式中為側向螢幕導航提供可選的選項卡,但Google沒有在框架或支援庫中提供選項卡小部件。

解決方案

(API Level 7)
我們可以通過Google提供為SDK樣本的SlidingTabLayout進行構建來實現選項卡導航。Google已完全淘汰以前的各種Android選項卡,例如TabWidget和ActionBar.Tab,但SlidingTabLayout符合當前的選項卡設計模式。
SlidingTabLayout旨在與ViewPager密切協作,因為選項卡設計模式的一部分是允許在每個檢視之間輕掃。因此,沒有用於手動新增選項卡條目的API。相反,從附加的ViewPager返回的頁面標題中派生選項卡。如果選項卡內容延伸超出螢幕寬度,使用者可以向左或向右滾動選項卡。輕掃ViewPager時,當前選項卡自動滾動以同時顯示出來。
在編寫本書時,SlidingTabLayout的設計採用了以前的Holo設計語言。在接下來的示例中,我們將對佈局做一些調整,從而更好地支援Material設計。

注意:
這些小部件未來可能移入支援庫中,但目前它們僅示例程式碼提供。

實現機制

在開始編寫自己的程式碼之前,我們需要將滑動選項卡示例程式碼引入專案中。我們需要從SlidingTabsBasicSDK示例專案複製兩個類,將其放入專案中src/main/java下的包目錄:SlidingTabLayout和SlidingTabStrip。

注意:
可以在如下網址找到SlidingTabsBasic SDK 示例:/samples/android-xx/ui/SlidingTabsBasic/。

Google提供瞭如何構建該例的說明,因此我們在這兒不會深入討論此選項卡小部件的細節。我們將其視為來自任何其他庫的小部件。完成此例時,我們將獲得如下圖所示的效果。

滑動選項卡Activity

請注意,選項卡位於Action Bar的下方,並且匹配Action Bar的背景以提供它們是單個元素的外觀。以下兩段程式碼定義了構造選項卡的Activity和佈局。
res/layout/activity_tabs

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.android.common.view.SlidingTabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/primaryGreen"/>
    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

滑動選項卡Activity

public class ActionTabsActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tabs);

        ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
        SlidingTabLayout tabLayout = (SlidingTabLayout) findViewById(R.id.tabs);

        viewPager.setAdapter(new TabsPagerAdapter(this));

        /*
         * SlidingTabLayout 與 ViewPager關聯, 繼承選項卡標題和滾動跟蹤行為
         */
        tabLayout.setViewPager(viewPager);
        tabLayout.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
            @Override
            public int getIndicatorColor(int position) {
                //顯示在每個選項卡位置下方的顏色
                return Color.WHITE;
            }

            @Override
            public int getDividerColor(int position) {
                //透明以隱藏分隔線
                return 0;
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.tabs, menu);
        return true;
    }

    /*
     * 簡單的PagerAdapter,用於顯示帶有靜態圖片的頁面檢視
     */
    private static class TabsPagerAdapter extends PagerAdapter {
        private Context mContext;

        public TabsPagerAdapter(Context context) {
            mContext = context;
        }

        /*
         * SlidingTabLayout 要求此方法定義每個選項卡將顯示的文字
         */
        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "Primary";
                case 1:
                    return "Secondary";
                case 2:
                    return "Tertiary";
                case 3:
                    return "Quaternary";
                case 4:
                    return "Quinary";
                default:
                    return "";
            }
        }

        @Override
        public int getCount() {
            return 5;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView pageView = new ImageView(mContext);
            pageView.setScaleType(ImageView.ScaleType.CENTER);
            pageView.setImageResource(R.drawable.ic_launcher);

            container.addView(pageView);

            return pageView;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return (view == object);
        }
    }
}

在onCreate()內部,我們必須通過向setViewPager()傳遞一個引用來將ViewPager附加到SlidingTabLayout。在內部,佈局將跟蹤ViewPager中的滾動事件,並且通過滾動對應選項卡下方的選擇器欄來反映當前所選頁面中的改動。
從主題中提取選項卡的預設顏色,該顏色通常是不正確的。可以使用TabColorizer例項為選項卡選擇器和選項卡之間的分隔線提供顏色。我們將前者設定為純白色,並且(上圖所示)隱藏了分隔線以符合Material設計外觀。
SlidingTabLayout從附加的PagerAdapter派生其內容。介面卡實現必須重寫getPageTitle()以提供將顯示在每個頁面選項卡上的名稱。

樣式調整
如果執行目前為止的程式碼,選項卡仍然看起來與上圖稍有不同。我們需要對SlidingTabLayout作如下調整,使其符合Material設計:

  • 將選擇器高度降低到2dp。
  • 移除底部的陰影邊框。
  • 移除預設的加粗文字。
  • 新增對已選擇和未選擇選項卡上不同文字顏色的支援。

注意:
還可以使用在本書示例程式碼中提供的不同版本的SlidingTabLayout和SlidingTabStrip檢視已應用的這些調整。

在SlidingTabStrip.java內部,更新如下常量值:

 private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
……
 private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 2;

這會降低選擇器高度並移除陰影。在SlidingTabStrip.java內部,我們需要使用以下程式碼中版本替換createDefaultTabView():
SlidingTabLayout的文字修正


這會移除Holo的預設背景強調以及選項卡的粗體文字。最後,我們需要新增到兩種選項卡顏色的支援。以下程式碼清單指出了新增的相關程式碼。
SlidingTabLayout的文字顏色


新增的這些程式碼用於在應用程式的主題中定義兩種文字顏色屬性,該主題將應用於選擇的選項卡和其他未選擇的選項卡。在每個選項卡選擇事件中,新的updateSelectedTitle()方法基於新的選項設定所有選項卡的文字顏色。目標這些屬性不存在。以下三段程式碼清單分別是在應用程式資源中定義這些屬性,對當前主題中的每個樣式應用顏色,定義使用的顏色值。
res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 為選項卡顏色自定義屬性-->
    <declare-styleable name="SlidingTabLayout">
        <attr name="android:textColorPrimary" />
        <attr name="textColorTabDefault" format="color"/>
        <attr name="textColorTabSelected" format="color"/>
    </declare-styleable>
</resources>

res/values/styles.xml

<resources>
    <!-- 基礎應用程式主題 -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!--提供裝飾主題顏色 -->
        <item name="colorPrimary">@color/primaryGreen</item>
        <item name="colorPrimaryDark">@color/darkGreen</item>
        <item name="colorAccent">@color/accentGreen</item>

        <!-- 移除Action Bar的陰影 -->
        <item name="android:windowContentOverlay">@null</item>

        <!--選項卡的顏色屬性-->
        <item name="textColorTabDefault">@color/tabTextDefault</item>
        <item name="textColorTabSelected">@color/tabTextSelected</item>
    </style>
</resources>

res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="primaryGreen">#259b24</color>
    <color name="darkGreen">#0a7e07</color>
    <color name="accentGreen">#d0f8ce</color>
    <color name="tabTextDefault">#99ffffff</color>
    <color name="tabTextSelected">#ffffff</color>
</resources>

目前在主題中可以看到對已選擇的選項卡應用了純白色,而對其他的選項卡應用了60%的白色。