ViewPager轻松完成TabHost效果

上一期学习了ViewPager的简单使用,本期一起来学习ViewPager的更多用法。

相信很多同学都使用过今日头条APP吧,一打开主界面就可以看到顶部有很多Tab,然后通过左右滑动来切换,就可以通过ViewPager来完成。当然具体实现又会有很多方式,我们本期就先来学习最简单的Tab切换吧,有一点类似于之前了解的TabHost。

一、PagerTitleStrip与PagerTabStrip

在实际运用中,很多时候只有页面滑动是不够的,还需要有标题栏才够友好。首先来学习一下官方自带的,在android.support.v4包中的两个控件PagerTabStrip与PagerTitleStrip。

上面提到的2个控件,其中PagerTitleStrip是普通文字,PagerTabStrip带有下划线。PagerTabStrip在效果上包含了PagerTitleStrip。

如果只添加PagerTabStrip可以看到只有线,但是它占的布局是有一定高度的,而且默认是不显示标题的,如果要显示出来,需在适配器里重写getPageTitle(int position)方法。关于标题及这条线的颜色,和整个标识View的背景,都可以在代码里设置。

还有一个区别就是,PagerTabStrip可以点击切换View,而PagerTitleStrip不能点击。

接下来通过一个案例来学习PagerTabStrip的使用。

继续再上一期的案例基础上来进行修改,首先修改viewpager_layout.xml文件,修改后的代码如下:

<?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">


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


        <android.support.v4.view.PagerTabStrip
            android:id="@+id/view_pager_tabstrip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </android.support.v4.view.ViewPager>
</LinearLayout>

然后几个页面布局文件不变,既然需要在顶部显示Tab和标题,那就需要我们通过适配器来设置,修改后的ViewPagerAdapter类代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample.adapter;


import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;


/**
 * @创建者 鑫鱻
 * @描述 Android零基础入门到精通系列教程
 * 首发微信公众号分享达人秀(ShareExpert)
 */
public class ViewPagerAdapter extends PagerAdapter {
    private ArrayList<View> mPageLists = null;
    private ArrayList<String> mTitleLists = null;


    public ViewPagerAdapter(ArrayList<View> pageLists, ArrayList<String> titleLists) {
        this.mPageLists = pageLists;
        this.mTitleLists = titleLists;
    }


    @Override
    public int getCount() {
        return (null == mPageLists) ? 0 :mPageLists.size();
    }


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


    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View pageView = mPageLists.get(position);
        container.addView(pageView);
        return pageView;
    }


    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // 将当前位置的View移除
        container.removeView(mPageLists.get(position));
    }


    @Override
    public CharSequence getPageTitle(int position) {
        return (null == mTitleLists && position < mTitleLists.size())
                ? null : mTitleLists.get(position);
    }
}

接着修改ViewPagerActivity,修改的代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample;


import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;


import com.jinyu.cqkxzsxy.android.advancedviewsample.adapter.ViewPagerAdapter;

import java.util.ArrayList;


/**
 * @创建者 鑫鱻
 * @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
 */
public class ViewPagerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {
    private ViewPager mViewPager = null;
    private PagerTabStrip mPagerTabStrip = null;
    private ViewPagerAdapter mAdapter = null;
    private ArrayList<View> mPageLists = null;
    private ArrayList<String> mTitleLists = null;


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


        mViewPager = (ViewPager) findViewById(R.id.view_pager);
        mPagerTabStrip = (PagerTabStrip) findViewById(R.id.view_pager_tabstrip);


        // 装入分页显示hi的View视图
        mPageLists = new ArrayList<>();
        LayoutInflater inflater = getLayoutInflater();
        mPageLists.add(inflater.inflate(R.layout.viewpager_page1, null, false));
        mPageLists.add(inflater.inflate(R.layout.viewpager_page2, null, false));
        mPageLists.add(inflater.inflate(R.layout.viewpager_page3, null, false));
        mPageLists.add(inflater.inflate(R.layout.viewpager_page4, null, false));
        // 装入每个页面的Title
        mTitleLists = new ArrayList<>();
        mTitleLists.add("第一页");
        mTitleLists.add("第二页");
        mTitleLists.add("第三页");
        mTitleLists.add("第四页");


        mAdapter = new ViewPagerAdapter(mPageLists, mTitleLists);
        mViewPager.setAdapter(mAdapter);

