TabHost和ViewPager

1 篇文章 0 订阅
0 篇文章 0 订阅

TabHost

从可视化UI编辑器中拖一个TabHost:

<TabHost
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/tabHost">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </TabWidget>
        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
      <!--也可以通过tabHost.addTab(tabSpec)添加tab内容-->
            <LinearLayout
                android:id="@+id/tab1"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
            </LinearLayout>
            <LinearLayout
                android:id="@+id/tab2"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
            </LinearLayout>
        </FrameLayout>
    </LinearLayout>
</TabHost>

通过TabHost可以简单的实现tab页签效果:

tabHost = (TabHost) findViewById(android.R.id.tabhost);
tabHost.setup(); //如果TabActivity的话,直接getTabHost(),无需setup。

TabSpec tabSpec = tabHost.newTabSpec(tag).setIndicator(indicator).setContent(viewId);
tabHost.addTab(tabSpec);

其中TabSpec的setIndicator和setContent分别重载了三个不同的方法:

public TabSpec setIndicator(CharSequence label)
public TabSpec setIndicator(CharSequence label, Drawable icon)
public TabSpec setIndicator(View view)

public TabSpec setContent(int viewId) 
public TabSpec setContent(TabContentFactory contentFactory)
public TabSpec setContent(Intent intent)

当点击tab页签头时,页签的内容发生变化,可以通过设置TabHost.OnTabChangedListener来监听这个变化:

tabHost.setOnTabChangedListener(new OnTabChangeListener() {
    @Override
    public void onTabChanged(String tabId) {
       // do something
    }
});

ViewPager

ViewPager是v4包定义的view,布局xml定义:

<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</android.support.v4.view.ViewPager>

使用ViewPager需要设置它的PagerAdapter,可以设置ViewPager.OnPageChangeListener来监听page页面的切换:

adapter = new MyPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(adapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        // do something
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }
});

在实现ViewPager的adapter时,可以选择继承PagerAdapter、FragmentPagerAdapter和FragmentStatePagerAdapter。
如果tab是一个Fragment时,选择后两者;如果tabs是不同的Fragment时,选择FragmentPagerAdapter;如果是相同的Fragment时选择FragmentStatePagerAdapter。
至于FragmentPagerAdapter和FragmentStatePagerAdapter的详细区别,参考这里


TabHost和ViewPager结合使用

具体参考如下代码,代码有个小bug,是由于布局引起的,会在后面提出两种解决办法:
activity布局文件:activity_my_tab.xml

<?xml version="1.0" encoding="utf-8"?>
<TabHost 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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </TabWidget>

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">

            <android.support.v4.view.ViewPager
                android:id="@+id/pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

            </android.support.v4.view.ViewPager>
        </FrameLayout>

    </LinearLayout>
</TabHost>

activity类文件:MyTabActivity.java

public class MyTabActivity extends FragmentActivity {
    private MyPagerAdapter adapter;
    private ViewPager viewPager;
    private TabHost tabHost;

    public static final String[] tabs = {"Tab0", "Tab1", "Tab2", "Tab3", "Tab4" };

    @Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.activity_my_tab);

        initTabs();

        initPager();
    }

    private void initTabs() {
        tabHost = (TabHost) findViewById(android.R.id.tabhost);
        tabHost.setup();

        for (int i = 0; i < tabs.length; i++) {
            TabSpec tabSpec = tabHost.newTabSpec(tabs[i]).setIndicator(tabs[i])
                    .setContent(android.R.id.tabcontent);
            tabHost.addTab(tabSpec);
        }

        tabHost.setOnTabChangedListener(new OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
                if (viewPager != null) {
                    viewPager.setCurrentItem(tabHost.getCurrentTab());
                }
            }
        });
    }

    private void initPager() {
        adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager = (ViewPager) findViewById(R.id.pager);
        viewPager.setAdapter(adapter);
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if (viewPager != null) {
                    tabHost.setCurrentTab(position);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }


    public class MyPagerAdapter extends FragmentPagerAdapter {
        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int i) {
            return MyFragment.newInstance("Tab", Integer.toString(i));
        }

        @Override
        public int getCount() {
            return tabs.length;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "PageTitle " + (position);
        }
    }

    public static class MyFragment extends Fragment {
        private static final String ARG_PARAM1 = "param1";
        private static final String ARG_PARAM2 = "param2";

        private String mParam1;
        private String mParam2;

        public static MyFragment newInstance(String param1, String param2) {
            MyFragment fragment = new MyFragment();
            Bundle args = new Bundle();
            args.putString(ARG_PARAM1, param1);
            args.putString(ARG_PARAM2, param2);
            fragment.setArguments(args);
            return fragment;
        }

        public MyFragment() {
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (getArguments() != null) {
                mParam1 = getArguments().getString(ARG_PARAM1);
                mParam2 = getArguments().getString(ARG_PARAM2);
            }
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_my, container,
                    false);
            Bundle args = getArguments();
            ((TextView) rootView.findViewById(R.id.tv)).setText(mParam1 + " : " + mParam2);
            return rootView;
        }
    }

}

