文档章节

ViewPager使用详解1

北ing
 北ing
发布于 2015/10/20 19:23
字数 2742
阅读 78
收藏 5
点赞 0
评论 0

一、简介

1.特点:可以左右滑动的控件,需要PagerAdapter配合使用,由v4包提供
类全名: android.support.v4.view.ViewPager

2.作用:ViewPager作用主要是能使界面左右滑动。比如最常用的使用是做一个引导界面;多张图片的预览或自动变换的图片展示(如淘宝首页面上的广告);viewpager还可以结合fragment作为主界面框架(如微信主界面)。。。

3.viewpager中的几个重要的方法
1)setAdapter()设置ViewPager的适配器。
2)setOnPageChangeListener()设置页面改变事件监听器
3)setCurrentItem(int position) 显示第几页
4)setCurrentItem(int position,boolean smoothScroll) 显示第几页,是否执行滚动动画
5)setOffscreenPageLimit(int limit) 设置脱离屏幕的页面限制--最多同时加载的页面数,limit默认是1
-这个方法解释一下:默认情况下,viewpager在显示页面时,会加载当前显示页和它左右的页面,这也是为什么移动页面时可以显示下一页面一部分。如果想设置其他数,viewpager会加载对应值的页面,比如设置2,则当前显示页和它左两页和右两页都会加载。

二、相关类介绍

1.viewpager相关的几个重要的类
1.1OnPageChangeListener:
–onPageScrollStateChanged(int state) 页面滚动发生或停止时
–onPageScrolled(int position, float offset, int offsetPixes) 滚动时
–onPageSelected(int position) 页面位置确定时

1.2 PagerAdapter
–作用:主要配合ViewPager显示相关的View
–用法:
1)创建类,并继承PagerAdatper
2) 必须实现的方法
–getCount() 获取View的数量
–instantiateItem(ViewGroup, int poistion) 实例化指定位置的View对象
–destroyItem(ViewGroup, int poistion, Object) 删除指定位置的View
–isViewFromObject(View, Object) 判断当前的View是否为Object

1.3FragmentPagerAdaper
–作用:与PagerAdapter的功能相同,不过显示的View改为Fragment
–FragmentStatePagerAdapter: 如果需要处理有很多页,并且数据动态性较大、占用内存较多的情况,应该使用FragmentStatePagerAdapter 保存当前界面,以及下一个界面和上一个界面(如果有),最多保存3个,其他会被销毁掉。
–FragmentPagerAdaper和FragmentStatePagerAdapter的区别:
FragmentPagerAdaper销毁的只是视图,数据没有销毁,FragmentStatePagerAdapter则是全部销毁。拿fragment生命周期来说,使用FragmentPagerAdaper,当滑动页面时,被销毁的fragment会执行到onDestyoyView()但没有执行onDestroy()。而使用FragmentStatePagerAdapter时,会执行onDestroy()方法。后面实例会看到。

三、使用

1.使用步骤:
1) 在布局文件中使用标签

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

2) 代码中增加显示的页面
3) 在Activity里实例化ViewPager组件,并设置它的Adapter
需要注意的是:根据不同的场景,选择不同的适配器父类,不结合fragment使用时使用继承PagerAdapter的适配器,结合Fragment使用时,根据需求,可以选择父类为FragmentPagerAdapter和FragmentStatePagerAdapter,这两个的区别在后面前面已经讲过了。

2.案例一:用viewpager做一个欢迎界面
先看效果图:
这里写图片描述
1) 布局文件

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="${relativePackage}.${activityClass}" >
    <!-- 其显示的页面需要通过PagerAdapter适配器增加或移除 -->
    <android.support.v4.view.ViewPager  android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" />
    <!-- 导航布局 ,小点点 -->
    <LinearLayout  android:id="@+id/ll_dots" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_marginBottom="10dp" android:gravity="center" android:orientation="horizontal" android:padding="10dp" >
    </LinearLayout>
</FrameLayout>

2) 然后看看几个主要的方法体:
第一个是自定义的适配器,各种方法都有注释。

// 声明PageAdapter子类,用于管理viewpager中显示的控件
    class WelComePageAdapter extends PagerAdapter {
        @Override
        public int getCount() {
            // TODO 返回页面的数量
            return views.size();
        }
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            // TODO 获取指定位置的View(UI),并增加到ViewPager中,同时作为当前页面的数据返回
            Log.i("--", "instantiateItem--" + position);
            View view = views.get(position);
            container.addView(view);
            return view;
        }
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            // TODO 当前位置与VIewPager中显示页面的位置的间隔超出一页面,需要将当前位置的页面移除
            Log.i("--", "destroyItem--" + position);
            container.removeView(views.get(position));
        }
        @Override
        public boolean isViewFromObject(View view, Object obj) {
            // TODO 判断当前显示的页面的UI和数据对象是否一致
            return view == obj;
        }
    }

