用法概述:

1、换页监听与换页方法
2、懒加载及预加载定制
3、设置间距与添加转场动画
4、轮播、禁止滑动与指示器的配合

这篇和下一篇都是偏向技巧的东西,对于前端开发者来讲,开发的应用是直接面对用户的,用户体验和个性化设计就显得非常重要,同样的一个控件,可能需要你在不同的应用中呈现不同的效果。对于ViewPager来讲,基本上每个应用都会用到的高频控件,而且地位和重要性都比较靠前。要想玩出花来,就是要对它有足够的认识,转场动画就是一项技能,类似我们页面进入退出的转场一样,ViewPager为每一个子View预留了灵活的动画扩展接口。这样只要我们掌握了规则,加上自己的一些创意和算法的相关知识,就能设计出丰富多彩的转场动画来。
本篇博客主要讲的是转场动画,但是有时候却也离不开对子View的间距的设置和控制。所以在正式介绍转场之前,我们有必要介绍一下margin的用法。

设置间距和添加转场动画

造成间距通常是两个变量导致的,一个是padding,一个是margin。而严格意义margin更能体现这个意义。而padding只是子View在自己的地盘上做了偏移。这里我们对ViewPager设置padding,会让子View都会在一个更小的空间里显示,也就是这时候的padding会压缩子View的显示空间(如果对子View设置padding会产生不一样的效果,下边的例子会说)。margin可以实现我们我们队子View的间距调整,这得得益于ViewPager暴露的公开方法,让我们设置。转场动画的实现方式是注入,这就在想要的时候就能生效。

设置间距

默认的ViewPager是从左到右依次连接所有子View,这样的效果就如同粘在一起的一张抽纸,如果我们想让每个子View之间拉开一定的距离,在一般的XML布局之中,我们通常是设置子View之间的margin,但是ViewPager并没有将子View的layout暴露给我们,那么我们是不是就没办法设置了呢?
答案是否定的!虽然我们不能像在XML中灵活设置margin,但是我们依然可以通过代码调整子View的布局,并且还可以添加一些炫酷或者美观的分界线,通过以下几个方法,通常是一些get,set方法:

int getPageMargin() 获得margin数值
void setPageMargin(int marginPixels) 设置margin相应的像素
setPageMarginDrawable(@Nullable Drawable d) 设置相应的margin的drawable
setPageMarginDrawable(@DrawableRes int resId) 设置相应margin的drawable的resId

在设置之前我们最好获取一下,就用第一个方法,如果新设置的值和获得的值一样,我们就不重新设置像素值得子margin了
第二个方法就是设置具体像素的margin了,注意是像素,所以适配的话你最好通过屏幕密度换算一下对应的dp值
第三个方法和第四个方法其实是一个,是利用重载方便用户加载drawable资源,但这里应该注意一下,如果你单独设置Drawable是不会生效的,必须和setPageMargin一起使用,也就是绘制的具体的间隔drawable高是ViewPager的高,宽就是我们通过setPageMargin设置的宽。所以你不设置像素间距,设置的drawable就会落在子View的下面,那么就不用画了,我们来看ViewPager三段源码来验证下:

public void setPageMargin(int marginPixels) {//前边省略mPageMargin = marginPixels;//后边省略
}

第一个方法我们只保留了关键的给属性赋值

public void setPageMarginDrawable(@Nullable Drawable d) {mMarginDrawable = d;//后边省略
}

第二个方法也是只保留了关键的给属性赋值

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);// Draw the margin drawable between pages if needed.
//翻译:如果有需要的话,在子页面之间绘制间隔图案if (mPageMargin > 0 && mMarginDrawable != null && mItems.size() > 0 && mAdapter != null) {//具体绘制drawable省略,这里只分析绘制drawable的启动机制}
}

第三个方法大家都很熟悉,没错,真正的drawable就是通过这个方法绘制出来的。

因为是分析为什么只设置drawable不会生效,我们看绘制onDraw方法的条件判断,这句是判断是否绘制的关键,他有四个因素,之间是与的关系,所以必须同时满足才行:
1、间隔的像素值必须大于0,这个值默认就是0,通过setPageMargin设置(见第一段源码)
2、间隔的图案不能为空,这个值默认就是null,通过setPageMarginDrawable设置(见第二段源码)
3、必须要有子View,没有子View就不存在什么子View间隔了,这个好理解
4、适配器必须不为空,不设置适配器也相当于没有子View,所以也没问题

