Android零基础入门第70节: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一起使用的,这里先不做过多学习。

    今天就先到这里,如果有问题欢迎留言一起探讨,也欢迎加入Android零基础入门技术讨论微信群,共同成长!

   此文章版权为微信公众号分享达人秀(ShareExpert)——鑫鱻所有,若需转载请联系作者授权,特此声明!

往期总结分享:

Android零基础入门第1节:Android的前世今生

Android零基础入门第2节:Android 系统架构和应用组件那些事

Android零基础入门第3节:带你一起来聊一聊Android开发环境

Android零基础入门第4节:正确安装和配置JDK, 高富帅养成第一招

Android零基础入门第5节:善用ADT Bundle, 轻松邂逅女神

Android零基础入门第6节:配置优化SDK Manager, 正式约会女神

Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅

Android零基础入门第8节:HelloWorld,我的第一趟旅程出发点

Android零基础入门第9节:Android应用实战,不懂代码也可以开发

Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio

Android零基础入门第11节:简单几步带你飞,运行Android Studio工程

Android零基础入门第12节:熟悉Android Studio界面,开始装逼卖萌

Android零基础入门第13节:Android Studio个性化配置,打造开发利器

Android零基础入门第14节:使用高速Genymotion,跨入火箭时代

Android零基础入门第15节:掌握Android Studio项目结构,扬帆起航

Android零基础入门第16节:Android用户界面开发概述

Android零基础入门第17节:文本框TextView

Android零基础入门第18节:输入框EditText

Android零基础入门第19节:按钮Button

Android零基础入门第20节:复选框CheckBox和单选按钮RadioButton

Android零基础入门第21节:开关组件ToggleButton和Switch

Android零基础入门第22节:图像视图ImageView

Android零基础入门第23节:图像按钮ImageButton和缩放按钮ZoomButton

Android零基础入门第24节:自定义View简单使用,打造属于你的控件

Android零基础入门第25节:简单且最常用的LinearLayout线性布局

Android零基础入门第26节:两种对齐方式,layout_gravity和gravity大不同

Android零基础入门第27节:正确使用padding和margin

Android零基础入门第28节:轻松掌握RelativeLayout相对布局

Android零基础入门第29节:善用TableLayout表格布局

Android零基础入门第30节:两分钟掌握FrameLayout帧布局

Android零基础入门第31节:少用的AbsoluteLayout绝对布局

Android零基础入门第32节:新推出的GridLayout网格布局

Android零基础入门第33节:Android事件处理概述

Android零基础入门第34节:Android中基于监听的事件处理

Android零基础入门第35节:Android中基于回调的事件处理

Android零基础入门第36节:Android系统事件的处理

Android零基础入门第37节:初识ListView

Android零基础入门第38节:初识Adapter

Android零基础入门第39节:ListActivity和自定义列表项

Android零基础入门第40节:自定义ArrayAdapter

Android零基础入门第41节:使用SimpleAdapter

Android零基础入门第42节:自定义BaseAdapter

Android零基础入门第43节:ListView优化和列表首尾使用

Android零基础入门第44节:ListView数据动态更新

Android零基础入门第45节:网格视图GridView

Android零基础入门第46节:列表选项框Spinner

Android零基础入门第47节:自动完成文本框AutoCompleteTextView

Android零基础入门第48节:可折叠列表ExpandableListView

Android零基础入门第49节:AdapterViewFlipper图片轮播

Android零基础入门第50节:StackView卡片堆叠

Android零基础入门第51节:进度条ProgressBar

Android零基础入门第52节:自定义ProgressBar炫酷进度条

Android零基础入门第53节:拖动条SeekBar和星级评分条RatingBar

Android零基础入门第54节:视图切换组件ViewSwitcher

Android零基础入门第55节:ImageSwitcher和TextSwitcher

Android零基础入门第56节:翻转视图ViewFlipper

Android零基础入门第57节:DatePicker和TimePicker选择器

Android零基础入门第58节:数值选择器NumberPicker

Android零基础入门第59节:常用三大Clock时钟组件

Android零基础入门第60节:日历视图CalendarView和定时器Chronometer

Android零基础入门第61节:滚动视图ScrollView

Android零基础入门第62节:搜索框组件SearchView

Android零基础入门第63节:值得借鉴学习的选项卡TabHost

Android零基础入门第64节:揭开RecyclerView庐山真面目

Android零基础入门第65节:RecyclerView分割线开发技巧

Android零基础入门第66节:RecyclerView点击事件处理

Android零基础入门第67节:RecyclerView数据动态更新

Android零基础入门第68节:RecyclerView添加首尾视图

Android零基础入门第69节:ViewPager快速实现引导页


发布了107 篇原创文章 · 获赞 56 · 访问量 17万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章