第二个是viewpager滑动监听事件,这里自定义一个类看着清楚点,继承OnPageChangeListener,然后事项相应的方法,特别注意onPageScrolled和onPageSelected这两个方法,onPageScrolled在滑动时会一直回调,根据回调的三个参数,在这里可以进行相应的操作,比如微信那个,当你滑动时,相邻两个标签会有颜色的渐变,就在这里处理的。onPageSelected方法在滑动一个页面停止后回调。

class WelcomPageChangeListner implements OnPageChangeListener {

        @Override
        public void onPageScrollStateChanged(int state) {
            Log.i("--", "onPageScrollStateChanged" + state);
            // TODO 页面滚动状态发生变化事件:开始滚动、停止滚动、正在设置页面
            // ViewPager.SCROLL_STATE_DRAGGING 开始滚动
            // ViewPager.SCROLL_STATE_IDLE 停止滚动
            // ViewPager.SCROLL_STATE_SETTLING 正在设置页面,即将要停止,并且设置当前显示的页面
            // setDot(position);
            // switch (state) {
            // case ViewPager.SCROLL_STATE_DRAGGING:
            // Log.i("--", "-SCROLL_STATE_DRAGGING-");
            // break;
            // case ViewPager.SCROLL_STATE_IDLE:
            // Log.i("--", "-SCROLL_STATE_IDLE-");
            // break;
            // case ViewPager.SCROLL_STATE_SETTLING:
            // Log.i("--", "-SCROLL_STATE_SETTLING-");
            // break;
            // }
        }
        /** * 第一个参数:滚动页面开始的位置 <br> * 第二个参数:两个页面之间滚动的偏移量,范围:0-1<br> * 第三个页面:两个页面之间的滚动的像素偏移量<br> */
        @Override
        public void onPageScrolled(int position, float offset, int offsetPixwls) {
            // TODO 从当前页面位置开始滚动事件
            // Log.i("--", "-onPageScrolled-" + position + ",[" + offset + "],"
            // + "[" + offsetPixwls + "]");
        }
        @Override
        public void onPageSelected(int position) {
            // TODO 指定位置的页面被选择
            setDot(position);
        }
    }

第三个是动态改变导航栏小点点的背景图。

/** * 选择指定位置的导航图片为选择图片,之前选择的导航图片重置为未选择图片 * * @param position */
    private void setDot(int position) {
        ImageView imageView = null;
        // 遍历导航布局张所有的子控件,判断子控件的位置是否未选择位置,若是则设置为选择图片
        for (int i = 0; i < LlDot.getChildCount(); i++) {
            imageView = (ImageView) LlDot.getChildAt(i);// 获取布局中指定位置的子控件
            if (i == position) {
                imageView.setImageResource(R.drawable.page_now);
            } else {
                imageView.setImageResource(R.drawable.page);
            }
        }
    }

其他还有小点点的点击事件,具体看源代码。

3.案例二:viewpager和view结合做一个主界面
效果图:
这里写图片描述
1)布局:最上面的导航栏是一个HorizonalScrollView,里面有一个LinearLayout(因为HorizonalScrollView里只能有一个子视图),然后在LinearLayout定义你想要的导航模块,TextView即可。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="${relativePackage}.${activityClass}" >
    <HorizontalScrollView  android:id="@+id/hScrollView" android:layout_width="match_parent" android:layout_height="50dp" android:layout_marginBottom="2dp" android:scrollbars="none" >
        <LinearLayout  android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="vertical" >
            <!-- 顶部模块的布局 -->
            <LinearLayout  android:id="@+id/navLayout" android:layout_width="wrap_content" android:layout_height="45dp" android:orientation="horizontal" >
                <TextView  android:id="@+id/tv_model1" android:layout_width="150dp" android:layout_height="match_parent" android:gravity="center" android:text="第一" android:textColor="#000" android:textSize="20sp" />
                <TextView  android:id="@+id/tv_model2" android:layout_width="150dp" android:layout_height="match_parent" android:gravity="center" android:text="第二" android:textColor="#000" android:textSize="20sp" />
                <TextView  android:id="@+id/tv_model3" android:layout_width="150dp" android:layout_height="match_parent" android:gravity="center" android:text="第三" android:textColor="#000" android:textSize="20sp" />
                <TextView  android:id="@+id/tv_model4" android:layout_width="150dp" android:layout_height="match_parent" android:gravity="center" android:text="第四" android:textColor="#000" android:textSize="20sp" />
                <TextView  android:id="@+id/tv_model5" android:layout_width="150dp" android:layout_height="match_parent" android:gravity="center" android:text="第五" android:textColor="#000" android:textSize="20sp" />
            </LinearLayout>
            <!-- 指示器控件 -->
            <View  android:id="@+id/view_nav" android:layout_width="150dp" android:layout_height="4dp" android:background="#0cf" />
            <!-- 基准线 -->
            <View  android:layout_width="match_parent" android:layout_height="1dp" android:background="#0cf" />
        </LinearLayout>
    </HorizontalScrollView>
    <android.support.v4.view.ViewPager  android:id="@+id/viewPager" android:layout_below="@id/hScrollView" android:layout_width="match_parent" android:layout_height="match_parent" />