1、2两点证明必须是要一起使用,drawable才能生效

那到这里有童鞋又会有疑问,四个条件如果只设置margin的像素值,而看源码是不执行绘制的,那么应该不会有间隔吧?实际上是有的,因为间隔值不需要绘制,通过布局将距离拉开就好了,你不用绘制任何东西,显示的就是背景色。具体的逻辑在源码分析章节会详细的说,逻辑较复杂。

其实,子View是Fragment的情况,并不需要设置间隔,这样有画蛇添足之嫌。而设置Margin通常是在一个画廊,就是我们经常用到的ViewPager里边包含了很多图片(图片上也可以有相应的文字信息),这样滑动的时候我们希望它能有更出色的交互体验,这里就引出了另一个技巧就是转场动画,其实margin是配合着转场动画使用的,从而可以实现更加丰富多彩的交互方式。下一节我们来介绍转场动画。

添加转场动画

上边已经说过了,ViewPager的一大用途是滑动的相册。默认的ViewPager的滑动动画,就是左右平行移动,就像在拖拉一幅很长很长的画。
我们知道RecyclerView对于子View的处理可以增加动画,那么可以猜测,同为V4
支持包推出的控件ViewPager也应该会有这样优秀的基因。

ViewPager还是将动画交给了一个接口去处理,既然是接口,如果使用就一定要去实现:

public interface PageTransformer {void transformPage(@NonNull View page, float position);
}

PageTransformer页面变换器,专注处理动画。

注意:由于ViewPager的转场动画利用的是属性动画的原理,而属性动画在不引入第三方支持包的基础上,是支持3.0及以上的,所以在3.0以下的手机,转场动画会被忽视,并不会报错。

页面变换器只需要我们实现一个方法transformPage即可,我们来分析下这个方法的参数是什么意思:

参数page:这个是你即将把动画赋予的子页面
参数position:一看是个float值,就要和作为int值的它区分开,float是个相对位置,它是一个-1到1的值,相对位置提供给开发者,可以控制动画进度和方式。具体的:0的时候是要执行动画的页面处于页面前端并居中的位置,1是要执行动画的页面向右移动到看不见(宽度为一整个页面),-1是要执行的动画向左移动到看不见的位置(宽度为一整个页面),正好对应一个进入动画,一个退出动画。

既然是属性动画,而每个子item的父类都是View,所以这里罗列一些设置属性动画的方法:

setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) 透明度
setTranslationX(float translationX) X轴平移
setTranslationY(float translationY) Y轴平移
setTranslationZ(float translationZ) Z轴平移
setRotation(float rotation) 设置相对中心点的旋转角度,正值按照顺时针转动
setRotationX(float rotationX) 设置相对X轴(水平轴)旋转,正值为从X轴向下看顺时针旋转
setRotationY(float rotationY)设置相对Y轴(竖直轴)旋转,正值为从Y轴向下看顺时针旋转
setPivotX(float pivotX) 设置X轴附近的轴心点的X坐标
setPivotY(float pivotY) 设置Y轴附近的轴心点的Y坐标
setScaleX(float scaleX) 设置X轴方向的缩放比例
setScaleY(float scaleY) 设置Y轴方向的缩放比例

其中setPivotX通常和setRotation和setScaleX (旋转和缩放)组合使用
其中setPivotY通常和setRotation和setScaleY(旋转和缩放)组合使用
注意:默认pivotX和PivotY组成的轴点(mRenderNode )是(0,0),但是你一旦通过设置pivotX或者pivotY改变了默认值,那么将破坏了默认旋转缩放规则,只能显示调用这两个方法了。

下面利用之前讲的提供几个动画实现:

页面变换器实现了,要与ViewPager建立联系才能发挥作用,可以通过以下方法注入:

setPageTransformer(boolean reverseDrawingOrder,@Nullable PageTransformer transformer)
setPageTransformer(boolean reverseDrawingOrder,@Nullable PageTransformer transformer, int pageLayerType)