        // 更改下划线颜色
        mPagerTabStrip.setTabIndicatorColor(Color.BLUE);
        mViewPager.addOnPageChangeListener(this);
    }


    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        Toast.makeText(this, "第" + (position + 1) + "页", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
}

重新运行程序,可以看到界面中已经出现了久违的Tab和标题,左右滑动页面也可以看到Tab处的线条跟随改变,如下图所示:

从上面的案例可以发现,PagerTabStrip是ViewPager的一个关于当前页面、上一个页面和下一个页面的一个可交互的指示器。其默认显示在顶部,还可以通过android:layout_gravity 属性设置为TOP或BOTTOM将它显示在ViewPager的顶部或底部。

关于PagerTitleStrip的使用,和PagerTabStrip基本差不多,只是布局文件中ViewPager包含的控件不同而已,这里就不再详细说明了,建议自己动手练习,如果有问题,欢迎进Android入门讨论群一起探讨。

二、自定义实现

上面我们使用了系统自带的控件来完成Tab显示,可能有的同学已经发现其与TabHost还是有一定的差别的,上面的Tab只显示3个,而且也不能完全满足实际需求,就需要我们自定义来实现了。

接下来依然通过一个案例来学习如何自定义ViewPager的Tab标签。

继续使用WidgetSample工程的advancedviewsample模块,在src/main/res/layout/目录下创建viewpager_custom_layout.xml文件,在其中填充如下代码片段:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:id="@+id/viewpager_tv_one"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="第一页"
            android:textColor="#000000" />

        <TextView
            android:id="@+id/viewpager_tv_two"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="第二页"
            android:textColor="#000000" />

        <TextView
            android:id="@+id/viewpager_tv_three"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1.0"
            android:gravity="center"
            android:text="第三页"
            android:textColor="#000000" />
    </LinearLayout>


    <ImageView
        android:id="@+id/cursor_img"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scaleType="matrix"
        android:src="@drawable/line" />


    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:flipInterval="30"
        android:persistentDrawingCache="animation" />

</LinearLayout>

在页面顶部有一个线性布局,里面包含3个TextView,也就是ViewPager顶部的3个Tab标签。然后下面紧跟一个ImageView,主要用于指示当前是哪一个Tab标签对应的页面,也就是常说的滑块。最后在最底下是一个ViewPager,其中android:flipInterval属性设置了动画的时间间隔,android:persistentDrawingCache属性指控件的绘制缓存策略,一共有4个可选值,或者选择2个进行组合,分别如下:

  • none:不在内存中保存绘图缓存。
  • animation:只保存动画绘图缓存。
  • scrolling:只保存滚动效果绘图缓存。
  • all:所有的绘图缓存都应该保存在内存中。

然后新建几个页面文件,这里继续使用上一期ViewPager快速实现引导页里面的页面文件,同样使用相同的适配器ViewPagerAdapter。

最后新建ViewPagerCustomActivity.java文件,加载上面新建的布局文件,具体代码如下:

package com.jinyu.cqkxzsxy.android.advancedviewsample;


import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.TextView;


import com.jinyu.cqkxzsxy.android.advancedviewsample.adapter.ViewPagerAdapter;

import java.util.ArrayList;


/**
 * @创建者 鑫鱻
 * @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
 */