注意:以上的Fragment和FragmentManager类均是v4包的

tab中所显示的fragment的布局文件:fragment_my.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:gravity="center"
        android:id="@+id/tv" />
</LinearLayout>

运行后显示:
图一

bug出现,初次显示时tab的内容并没有显示出来
只有点击其他页签再返回,该页签才显示出来。
网上有解决办法是:

@Override
    protected void onCreate(Bundle arg0) {
        super.onCreate(arg0);
        setContentView(R.layout.activity_my_tab);

        initTabs();

        initPager();

        tabHost.setCurrentTab(1); 
        tabHost.setCurrentTab(0);
    }

虽然这种办法解决了bug,但却做了无用功,只有加载其他页签才能返回显示第一个页签。

该问题其实由于初次显示时,TabHost里面内容FrameLayout“遮挡”了ViewPager的显示。所以我的解决方法是:

  • 方法1:
    调整activity_my_tab.xml,将FrameLayout包含ViewPager改为:ViewPager包含FragmLayout,高度属性也调换了。
 <android.support.v4.view.ViewPager
     android:id="@+id/pager"
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1">

     <FrameLayout
         android:id="@android:id/tabcontent"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
     </FrameLayout>
</android.support.v4.view.ViewPager>
  • 方法2:
    • 调整activity_my_tab.xml,将FrameLayout包含ViewPager改为:ViewPager和FragmLayout并列为兄弟,并修改它们的属性
    • 修改MyTabActivity.java ,增加一个实现了 TabHost.TabContentFactory接口的静态内部类DummyTabContent,并将initTabs方法中的setContent(android.R.id.tabcontent)改为setContent(dummyTabContent),具体参考下面修改后的代码。
 <FrameLayout
     android:id="@android:id/tabcontent"
     android:layout_width="0dp"
     android:layout_height="0dp" >
 </FrameLayout>
 <android.support.v4.view.ViewPager
     android:id="@+id/pager"
     android:layout_width="match_parent"
     android:layout_height="0dp"
     android:layout_weight="1">
 </android.support.v4.view.ViewPager>
private void initTabs() {
    tabHost = (TabHost) findViewById(android.R.id.tabhost);
    tabHost.setup();

    // 当TabHost和ViewPager组合使用时,如果ViewPager放在tabcontent的FrameLayout中,
    // tab第一次显示时,将无法显示它的子视图ViewPager。因此,在布局中,将FrameLayout显示为宽高为0dp,
    // ViewPager作为它的兄弟视图。
    // 生成一个假的tab内容,填充id为android.R.id.tabcontent的FrameLayout,
    // 真正的tab内容显示在它的兄弟视图ViewPager。
    TabHost.TabContentFactory dummyTabContent = new DummyTabContent(this);
    for (int i = 0; i < tabs.length; i++) {
        TabSpec tabSpec = tabHost.newTabSpec(tabs[i]).setIndicator(tabs[i])
                .setContent(dummyTabContent);
        tabHost.addTab(tabSpec);
    }

    tabHost.setOnTabChangedListener(new OnTabChangeListener() {
        @Override
        public void onTabChanged(String tabId) {
            if (viewPager != null) {
                viewPager.setCurrentItem(tabHost.getCurrentTab());
            }
        }
    });
}

private static final class DummyTabContent implements TabHost.TabContentFactory{
   private Context mContext;

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

    @Override
    public View createTabContent(String tag) {
        View v = new View(mContext);
        v.setMinimumWidth(0);
        v.setMinimumHeight(0);
        return v;
    }
}

修改后显示:
图二


Tab页签头的位置

如果想将tab页签头显示在下方,则只需将TabWidget和ViewPager的位置调换,此时页签头和页签内容之间没有蓝条隔开,Android官方是推荐页签头显示在上方的。效果如下:
图片三


关于FragmentTabHost和ViewPager

不建议FragmentTabHost和ViewPager一同使用,因为FragmentTabHost会替我们创建并管理Fragment,而使用ViewPager需要实现PagerAdapter,实现PagerAdapter、FragmentPagerAdapter和FragmentStatePagerAdapter需要我们自己创建并返回一个Fragment,这就意味着一个要显示的Fragment的被创建了两次。

FragmentTabHost使用方法
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args)添加Fragment的class,并在方法private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft)中创建class对应的Fragment。具体可参考FragmentTabHost的源码。

FragmentPagerAdapter和FragmentStatePagerAdapter会为我们管理Fragment,但是需要我们自己创建Fragment,即复写方法public Fragment getItem(int position)


Android Tab的其他实现

可参考:Android tab based applications series

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值