前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ViewPager轻松完成TabHost效果

ViewPager轻松完成TabHost效果

作者头像
分享达人秀
发布2018-02-05 14:32:25
1.5K0
发布2018-02-05 14:32:25
举报
文章被收录于专栏:分享达人秀分享达人秀

上一期学习了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文件,修改后的代码如下:

代码语言:javascript
复制
<?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类代码如下:

代码语言:javascript
复制
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,修改的代码如下:

代码语言:javascript
复制
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文件,在其中填充如下代码片段:

代码语言:javascript
复制
<?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文件,加载上面新建的布局文件,具体代码如下:

代码语言:javascript
复制
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一起使用的,这里先不做过多学习。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 分享达人秀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档