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
  1. mScroller = new Scroller(context);
  1. 重写computeScroll()方法,实现模拟滑动
  2. 用startScroll开启模拟过程。

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

  1. public class MyViewPager extends ViewGroup{
  2. private Scroller mScroller;
  3. private int lastX;
  4. private int mStart, mEnd;
  5. private int mScreenWidth;
  6. public MyViewPager(Context context) {
  7. super(context);
  8. init(context);
  9. }
  10. public MyViewPager(Context context, AttributeSet attrs) {
  11. super(context, attrs);
  12. init(context);
  13. }
  14. public MyViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
  15. super(context, attrs, defStyleAttr);
  16. init(context);
  17. }
  18. private void init(Context context) {
  19. // 初始化Scoller
  20. mScroller = new Scroller(context);
  21. //定义三个match_parent的LinearLayout
  22. LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
  23. LinearLayout l1 = new LinearLayout(context);
  24. l1.setLayoutParams(lp);
  25. l1.setBackgroundColor(context.getResources().getColor(android.R.color.holo_orange_dark));
  26. LinearLayout l2 = new LinearLayout(context);
  27. l2.setLayoutParams(lp);
  28. l2.setBackgroundColor(context.getResources().getColor(android.R.color.holo_blue_dark));
  29. LinearLayout l3 = new LinearLayout(context);
  30. l3.setLayoutParams(lp);
  31. l3.setBackgroundColor(context.getResources().getColor(android.R.color.holo_green_dark));
  32. addView(l1);
  33. addView(l2);
  34. addView(l3);
  35. }
  36. @Override
  37. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  38. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  39. measureChildren(widthMeasureSpec, heightMeasureSpec);
  40. }
  41. @Override
  42. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  43. int width = 0;
  44. int childCount = getChildCount();
  45. for(int i = 0 ; i < childCount; i++) {
  46. View child = getChildAt(i);
  47. child.layout(width,0,width + child.getMeasuredWidth(),child.getMeasuredHeight());
  48. width += child.getMeasuredWidth();
  49. mScreenWidth = child.getMeasuredWidth();
  50. }
  51. }
  52. }

关键的部分都注释了,onMeasureonLayout的写法可以参考我之前的ViewGroup的部分。

现在,我们处理onTouchEvent让这个ViewPager可以滑动。

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. int x = (int)event.getX();
  4. switch (event.getAction()) {
  5. case MotionEvent.ACTION_DOWN:
  6. lastX = x;
  7. break;
  8. case MotionEvent.ACTION_MOVE:
  9. int deltaX = lastX - x;
  10. if (isMove(deltaX)) {
  11. scrollBy(deltaX, 0);
  12. }
  13. lastX = x;
  14. break;
  15. case MotionEvent.ACTION_UP:
  16. break;
  17. }
  18. invalidate();
  19. return true;
  20. }
  1. private boolean isMove(int deltaX){
  2. int scrollX = getScrollX();
  3. //滑动到第一屏,不能在向右滑动了
  4. if (deltaX < 0) {//从左向右滑动
  5. if (scrollX <= 0) {
  6. return false;
  7. } else if (deltaX + scrollX < 0) {
  8. scrollTo(0,0);
  9. return false;
  10. }
  11. }
  12. //滑动到最后一屏,不能在向左滑动
  13. int leftX = (getChildCount() - 1) * getWidth();
  14. if (deltaX > 0) {
  15. if (scrollX >= leftX) {
  16. return false;
  17. } else if (scrollX + deltaX > leftX) {
  18. scrollTo(leftX, 0);
  19. return false;
  20. }
  21. }
  22. return true;
  23. }

好了,现在的ViewPager已经可以滑动了,现在,我们要实现一个弹性滑动的机制,这个就需要借助Scroller类了,所谓弹性滑动,就是当我滑动页面不到一半的时候,页面自动缓慢的滑回去,当滑动的页面超过一半的时候,页面自动缓慢的滑到下一屏。