</RelativeLayout>

还需要几个fragment页,这里只展示一个,其他类似:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" android:background="#80f0">
    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="第一个页面" android:textSize="30sp" />
</RelativeLayout>

2)代码中需要做的事情有:
2).1 指示器的移动,也就是模块下那个粗线条的移动,viewpager本身就是可滑动的控件,这里需要指示器随着手指的滑动进行滑动
2).2选择导航模块的位置,将水平滚动到当前模块位置的中心点,
2).3模块点击事件,点击模块,显示相应的page
那么来一步步实现上面的工作:
2.1指示器的移动
要求是指示器随着手指的滑动,起始也就是动态的改变指示器的leftMargin

/** * 指示器移动 * @param position * @param offset */
    private void navIndicateMove(int position, float offset){
        int leftMargin = (int) (indicateParams.width * (position + offset));
        indicateParams.leftMargin = leftMargin;
        navIndicate.setLayoutParams(indicateParams);
    }

2.2选择导航模块的位置,将水平滚动到当前模块位置的中心点
因为整个导航模块都在一个HorizonalScrollView中,HorizonalScrollView中有两个方法可以指定的移动位置,一个是hScrollView.scrollTo(x, y);//非平滑移动
hScrollView.smoothScrollTo(x, y);//平滑移动
所以根据选择的模块得到移动值即可。

/** * 选择导航模块的位置,将水平滚动到当前模块位置的中心点 * * @param position */
    private void selectNav(int position) {
        TextView modelTv = (TextView) navLayout.getChildAt(position);
        int left = modelTv.getLeft();// 获取当前控件的左边位置
        // 怎么放到中间?
// int scWidth = (getResources().getDisplayMetrics().widthPixels / 2);
        int offset = left
                - (getResources().getDisplayMetrics().widthPixels / 2)
                + modelTv.getWidth() / 2;
        hScrollView.smoothScrollTo(offset, 0);// 水平滚动到指定位置

//设置被选中的模块
         for(int i = 0; i < navLayout.getChildCount(); i++){
             modelTv = (TextView) navLayout.getChildAt(i);
             if(i == position){
                 modelTv.setTextColor(Color.argb(100, 255, 0, 0));
             }else{
                 modelTv.setTextColor(Color.argb(255, 0, 0, 0));
             }
         }
    }

3.模块点击事件,点击模块,显示相应的page
给每个模块添加点击事件,然后点击一个模块,只用调用viewPager的
viewPager.setCurrentItem(item);//非平滑滑动
viewPager.setCurrentItem(item, smoothScroll);//平滑滑动

/** * 模块点击事件 */
    private void modelTvClickEvent() {
         for(int i = 0; i < navLayout.getChildCount(); i++){
             final TextView tv = (TextView) navLayout.getChildAt(i);
             tv.setTag(i);
             tv.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = (Integer)tv.getTag();
// selectNav(position);
// navIndicateMove(position, 0);
                    viewPager.setCurrentItem(position, true);
                }
            });
         }
    }

这样,viewpager结合导航栏都可以同步滑动了。

4.案例三:viewpager和fragment结合做一个主界面
效果图:
这里写图片描述
结合fragment使用和结合view使用很相似,只是继承的适配器不同。
上面继承PagerAdapter,这里继承FragmentPagerAdapter或者FragmentStatePagerAdapter。
1)定义一个ListFragment
2)自定义适配器
3)初始化数据源

ListFragment的使用前面有一篇讲过,这里就不附代码了。
来看看自定义适配器:很简单就完事

class InfoFragmentAdapter extends FragmentStatePagerAdapter{
        public InfoFragmentAdapter(FragmentManager fm){
            super(fm);
        }
        @Override
        public int getCount() {
            return fragments.size();
        }
        @Override
        public Fragment getItem(int position) {
            //返回指定位置的碎片
            return fragments.get(position);
        }
}

最后可以借这个案例看一看FragmentPagerAdapter和FragmentStatePagerAdapter区别:
ViewPager:默认创建自己和左右两页,当某个页面与显示的页面间放大点隔大于1,则销毁UI界面(注意,只是UI,里面的数据则不会销毁)
FragmentPagerAdapter:管理fragment时销毁的是UI界面,(fragment生命周期只会执行到onDestroyView)
FragmentStatePagerAdapter:管理fragment时,完全销毁(fragment生命周期会执行onDestroy)

