自定义TabHost中选项卡的点击监听事件

而在Android上,可以用support v4中的 FragmentTabHost 来实现类似效果,它继承自 TabHost ,每个子页面都是一个 Fragment 。
今天遇到一个需求,需要在点击Tab按钮后,不切换子页面,而是跳转到一个新的页面。通过查阅文档, FragmentTabHost 与其父类 TabHost 似乎都没有提供相关函数可以自定义Tab的点击事件。只有一个当Tab切换完成后的回调监听OnTabChangeListener ,但是切换已经完成为时已晚啊。

看下源码吧,源源码还是非常简单就可以解决问题,遂记录一下。

首先我们来看一下 FragmentTabHost 的基本使用,来自 官方文档(http://developer.android.com/reference/android/support/v4/app/FragmentTabHost.html)

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的处理后,继续调用父类 TabHost 的 addTab 方法。

首先是 FragmentTabHost ,它继承自 TabHost ,增加了一个 addTab 的重载方法,第二个参数接收Fragment的class,用于Fragment相关逻辑。在完成对Fragment的处理后,继续调用父类 TabHost 的 addTab 方法。

public class FragmentTabHost extends TabHost {
 
    public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
        tabSpec.setContent(new DummyTabFactory(mContext));
        String tag = tabSpec.getTag();

        TabInfo info = new TabInfo(tag, clss, args);

        if (mAttached) {
            // If we are already attached to the window, then check to make
            // sure this tab's fragment is inactive if it exists.  This shouldn't
            // normally happen.
            info.fragment = mFragmentManager.findFragmentByTag(tag);
            if (info.fragment != null && !info.fragment.isDetached()) {
                FragmentTransaction ft = mFragmentManager.beginTransaction();
                ft.detach(info.fragment);
                ft.commit();
            }
        }

        mTabs.add(info);
        addTab(tabSpec);
    }
 
}
然后来到 TabHost ,其中成员变量 mTabWidget 是Tab的容器。在 addTab 方法中,先取出了Tab的View,然后执行mTabWidget的addView,将Tab添加到容器中。

public class TabHost extends FrameLayout {
  private TabWidget mTabWidget;
    /**
     * Add a tab.
     * @param tabSpec Specifies how to create the indicator and content.
     */
    public void addTab(TabSpec tabSpec) {

        if (tabSpec.mIndicatorStrategy == null) {
            throw new IllegalArgumentException("you must specify a way to create the tab indicator.");
        }

        if (tabSpec.mContentStrategy == null) {
            throw new IllegalArgumentException("you must specify a way to create the tab content");
        }
        View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();
        tabIndicator.setOnKeyListener(mTabKeyListener);

        // If this is a custom view, then do not draw the bottom strips for
        // the tab indicators.
        if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) {
            mTabWidget.setStripEnabled(false);
        }

        mTabWidget.addView(tabIndicator);
        mTabSpecs.add(tabSpec);

        if (mCurrentTab == -1) {
            setCurrentTab(0);
        }
    }
}
最后来到 TabWidget ,可以看到它继承自我们的老朋友LinearLayout,这为Tab View提供了线性排布。在 addView 方法的最后,我们找到了为每个Tab设置点击监听的地方。
public class TabWidget extends LinearLayout {
    @Override
    public void addView(View child) {
        if (child.getLayoutParams() == null) {
            final LinearLayout.LayoutParams lp = new LayoutParams(
                    0,
                    ViewGroup.LayoutParams.MATCH_PARENT, 1.0f);
            lp.setMargins(0, 0, 0, 0);
            child.setLayoutParams(lp);
        }

        // Ensure you can navigate to the tab with the keyboard, and you can touch it
        child.setFocusable(true);
        child.setClickable(true);

        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);
    }
}
为了解决Tabhost的点击监听事件的发生,通过一路追踪源码,现在我们已经知道了 FragmentTabHost 是如何设定每个Tab的点击事件的。最后设置监听器的地方给了我启发,假如能够获取到Tab View,就可以设置自己的点击监听,同时覆盖掉了系统的监听器,从而完成自定义点击效果的任务。
于是从 TabWidget 反过来查找获取Tab View的方法。首先 TabWidget 提供了getChildTabViewAt(int index) 方法,可以根据Tab的索引获取到Tab View。然后通过 TabHost 的 getTabWidget 可以获取到TabWidget 。得到目标Tab View后,设定自己的OnClickListener,搞定任务。
下面代码演示了设置第一个Tab点击事件的方法。

mTabHost.getTabWidget().getChildTabViewAt(0).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
 
    }
});
总结:源码中还有很多可以用到的方法,大家如果需要可以自己去了解一下源码。







你可能感兴趣的:(源码,android,tabhost)