完成Scroller类的第一步,初始化已经完成,下面,我们完成computeScroll()

看代码:

  1. @Override
  2. public void computeScroll() {
  3. super.computeScroll();
  4. if (mScroller.computeScrollOffset()) {
  5. scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
  6. invalidate();
  7. }
  8. }

这个可以看成是一个模板的代码,基本不需要改变,这里,我们直接调用的scrollTo表示移动的是这个ViewGroup中所有的子View, 然后在调用invalidate()方法,调用这个方法主要原因是想间接的调用computeScroll(),因为computeScroll()方法是不会自动调用,而是通过invalidate() -> draw()->computeScroll()

所以需要我们在这个模板代码中调用invalidate(),实现循环获取scrollX和scrollY的目的。最后通过computeScrollOffset来判断是否滑动结束。

接下来,只剩下最后一步,开启滑动,需要我们在上面的onTouchEvent基础,做出如下修改:

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. int x = (int)event.getX();
  4. switch (event.getAction()) {
  5. case MotionEvent.ACTION_DOWN:
  6. //记录下起时点的位置
  7. mStart = getScrollX();
  8. //滑动结束,停止动画
  9. if (mScroller.isFinished()) {
  10. mScroller.abortAnimation();
  11. }
  12. //记录位置
  13. lastX = x;
  14. break;
  15. case MotionEvent.ACTION_MOVE:
  16. int deltaX = lastX - x;
  17. Log.e("deltaX", deltaX + "");
  18. if (isMove(deltaX)) {
  19. //在滑动范围内,允许滑动
  20. scrollBy(deltaX, 0);
  21. }
  22. Log.e("getScrollX", getScrollX() + "");
  23. lastX = x;
  24. break;
  25. case MotionEvent.ACTION_UP:
  26. //判断滑动的距离
  27. int dScrollX = checkAlignment();
  28. //弹性滑动开启
  29. if(dScrollX > 0){//从左向右滑动
  30. if (dScrollX < mScreenWidth / 2) {
  31. Log.e("dScrollX", dScrollX + "");
  32. mScroller.startScroll(getScrollX(),0,- dScrollX,0,500);
  33. // scrollBy(-dScrollX,0);
  34. }else {
  35. mScroller.startScroll(getScrollX(),0,mScreenWidth - dScrollX,0,500);
  36. // scrollBy(mScreenWidth - dScrollX,0);
  37. }
  38. }else {//从右向左滑动
  39. if (-dScrollX < mScreenWidth / 2) {
  40. mScroller.startScroll(getScrollX(),0, - dScrollX,0,500);
  41. // scrollBy(-dScrollX,0);
  42. }else{
  43. mScroller.startScroll(getScrollX(),0,-mScreenWidth - dScrollX,0,500);
  44. // scrollBy(-mScreenWidth - dScrollX,0);
  45. }
  46. }
  47. break;
  48. }
  49. //重绘
  50. invalidate();
  51. return true;
  52. }
  1. private int checkAlignment(){
  2. //判断滑动的趋势,是向左还是向右,滑动的偏移量是多少
  3. mEnd = getScrollX();
  4. boolean isUp = ((mEnd - mStart) > 0);
  5. int lastPrev = mEnd % mScreenWidth;
  6. int lastNext = mScreenWidth - lastPrev;
  7. if (isUp) {
  8. return lastPrev;
  9. }else{
  10. return -lastNext;
  11. }
  12. }

效果如下:

全部代码

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