只用把自定义适配器类的父类改一改,然后看fragment各生命周期
打印的日志即可看到区别,这里不做演示了。

现在,结合案例二和案例三就可以做一个现在很流行的主界面了。

源码下载

版权声明:本文为博主原创文章,未经博主允许不得转载。

© 著作权归作者所有

共有 人打赏支持
北ing
粉丝 0
博文 12
码字总数 17332
作品 0
海淀
Android NestedScrollView/ScrollView包裹ViewPager自适应高度

Android NestedScrollView/ScrollView包裹ViewPager自适应高度 当Android的NestedScrollView/ScrollView这类滚动View包裹ViewPager时候,ViewPager中的Fragment包含的又是一系列高度值不固定...

zhangphil ⋅ 05/12 ⋅ 0

打造万能的BannerView(ViewPager)无限轮播图

为什么写这篇文章,因为在网上看到的绝大多数BannerView实现了右无限轮播图,甚至没有实现无限轮播图,说成是无限轮播图,实现了左右无限轮播图的,并没有做性能上的优化。 先看张效果图 工程...

Steven_520 ⋅ 05/11 ⋅ 0

你真的会用Fragment吗?Fragment复用的那些事儿

作者: @怪盗kidou 如需转载不得删除本文中的任何内容(含本段) 如果博客中有不恰当之处欢迎在原文中留言交流 https://www.jianshu.com/p/31f013df7580 大家好,好像距离上次发布博客好像又...

怪盗kidou ⋅ 05/24 ⋅ 0

Android Fragment 监听hide和show

setUserVisibleHint方法耳熟能详,可是仅仅适用于ViewPager,他是ViewPager中手动调用的,但是正常情况下我们该怎么监听呢? 我们也手动调用不就好了 上addFragment代码 private Fragment m...

qq_36523667 ⋅ 05/10 ⋅ 0

从零开发Android视频点播APP

第1章 课程介绍,技术选型 本章将向大家介绍本课程你们学到什么,项目功能模块有哪些,并对技术进行分解,方便大家有针对性的准备和学习,同时会将项目结构设计好,为后面项目的开发做好基础准...

13269051240 ⋅ 05/21 ⋅ 0

从零开发Android视频点播APP视频课程 点播APP实战教程

第1章 课程介绍,技术选型 本章将向大家介绍本课程你们学到什么,项目功能模块有哪些,并对技术进行分解,方便大家有针对性的准备和学习,同时会将项目结构设计好,为后面项目的开发做好基础准...

17087075817 ⋅ 05/14 ⋅ 0

想把GridView添加到ViewPager里面。报空指针异常

用TabLayout和ViewPager关联后,想把GridView添加到ViewPager里面。报空指针异常。不知道哪里写错了 04-12 08:39:37.363 25278-25278/? E/AndroidRuntime: FATAL EXCEPTION: main Process: ...

csl232 ⋅ 04/13 ⋅ 0

ScrollView 嵌套 ViewPager,ViewPager内容不显示问题

解决办法: 1.简单点的就是重写ViewPager //自定义ScrollView嵌套的ViewPager可显示public class ViewPagerForScrollView extends ViewPager { } 但是有个缺点是有时候几个Fragment高度一样了...

王先森oO ⋅ 05/24 ⋅ 0

图片预览组件PhotoView

图片预览组件PhotoView PhotoView是一款图片预览组件,广泛应用于大图的查看。该组件支持图片手势缩放、旋转等功能。它可以很好的和ViewPager、Picasso等组件结合,实现各种复杂的功能。...

大学霸 ⋅ 05/29 ⋅ 0

TabLayout切换标题栏被顶上去的处理

一般都是ScrollView滑动然后包裹TabLayout+ViewPager+Fragment 在Fragment内包含 WebView、ListView、或者GridView之类的 1、设置ScrollView获取焦点: mVideoScrollview.setFocusable(true...

王先森oO ⋅ 05/11 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

Dockerfile基础命令总结

FROM 指定使用的基础base image FROM scratch # 制作base image ,不使用任何基础imageFROM centos # 使用base imageFROM ubuntu:14.04 尽量使用官方的base image,为了安全 LABEL 描述作...

ExtreU ⋅ 昨天 ⋅ 0

存储,对比私有云和公有云的不同

导读 说起公共存储,很难不与后网络公司时代的选择性外包联系起来,但尽管如此,它还是具备着简单和固有的可用性。公共存储的名字听起来也缺乏专有性,很像是把东西直接堆放在那里而不会得到...

问题终结者 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部