如何自定義FragmentTabHost中某一個Tab的點擊效果

問題

iOS上的Tab Bar相信大家都很熟悉了,就是界面底部幾個按鈕,點擊可以切換子頁面。
而在Android上,可以用support v4中的FragmentTabHost來實現類似效果,它繼承自TabHost,每個子頁面都是一個Fragment
今天遇到一個需求,需要在點擊Tab按鈕后,不切換子頁面,而是跳轉到一個新的頁面。通過查閱文檔,FragmentTabHost與其父類TabHost似乎都沒有提供相關函數可以自定義Tab的點擊事件。只有一個當Tab切換完成后的回調監聽OnTabChangeListener,但是切換已經完成為時已晚啊。
在查閱文檔無果后,我祭出了Google,但是卻苦于不知道該如何組織關鍵詞(英文不會,中文離譜),試了幾個關鍵詞依然無果。既然如此,就嘗試看下源碼吧,沒想到最終還是非常簡單就可以解決問題,遂記錄一下。

首先我們來看一下FragmentTabHost的基本使用,來自官方文檔

mTabHost = new FragmentTabHost(getActivity());
mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.fragment1);
mTabHost.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), FragmentStackSupport.CountingFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), LoaderCursorSupport.CursorLoaderListFragment.class, null);

第二行通過setup進行初始化,三四行通過addTab方法添加了兩個Tab,我們就從Tab的添加入手,開始追蹤源碼,看看它都做了什么,是如何設置每個Tab的點擊事件的。以下的源碼經過了簡化,只保留部分關鍵信息。

源碼追蹤

首先是FragmentTabHost,它繼承自TabHost,增加了一個addTab的重載方法,第二個參數接收Fragment的class,用于Fragment相關邏輯。在完成對Fragment的處理后,繼續調用父類TabHostaddTab方法。

public class FragmentTabHost extends TabHost {

    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
        ...    
        addTab(tabSpec);
    }

}

然后來到TabHost,其中成員變量mTabWidget是Tab的容器。在addTab方法中,先取出了Tab的View,然后執行mTabWidget的addView,將Tab添加到容器中。

public class TabHost extends FrameLayout {
    private TabWidget mTabWidget;

    public void addTab(TabSpec tabSpec) {
        ...
        View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();
        ...
        mTabWidget.addView(tabIndicator);
        ...
    }

}

最后來到TabWidget,可以看到它繼承自我們的老朋友LinearLayout,這為Tab View提供了線性排布。在addView方法的最后,我們找到了為每個Tab設置點擊監聽的地方。

public class TabWidget extends LinearLayout {

    @Override
    public void addView(View child) {
        ...
        super.addView(child);

        // TODO: detect this via geometry with a tabwidget listener rather
        // than potentially interfere with the view's listener
        child.setOnClickListener(new TabClickListener(getTabCount() - 1));
        child.setOnFocusChangeListener(this);
    }

}

解決問題

通過一路追蹤源碼,現在我們已經知道了FragmentTabHost是如何設定每個Tab的點擊事件的。最后設置監聽器的地方給了我啟發,假如能夠獲取到Tab View,就可以設置自己的點擊監聽,同時覆蓋掉了系統的監聽器,從而完成自定義點擊效果的任務。
于是從TabWidget反過來查找獲取Tab View的方法。首先TabWidget提供了getChildTabViewAt(int index)方法,可以根據Tab的索引獲取到Tab View。然后通過TabHostgetTabWidget可以獲取到TabWidget 。得到目標Tab View后,設定自己的OnClickListener,搞定任務。
下面代碼演示了設置第一個Tab點擊事件的方法。

mTabHost.getTabWidget().getChildTabViewAt(0).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        
    }
});

總結

系統的源碼真的寫的非常清楚,只要肯耐心看,很多問題都可以迎刃而解。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容