View滑动是自定义ViewGroup中十分常见的一个功能。Android提供了多种View滑动的方法。

  1. layout方法
  2. offsetLeftAndRight()与offsetTopAndBottom方法
  3. LayoutParams方法
  4. scrollTo 与scrollBy方法
  5. 利用Scroller类
  6. 属性动画
  7. ViewDragHelper

虽然Android提供了这个多方法,实际上他们的原理都是一样的,当触摸到View时,系统记下当前触摸点的坐标;当手指移动时,系统记下移动后的触摸点坐标,从而获取到相对于前一次坐标点的偏移量,并通过偏移量来修改View的坐标。这样不断的重复,就实现了滑动。
这篇文章,主要说下利用Scroller类来实现滑动,Scroller类比起他之前的说的那些方法,他有一个优势在于他的滑动效果是平滑的。

View中的坐标系

在Android中有两种坐标系,一种是Android坐标系,一种是视图坐标系。根据物理学知识,坐标系的选取不同,物体的移动会有不同的效果。
在Android坐标系中,坐标的原点是以屏幕的左上角为(0,0)。这个点向右为x轴正方向,这个点向下为y轴正方向。在滑动处理的时候,我们常常需要获得点的坐标,如果我们用getRawX()和getRawY()来获得该点的坐标,则这个坐标是相对于Android坐标系的坐标。
在视图坐标系中,坐标的原点是父视图的左上角为(0,0)。同样,这个点向右为x轴正方向,这个点向下为y轴正方向。我们常常用getX()和getY()来获得该点的坐标,则这个坐标就是视图坐标系的坐标,也就是说相对于父视图的相对坐标。
最后,我们总结一下这4个方法的具体含义,在后面的滑动时会经常遇到。

getX(): 获取点击事件距离控件左边的距离,即视图坐标
getY(): 获取点击事件距离控件顶边的距离,即视图坐标
getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标
getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标

Scroller类

上面提到了Scroller类实现的是平滑移动,他实现的原理也很简单,他将一个距离的滑动分成了非常多的小距离,每个小距离通过ScrollBy方法进行瞬间移动,但在整体看来却获得了一个平滑移动的效果。
在处理滑动的时候,有一些经常容易弄混的概念,在这里说明一下,分别是scrollBy(int dx ,int dy) 中的dx,dy 以及getScrollX()getScrollY()

dx ,dy表示视图的X,Y方向上各移动dx,dy距离。
dx > 0 表示视图中(View或者ViewGroup)内容从右向左滑动,反之,从左向右滑动
dy > 0 表示视图中(View或者ViewGroup)内容从下向上滑动,反之,从上向下滑动

getScrollX(): 为了好说明这个东西,我们假设我们目前是要处理一个拥有三个并排的match_parent大小LinearLayout的ViewGroup,getScrollX()得到的就是手机屏幕显示区域左上角X坐标减去这个ViewGroup视图左上角X坐标。即如果是第一个LinearLayout则getScrollX是0,如果是第三个LinearLayout,则getScrollX是2个屏幕的宽度综合
getScrollY():按照上面的例子,getScrollY()得到就是手机屏幕显示区域左上角的Y坐标减去这个ViewGroup视图左上角的Y坐标,这里不出意外,都是0,因为ViewGroup的高度就是手机屏幕高度。

在借助Scroller类完成平缓移动的时候,需要我们复写如下几个方法

startScroll(int startX, int startY, int dx, int dy) //使用默认完成时间250ms,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间
computeScrollOffset()//返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。

所以,使用Scroller类需要如下三个步骤:

  1. 初始化Scroller
mScroller = new Scroller(context);
  1. 重写computeScroll()方法,实现模拟滑动
  2. 用startScroll开启模拟过程。

下面,我们通过一个例子,具体说一下Scroller类的使用。这个例子是实现一个简单的ViewPager。我们放置三个并排的LinearLayout,每个都是match_parent。下面的代码是编写了拥有三个不能滑动的LinearLayout。

