zoukankan      html  css  js  c++  java
  • 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快速实现引导页

  • 相关阅读:
    第21,22章:出现次数超过一半的数字,最短摘要的生成
    String的方法
    最长公共子串
    第29章:字符串编辑距离
    字符串转换为整数
    第11章:最长公共子序列(LCS:Longest Common Subsequence)
    django开发_七牛云CNAME解析
    django开发_七牛云图片管理
    FastDFS分布式文件系统
    requests获取响应时间(elapsed)与超时(timeout)、小数四舍五入
  • 原文地址:https://www.cnblogs.com/cqkxzsxy/p/7605578.html
Copyright © 2011-2022 走看看