以上设置变换器有两个重载方法,一个是两个参数,一个是三个参数

参数reverseDrawingOrder:是否执行相反的绘制命令,绘制后面的page在前面page之上,就为false,反之则为true
参数PageTransformer:这个就是我们实现的页面变换器接口,这个提供一个实例
参数pageLayerType:这个是和SurfaceView绘制相关,主要有以下几种图层类型
View.LAYER_TYPE_HARDWARE = 2; 采用硬件加速图层
View.LAYER_TYPE_SOFTWARE = 1; 不论硬件加速是否打开,都会使用软件渲染pipe通道
View.LAYER_TYPE_NONE = 0; 指示View没有图层

为什么会区分这三种模式呢?这里涉及复杂的绘制原理,这里我们只要记住,如果你的ViewGroup中包含了SurfaceView并且没有调用setZOrderOnTop(boolean)方法,那么就会出一些bug了。这个时候为了避免这种情况,你需要调用三个参数的方法,并且需要传递LAYER_TYPE_NONE这个值给pageLayerType。两个参数,系统会默认采用LAYER_TYPE_HARDWARE图层。除此之外,没有影响,就调用默认两个参数的方法。

看下面的实际效果:

//旋转缩放效果class MyPageTransformer implements ViewPager.PageTransformer {private final float MIN_SCALE = 0.75f;@Overridepublic void transformPage(@NonNull View page, float position) {/*缩放旋转*/if (position <= 0f) {page.setTranslationX(0f);page.setScaleX(1f);page.setScaleY(1f);} else if (position <= 1f) {final float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));page.setAlpha(1 - position);page.setPivotY(0.5f * page.getHeight());page.setTranslationX(page.getWidth() * -position);page.setScaleX(scaleFactor);page.setScaleY(scaleFactor);}page.setRotation(360 * position);}}

 class MyPageTransformer implements ViewPager.PageTransformer {@Overridepublic void transformPage(@NonNull View page, float position) {//3D旋转int width = page.getWidth();int pivotX = 0;if (position <= 1 && position > 0) {// right scrollingpivotX = 0;} else if (position == 0) {} else if (position < 0 && position >= -1) {// left scrollingpivotX = width;}//设置x轴的锚点page.setPivotX(pivotX);//设置绕Y轴旋转的角度page.setRotationY(90f * position);}}

//这种转变需要结合我们之前讲过的margin以及padding来实现
//在此之前我们需要在layout.xml文档中关闭剪切子View开关
//android:clipChildren="false"
//并且注意,是ViewPager及其父布局都要设置,否则不生效
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:clipChildren="false"android:layout_height="match_parent"><com.hzx.viewpagerdirector.view.BannerViewPagerandroid:id="@+id/banner"android:layout_width="match_parent"android:clipChildren="false"android:layout_height="match_parent"/>
</LinearLayout>//接下来是转场变换器class MyPageTransformer implements ViewPager.PageTransformer {//        private final float MIN_SCALE = 0.75f;private final float MIN_SCALE = 0.5f;private final float MIN_ALPHA = 0.5f;@Overridepublic void transformPage(@NonNull View page, float position) {float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));float alphaFactor = MIN_ALPHA + (1 - MIN_ALPHA) * (1 - Math.abs(position));page.setScaleY(scaleFactor);page.setAlpha(alphaFactor);}}//然后还有适配器加载子View方法
class MyBaseAdapter extends PagerAdapter {//其他必须继承的方法这里省略,前边的文章有详细介绍@NonNull@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {ImageView image = mImageList.get(position);container.addView(image);ViewGroup.LayoutParams params = image.getLayoutParams();params.width = 1080;params.height = ViewGroup.LayoutParams.MATCH_PARENT;image.setLayoutParams(params);//对,关键是instantiateItem的这个padding的设置方法image.setPadding(200, 80, 200, 80);image.setScaleType(ImageView.ScaleType.FIT_XY);image.setImageDrawable(getResources().getDrawable(mPicIds[position]));return image;}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//最后通过设置margin生效mBaseAdapter = new MyBaseAdapter();mPager.setPageMargin(-300);mPager.setOffscreenPageLimit(3);mPager.setAdapter(mBaseAdapter);mPager.setPageTransformer(true, new MyPageTransformer());}

别着急,如果你的公司还有以下的变态需求(见下方gif):

这个和上一个很相似,不知道读者有没有看到区别:
上一个每个子View都居中,而下边这个第一个靠左,最后一个靠右,其他的才居中
实现这个的关键,是需要我们动态调整padding,同时还需要匹配子View的width,覆写适配器的另一个方法,好了下面看代码:

//除了layout.xml,转换器,初始化设置保持相同外,以下是不同之处,只贴这部分代码:
//主要是适配器的两个方法
class MyBaseAdapter extends PagerAdapter {//其他方法省略,只讲以下两个方法@NonNull@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {ImageView image = mImageList.get(position);container.addView(image);ViewGroup.LayoutParams params = image.getLayoutParams();params.width = 1080;params.height = ViewGroup.LayoutParams.MATCH_PARENT;image.setLayoutParams(params);//这里设置第一页靠左,最后一页靠右,其他居中的效果if (position == 0) {image.setPadding(50, 80, 200, 80);} else if (position == mImageList.size() - 1) {image.setPadding(200, 80, 50, 80);} else {image.setPadding(200, 80, 200, 80);}image.setScaleType(ImageView.ScaleType.FIT_XY);image.setImageDrawable(getResources().getDrawable(mPicIds[position]));return image;}@Overridepublic float getPageWidth(int position) {super.getPageWidth(position);float width = 1.0f;if (position == 0 || position == mImageList.size() - 1) {//这个方法有些童鞋可能熟悉,这个是ViewPager在测量确定子View所占宽度的时候用到的,//由于在instantiateItem方法中第一个和最后一个特殊处理了,导致在展示的时候,//第一个的右边间距过大,最后一个左边间距过大,为了调整这部分差异,//需要实现这个方法,并且对第一个和最后一个宽度进行计算//下边代码中150,正是第一个和最后一个异常间距的差异值width = (float) (DensityUtil.getScreenWidth(AdapterActivity.this) - 150)/ DensityUtil.getScreenWidth(AdapterActivity.this);}return width;}

好了经过以上调整,即使是这么变态的效果,我们也能轻松实现,只要大家多想想,多结合一些动画方式,就能定制化各种效果。

下边我们总结一下本章的内容:

1、增加转场效果,你需要实现ViewPager.PageTransformer的唯一方法,在这个方法中的参数值区间是[-1,1],其中-1是从左边退出到头,-1到0,是左边到中间的一个进度值,0是正好稳定显示的位置,0到1是中间到右边的一个进度值,1就是正好右边退出到头;根据这个关系,我们结合属性动画,就能实现更加丰富多彩的转场动画效果了;

2、padding可以在适配器的加载方法中设置,通过view.setPadding();方法能够调整当前页面的相对位置;

3、如果你想让左右两边的View部分进入当前View,首先你需要关闭clipChildren开关,然后设置负的margin值,不关闭的话,默认会剪切,那么负的就会被剪切掉;导致效果失效;

ViewPager应用系列马上就迎来最后一篇博客了,撒花,

ViewPager (七) 让ViewPager用起来更顺滑——轮播、禁止滑动与指示器的配合

ViewPager(六)让ViewPager用起来更顺滑——设置间距与添加转场动画相关推荐

  1. ViewPager系列之ViewPager无限循环滑动

    目前ViewPager实现无限循环有2种方法,直接上具体方法: 方法1:重写 PagerAdapter 中的 getCount() 方法.其实只是在计算item 数目的时候给了一个很大的数,然后通过调 ...

  2. android viewpager button 导航,ViewPager做底部导航

    做了一个项目,需要使用底部导航功能,仿微信效果,可以左右滑动切换页面.好处就是比较适合用户手势操作,操作效果比较好:缺点是viewpager控件需要提前加载下一个页面,最少提前加载一个页面,为了保证左 ...

  3. Android ViewPager 里有子ViewPager的事件冲突

    在Android应用中有时候要用到类似网易新闻左右滑动页面且页面里又有左右滑动的图片功能,我不知道网易是怎么实现的,本人的做法是外面的BaseFragmentActivity布局就是TabViewPa ...

  4. Android嵌套式ViewPager,垂直ViewPager

    In this tutorial, we'll be implementing android nested ViewPager i.e a ViewPager within a ViewPager. ...

  5. 好用计算机,六款让你电脑更好用的软件

    电脑已经是我们生活中离不开的工具,但是广告的横行让我们的电脑不那么好用.我们的日常工作中也有很多任务或工作无法一步到位.今天小编就为大家推荐六款电脑软件,让我们的电脑变得更加好用. 六款让你电脑更好用 ...

  6. ViewPager套嵌RecyclerView出现滑动条目时滑不动原因

    ViewPager套嵌RecyclerView出现滑动条目时滑不动原因 在滑动条目时,在有TextView地方滑不动,在条目其他空白地方却可以滑动,去除 android:singleLine=&quo ...

  7. java安装好了打不开机_劝告大家!早餐打豆浆,黄豆泡好直接打不对,教你1招,豆浆香浓更丝滑...

    " 导语:打豆浆泡好直接打?难怪豆浆没有早餐店的好喝,教你正确做法 适当吃豆制品对于大人小孩都有好处,豆浆作为其中的一种,更是我家早餐常会喝的,香浓顺滑的豆浆,搭配油条或者是饼,美好的早餐便 ...

  8. 谷歌最新视频抠图术:影子烟雾都能抠,添加水印更顺滑,UP主剪辑利器 | 开源...

    杨净 发自 凹非寺 量子位 报道 | 公众号 QbitAI 如何更顺滑的添加水印? 谷歌的这项新技术,让文本简直就像贴在地面上,哪怕是在沙尘横飞的场景里. 方法也很简单. 只需输入一段视频,和指定对象 ...

  9. 三星android 8.0 流畅,三星Galaxy S8使用安卓Android 8.0:更顺滑更流畅

    ­ 虽然三星给旗下手机升级新版Android系统的速度并不是最快的,但一般来说承诺了升级就必定会在约定的时间内做到. ­ 目前最新的消息显示,三星已经开始为旗下的Gaalxy S8和Galaxy No ...

最新文章

  1. Failed to resolve:aar
  2. python连接文本文件_Python连接文本文件
  3. 人生第一份Offer,国企、私企、外企该选择哪一个?
  4. python红包游戏_脑力2048红包版
  5. VTK:多边形曲面等高线插值用法实战
  6. Xcode 4.4 的新特性 | LLVM 4.0 的新语法
  7. OCP 论证书籍 -- ORACLE DATABASE 10G OCP Certification All-in - One Exam Guide
  8. Newbe.ObjectVisitor 0.2.10 发布,更花里胡哨
  9. 后端学习 - 并发编程
  10. 【 Grey Hack 】综合工具 shellOs
  11. 数据库最最常用语句(10年工作笔记)
  12. php怎么学小程序,PHP学习之路之Hello World小程序
  13. AirServer for mac如何实现无线投屏
  14. 读《活着》余华---笔记
  15. 这么好用的PDF密码移除器,你知道吗
  16. 推荐几个后台管理界面
  17. glassfish配置错误问题
  18. 洛达悦虎1562M各个版本固件升级教程
  19. 个人台式计算机的主要安装步骤,台式机固态硬盘如何安装 台式机固态硬盘安装教程【图文详解】...
  20. 聚合和分组F,Q和事物,cookie,session

热门文章

  1. 计算机应用基础2004年,计算机应用基础2004年上半年全国试题参考答案1
  2. 南京大学计算机科学与技术系罗金宇,2017年江苏省大学生计算机设计大赛.PDF
  3. 分别兼容IE7,IE8,IE9,IE10,超简单一看就会
  4. 根据url动态生成二维码
  5. 清华大学计算机系女生人数,2013年清华大学在各省市录取人数及男女比例
  6. python|面向对象(一)
  7. vue实现图片切换效果
  8. L2-037 包装机
  9. VC++实现获取本地主机网卡信息
  10. CTDC 2017 首席技术官领袖峰会 | 技术、探索、创新