public class MyViewPager extends ViewGroup{private Scroller mScroller;private int lastX;private int mStart, mEnd;private int mScreenWidth;public MyViewPager(Context context) {super(context);init(context);}public MyViewPager(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {// 初始化ScollermScroller = new Scroller(context);//定义三个match_parent的LinearLayoutLayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);LinearLayout l1 = new LinearLayout(context);l1.setLayoutParams(lp);l1.setBackgroundColor(context.getResources().getColor(android.R.color.holo_orange_dark));LinearLayout l2 = new LinearLayout(context);l2.setLayoutParams(lp);l2.setBackgroundColor(context.getResources().getColor(android.R.color.holo_blue_dark));LinearLayout l3 = new LinearLayout(context);l3.setLayoutParams(lp);l3.setBackgroundColor(context.getResources().getColor(android.R.color.holo_green_dark));addView(l1);addView(l2);addView(l3);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);measureChildren(widthMeasureSpec, heightMeasureSpec);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {int width = 0;int childCount = getChildCount();for(int i = 0 ; i < childCount; i++) {View child = getChildAt(i);child.layout(width,0,width + child.getMeasuredWidth(),child.getMeasuredHeight());width += child.getMeasuredWidth();mScreenWidth = child.getMeasuredWidth();}}
}

关键的部分都注释了,onMeasureonLayout的写法可以参考我之前的ViewGroup的部分。
现在,我们处理onTouchEvent让这个ViewPager可以滑动。

 @Overridepublic boolean onTouchEvent(MotionEvent event) {int x = (int)event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:lastX = x;break;case MotionEvent.ACTION_MOVE:int deltaX = lastX - x;if (isMove(deltaX)) {scrollBy(deltaX, 0);}lastX = x;break;case MotionEvent.ACTION_UP:break;}invalidate();return true;}
private boolean isMove(int deltaX){int scrollX = getScrollX();//滑动到第一屏,不能在向右滑动了if (deltaX < 0) {//从左向右滑动if (scrollX <= 0) {return false;} else if (deltaX + scrollX < 0) {scrollTo(0,0);return false;}}//滑动到最后一屏,不能在向左滑动int leftX = (getChildCount() - 1) * getWidth();if (deltaX > 0) {if (scrollX >= leftX) {return false;} else if (scrollX + deltaX > leftX) {scrollTo(leftX, 0);return false;}}return true;}

好了,现在的ViewPager已经可以滑动了,现在,我们要实现一个弹性滑动的机制,这个就需要借助Scroller类了,所谓弹性滑动,就是当我滑动页面不到一半的时候,页面自动缓慢的滑回去,当滑动的页面超过一半的时候,页面自动缓慢的滑到下一屏。
完成Scroller类的第一步,初始化已经完成,下面,我们完成computeScroll()
看代码:

@Override
public void computeScroll() {super.computeScroll();if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(),mScroller.getCurrY());invalidate();}
}

这个可以看成是一个模板的代码,基本不需要改变,这里,我们直接调用的scrollTo表示移动的是这个ViewGroup中所有的子View, 然后在调用invalidate()方法,调用这个方法主要原因是想间接的调用computeScroll(),因为computeScroll()方法是不会自动调用,而是通过invalidate() -> draw()->computeScroll()
所以需要我们在这个模板代码中调用invalidate(),实现循环获取scrollX和scrollY的目的。最后通过computeScrollOffset来判断是否滑动结束。
接下来,只剩下最后一步,开启滑动,需要我们在上面的onTouchEvent基础,做出如下修改:

@Overridepublic boolean onTouchEvent(MotionEvent event) {int x = (int)event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN://记录下起时点的位置mStart = getScrollX();//滑动结束,停止动画if (mScroller.isFinished()) {mScroller.abortAnimation();}//记录位置lastX = x;break;case MotionEvent.ACTION_MOVE:int deltaX = lastX - x;Log.e("deltaX", deltaX + "");if (isMove(deltaX)) {//在滑动范围内,允许滑动scrollBy(deltaX, 0);}Log.e("getScrollX", getScrollX() + "");lastX = x;break;case MotionEvent.ACTION_UP://判断滑动的距离int dScrollX = checkAlignment();//弹性滑动开启if(dScrollX > 0){//从左向右滑动if (dScrollX < mScreenWidth / 2) {Log.e("dScrollX", dScrollX + "");mScroller.startScroll(getScrollX(),0,- dScrollX,0,500);
//                        scrollBy(-dScrollX,0);}else {mScroller.startScroll(getScrollX(),0,mScreenWidth - dScrollX,0,500);
//                        scrollBy(mScreenWidth - dScrollX,0);}}else {//从右向左滑动if (-dScrollX < mScreenWidth / 2) {mScroller.startScroll(getScrollX(),0, - dScrollX,0,500);
//                        scrollBy(-dScrollX,0);}else{mScroller.startScroll(getScrollX(),0,-mScreenWidth - dScrollX,0,500);
//                        scrollBy(-mScreenWidth - dScrollX,0);}}break;}//重绘invalidate();return true;}
 private int checkAlignment(){//判断滑动的趋势,是向左还是向右,滑动的偏移量是多少mEnd = getScrollX();boolean isUp = ((mEnd - mStart) > 0);int lastPrev = mEnd % mScreenWidth;int lastNext = mScreenWidth - lastPrev;if (isUp) {return lastPrev;}else{return -lastNext;}}

效果如下:

全部代码

自此,我们实现了一个简单可以带弹性滑动的viewPager,我们距离掌控自定义ViewGroup又进了一步。

转载于:https://www.cnblogs.com/qifengshi/p/5808794.html

简单的ViewPager了解Scroller类相关推荐

  1. Scroller类的源码分析以及使用

    Scroller类是用于处理滚动效果的一个类,我们平时使用的ViewPager,可以触摸左右滑动页面,其内部就是使用了Scroller.由于Scroller类是配合View或者ViewGroup的子类 ...

  2. scroller类的用法完全解析以及带源码分析

    上一篇:scrollTo与scrollBy用法以及TouchSlop与VelocityTracker解析 通过上一篇内容对scrollTo与scrollBy用法以及TouchSlop与Velocity ...

  3. android 最新消息滚动,Android 滚动操作Scroller类详解

    Scroller这个类理解起来有一定的困难,刚开始接触Scroller类的程序员可能无法理解Scroller和View系统是怎么样联系起来的.我经过自己的学习和实践,对Scroller的用法和工作原理 ...

  4. 可扩展的TextView,ExpandableTextView与Scroller类的使用

    转载时请注明出处,尊重他人的劳动成果,谢谢. 废话不多说,先上图演示下成果(图有些丑,别见怪): 最近一直在研究Scroller类的使用方法,看了很多遍别人的例子总是感觉不得要领,最后还是自己实践一下 ...

  5. Android平滑移动——Scroller类研究

    Scroller是Android中View平滑移动的一个辅助类,对于刚接触Scroller的人群来说它可能难以理解: 1.它是怎样滑动View的(如何与View关联的)? 2.又是谁触发了它? 其实要 ...

  6. Android Scroller类的详细分析

    尊重原创作者,转载请注明出处: http://blog.csdn.net/gemmem/article/details/7321910 Scroller这个类理解起来有一定的困难,刚开始接触Scrol ...

  7. Scroller类源码解析及其应用(一)

    滑动是我们在自定义控件时候经常遇见的难题,让新手们倍感困惑,这篇文章主要介绍Scroller类的源码,告诉打击这个到底有什么用,怎么使用它来控制滑动.另外,我还会结合一个简单的例子,来看一下这个类的应 ...

  8. Android 仿 窗帘效果 和 登录界面拖动效果 (Scroller类的应用) 附 2个DEMO及源码

    在Android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现.下面要说的就是上次Scroller ...

  9. viewpager 的工具类,内置多种指示器,能够帮你快速完成,轮播图,app 引导页,viewpager 的 tab 指示器等等

    ViewPagerHelper 项目地址:LillteZheng/ViewPagerHelper  简介:这是一个,viewpager 的工具类,内置多种指示器,能够帮你快速完成,轮播图,app 引导 ...

最新文章

  1. SSM综合练习表结构介绍
  2. activiti api文档_【白银人机】Activiti 工作流从入门到入土:完整 hello world 大比拼(API 结合实例讲解)...
  3. 基于php的成绩管理设计(含源文件)
  4. 145元!苹果上架一块儿“天价抹布” ,你会买吗?
  5. mfc在运行的时候为什么没有实例化_Redis(7)——持久化【一文了解】
  6. @Resource kdown
  7. Atitit db deadlock prblm cause and solu 数据库死锁原因与解决   在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享
  8. UE4 遮挡剔除文档
  9. 如何使用Win7系统自带的刻录功能刻录启动光盘
  10. 绘制基本网络图(Networkx)
  11. 在PyTorch训练一个epoch时,模型不能接着训练,Dataloader卡死
  12. Android RecyclerView实现类似于老虎机抽奖,数字滚动等动画效果
  13. 浪潮服务器重置密码方法
  14. Jni native java.lang.UnsatisfiedLinkError:No implementation found for boolean com.xxx.xxx.jni.init()
  15. OpenAI完胜DOTA世界冠军,20行python代码带你领略其魅力
  16. 采用scp命令进行FTP数据迁移
  17. win10硬盘启动从IDE改成ahci后无法启动系统的解决方式
  18. c++中if语句的使用
  19. c语言编程led数码管,数码管显示函数
  20. 操作系统——bilibili王道考研操作系统课程笔记

热门文章

  1. liferay 学习网站 作者为 胡启稳
  2. html插音乐怎么设置样式,是否可以设置html5音频标签的样式?
  3. 学习《apache源代码全景分析》之存储段和存储段组摘录
  4. 《剑指offer》替换空格
  5. 两分钟倒计时(Python)
  6. SQL语句、PL/SQL语句、SQL*PLUS语句结束符号
  7. JVM实用参数(七)CMS收集器
  8. xgboost 多gpu支持 编译
  9. python全栈开发 * 08知识点汇总 * 180608
  10. win下php5.4安装ffmpeg-php扩展