public class ViewPagerCustomActivity extends AppCompatActivity
        implements ViewPager.OnPageChangeListener, View.OnClickListener {
    private ViewPager mViewPager = null;
    private ImageView mCursorImg = null;
    private TextView mOneTv = null;
    private TextView mTwoTv = null;
    private TextView mThreeTv = null;


    private ViewPagerAdapter mAdapter = null;
    private ArrayList<View> mPageList = null;


    private int mOffset = 0;// 移动条图片的偏移量
    private int mCurrIndex = 0; // 当前页面的编号
    private int mOneDis = 0; // 移动条滑动一页的距离
    private int mTwoDis = 0; // 滑动条移动两页的距离


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


        // 获取界面组件
        mViewPager = (ViewPager) findViewById(R.id.view_pager);
        mCursorImg = (ImageView) findViewById(R.id.cursor_img);
        mOneTv = (TextView) findViewById(R.id.viewpager_tv_one);
        mTwoTv = (TextView) findViewById(R.id.viewpager_tv_two);
        mThreeTv = (TextView) findViewById(R.id.viewpager_tv_three);


        // 初始化指示器位置
        initCursorPosition();


        mPageList = new ArrayList<>();
        LayoutInflater inflater = getLayoutInflater();
        mPageList.add(inflater.inflate(R.layout.viewpager_page1, null, false));
        mPageList.add(inflater.inflate(R.layout.viewpager_page2, null, false));
        mPageList.add(inflater.inflate(R.layout.viewpager_page3, null, false));


        // 设置适配器
        mAdapter = new ViewPagerAdapter(mPageList);
        mViewPager.setAdapter(mAdapter);


        // 文本框点击监听器
        mOneTv.setOnClickListener(this);
        mTwoTv.setOnClickListener(this);
        mThreeTv.setOnClickListener(this);

        // 页面改变监听器
        mViewPager.addOnPageChangeListener(this);

        // 初始默认第一页
        mViewPager.setCurrentItem(0);
    }


    private void initCursorPosition() {
        // 获取指示器图片宽度
        int cursorWidth = BitmapFactory.decodeResource(getResources(), R.drawable.line).getWidth();


        // 获取分辨率宽度
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        int screenWidth = dm.widthPixels;


        // 计算偏移量
        mOffset = (screenWidth / 3 - cursorWidth) / 2;


        // 设置动画初始位置
        Matrix matrix = new Matrix();
        matrix.postTranslate(mOffset, 0);
        mCursorImg.setImageMatrix(matrix);


        // 计算指示器图片的移动距离
        mOneDis = mOffset * 2 + cursorWidth;// 页卡1 -> 页卡2 偏移量
        mTwoDis = mOneDis * 2;// 页卡1 -> 页卡3 偏移量
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.viewpager_tv_one:
                mViewPager.setCurrentItem(0);
                break;
            case R.id.viewpager_tv_two:
                mViewPager.setCurrentItem(1);
                break;
            case R.id.viewpager_tv_three:
                mViewPager.setCurrentItem(2);
                break;
        }
    }


    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }


    @Override
    public void onPageSelected(int position) {
        // 指示器图片动画设置
        Animation animation = null;
        switch (position) {
            case 0:
                if (1 == mCurrIndex) {
                    animation = new TranslateAnimation(mOneDis, 0, 0, 0);
                } else if (2 == mCurrIndex) {
                    animation = new TranslateAnimation(mTwoDis, 0, 0, 0);
                }
                break;
            case 1:
                if (0 == mCurrIndex) {
                    animation = new TranslateAnimation(mOffset, mOneDis, 0, 0);
                } else if (2 == mCurrIndex) {
                    animation = new TranslateAnimation(mTwoDis, mOneDis, 0, 0);
                }
                break;
            case 2:
                if (0 == mCurrIndex) {
                    animation = new TranslateAnimation(mOffset, mTwoDis, 0, 0);
                } else if (1 == mCurrIndex) {
                    animation = new TranslateAnimation(mOneDis, mTwoDis, 0, 0);
                }
                break;
            default:
                break;
        }

        mCurrIndex = position;
        animation.setFillAfter(true); // True:图片停在动画结束位置
        animation.setDuration(300);
        mCursorImg.startAnimation(animation);
    }


    @Override
    public void onPageScrollStateChanged(int state) {

    }
}

可以发现这里的代码和上期大致相同,只是在其中增加了滑块的位置及动画设置,为3个Tab标签监听了点击事件。其中initCursorPosition()方法主要初始化指示器图标的位置,需要根据屏幕宽度来计算游标显示位置。然后同样设置了页面监听器,主要根据滑动到的页面把游标滑动找指定位置。关于动画的这一块代码,可能有很多新人不太懂,不要太介意这个了,后续会专门进行学习,这里只需要知道可以这样使用就行。

修改程序启动的Activity,运行程序,然后左右滑动屏幕或点击Tab标签,可以看到下图所示界面效果。

通过上面的学习,是不是发现开发一个这样的漂亮界面其实非常简单。除了系统自带的和上面的自定义方法,在实际开发中常会结合Fragment来一起开发,建议后期学完Fragment后再来进一步学习。

除了学习的PagerAdapter适配器,还有另外2个FragmentPagerAdapter和FragmentStatePagerAdapter,主要也是结合Fragment一起使用的,这里先不做过多学习。

原文发布于微信公众号 - 分享达人秀(ShareExpert)

原文发表时间:2017-09-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

扫码关注云+社区

领取腾讯云代金券