简单的ViewPager了解Scroller类的更多相关文章

  1. Android中滑屏实现----手把手教你如何实现触摸滑屏以及Scroller类详解

    前言:  虽然本文标题的有点标题党的感觉,但无论如何,通过这篇文章的学习以及你自己的实践认知,写个简单的滑屏小 Demo还是just so so的. 友情提示: 在继续往下面读之前,希望您对以下知识点 ...

  2. Android Scroller类的详细分析

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

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

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

  4. Android开发——打造简单的Viewpager指示器

    准备工作: 1.两张不同颜色的小圆点图片,可以去阿里巴巴矢量图网站搜索 我把我使用的图片贴出来 2.一个简单的Viewpager的实现 下面是简单的Viewpager实现步骤: 1.布局文件使用Vie ...

  5. Android开发——打造简单的Viewpager指示器(小圆点指示器)

    准备工作: 1.两张不同颜色的小圆点图片,可以去阿里巴巴矢量图网站搜索 我把我使用的图片贴出来 2.一个简单的Viewpager的实现 下面是简单的Viewpager实现步骤: 1.布局文件使用Vie ...

  6. Scroller类的使用总结

    Scroll类之所以不好理解是因为没有搞清楚View的绘制流程. 1)简单来讲 viewgroup重绘时依次会调用  dispatchDraw -- drawChild --child.compute ...

  7. Android中滑屏实现----触摸滑屏以及Scroller类详解 .

    转:http://blog.csdn.net/qinjuning/article/details/7419207 知识点一:  关于scrollTo()和scrollBy()以及偏移坐标的设置/取值问 ...

  8. C#反射技术的简单操作(读取和设置类的属性)

    public class A { public int Property1 { get; set; } } static void Main(){ A aa = new A(); Type type ...

  9. 简单实用的PHP验证码类

    一个简单实用的php验证码类,分享出来 ,供大家参考. 代码如下: <?php /** @ php 验证码类 @ http://www.jbxue.com */ Class code { var ...

随机推荐

  1. 【原】谈谈对Objective-C中代理模式的误解

    [原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...

  2. Python高手之路【五】python基础之正则表达式

    下图列出了Python支持的正则表达式元字符和语法: 字符点:匹配任意一个字符 import re st = 'python' result = re.findall('p.t',st) print( ...

  3. ABP文档 - Mvc 控制器

    文档目录 本节内容: 简介 AbpController基类 本地化 其它 过滤 异常处理和结果包装 审计日志 验证 授权 工作单元 反伪造 模型绑定器 简介 ABP通过nuget包Abp.Web.Mv ...

  4. jsp中出现onclick函数提示Cannot return from outside a function or method

    在使用Myeclipse10部署完项目后,原先不出错的项目,会有红色的叉叉,JSP页面会提示onclick函数错误 Cannot return from outside a function or m ...

  5. SQLServer文件收缩-图形化+命令

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 收缩前 图形化演示: 不仅仅可以收缩日记文件,数据库文件也是可以收缩的,只不过日记收缩比 ...

  6. Python的单元测试(二)

    title: Python的单元测试(二) date: 2015-03-04 19:08:20 categories: Python tags: [Python,单元测试] --- 在Python的单 ...

  7. angular2系列教程(九)Jsonp、URLSearchParams、中断选择数据流

    大家好,今天我们要讲的是http模块的第二部分,主要学习ng2中Jsonp.URLSearchParams.observable中断选择数据流的用法. 例子

  8. 算法与数据结构(十七) 基数排序(Swift 3.0版)

    前面几篇博客我们已经陆陆续续的为大家介绍了7种排序方式,今天博客的主题依然与排序算法相关.今天这篇博客就来聊聊基数排序,基数排序算法是不稳定的排序算法,在排序数字较小的情况下,基数排序算法的效率还是比 ...

  9. 利用注册表在右键添加VS15的快捷方式打开文件夹

    1.简介 最近安装VS15 Preview 5,本版本可以打开"文件夹" 是否可以向Visual Studio Code一样在文件夹或文件右键菜单添加"Open with ...

  10. C# BackgroundWorker 详解

    在C#程序中,经常会有一些耗时较长的CPU密集型运算,如果直接在 UI 线程执行这样的运算就会出现UI不响应的问题.解决这类问题的主要途径是使用多线程,启动一个后台线程,把运算操作放在这个后台线程中完 ...