前言

 View的滑动对于View交互性及效果有很大影响,我们可以通过以下四种方式来实现View的滑动,准确地说是View位置的改变。要改变View的位置,首先我们需要了解Android的坐标系,因为View的是通过坐标来定位的。

绝对坐标系

 Android系统中,屏幕的最左上角为坐标原点,如下图所示。

屏幕最左上角的点为坐标原点,向右向下分别为x轴和y轴

视图坐标系

 视图坐标系是在View的层级体系中使用到的,View的父布局最左上角为坐标原点,向右向下为x轴和y轴,如下图所示:

几个容易混淆的方法:

  1. getX():视图坐标系点的X坐标
  2. getRawX():绝对坐标系点的X坐标
  3. getLeft():视图坐标系View左边框距离ViewGroup左边框距离
  4. getTranslationX():View的偏移量,初始为0,当View发生平移时,其值会变,向右为正,向左为负。

其中view.getX() = view.getLeft() + view.getTranslationX(),而get*Y同理。

1. 通过改变View的布局位置

 View的layout方法用来将View放到布局的合适位置,我们可以通过这个方法改变它的left,top,right,bottom参数的值来改变它在布局中的位置。在此基础上,如果我们在用户手指移动的过程中不断地改变View的位置就可以让View跟随手指移动。要实现View跟随用户手指滑动,我们可以监听用户手指的动作(按下,移动,。。。)计算偏移量,通过layout改变View的位置即可。

如下代码则通过layout实现View跟随手指滑动(重写View的onTouchEvent方法)

private int lastX;
private int lastY;@Override
public boolean onTouchEvent(MotionEvent event) {int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.d(TAG,"onTouchEvent() down");lastX = x;lastY = y;break;case MotionEvent.ACTION_MOVE:Log.d(TAG,"onTouchEvent() move");int offX = x - lastX;int offY = y -lastY;layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);break;}return true;
}

这里通过相对坐标计算偏移量完成View的滑动,还可以通过绝对坐标计算偏移量,代码如下:

private int lastRawX;
private int lastRawY;@Override
public boolean onTouchEvent(MotionEvent event) {int rawX = (int) event.getRawX();int rawY = (int) event.getRawY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.d(TAG,"onTouchEvent() down");lastRawX = rawX;lastRawY = rawY;break;case MotionEvent.ACTION_MOVE:Log.d(TAG,"onTouchEvent() move");int offX = rawX - lastRawX;int offY = rawY -lastRawY;layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);lastRawX = rawX; // 重置坐标lastRawY = rawY; // 重置坐标break;}return true;
}

与相对坐标不同的是,使用绝对坐标需要在move事件结束后重置上一次手指的坐标值,这样才能准确地计算出偏移量。

为什么绝对坐标要重置?那是如果不重置的话每次移动都是拿新的坐标与最开始的坐标比较得到偏移量,而最开始的坐标是View的初始位置手指按下的坐标,View每次移动的偏移量应该是新位置的坐标减去上一次的坐标,所以每次移动后需要更新上一次的坐标。

除了使用View的layout方法重新布局View外,还可以使用offsetLeftAndRight和offsetTopAndBottom方法重新布局View,同样可以实现View的滑动效果。代码如下:

private int lastRawX;
private int lastRawY;@Override
public boolean onTouchEvent(MotionEvent event) {int rawX = (int) event.getRawX();int rawY = (int) event.getRawY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.d(TAG,"onTouchEvent() down");lastRawX = rawX;lastRawY = rawY;break;case MotionEvent.ACTION_MOVE:Log.d(TAG,"onTouchEvent() move");int offX = rawX - lastRawX;int offY = rawY -lastRawY;offsetLeftAndRight(offX);offsetTopAndBottom(offY);
//                layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);lastRawX = rawX;lastRawY = rawY;break;}return true;
}

还可以通过View的改变布局参数LayoutParams的leftMargin和topMargin属性值改变View的位置,实现View的滑动,代码如下:

private int lastRawX;
private int lastRawY;@Override
public boolean onTouchEvent(MotionEvent event) {int rawX = (int) event.getRawX();int rawY = (int) event.getRawY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.d(TAG,"onTouchEvent() down");lastRawX = rawX;lastRawY = rawY;break;case MotionEvent.ACTION_MOVE:Log.d(TAG,"onTouchEvent() move");int offX = rawX - lastRawX;int offY = rawY -lastRawY;
//                offsetLeftAndRight(offX);
//                offsetTopAndBottom(offY);
//                layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);ViewGroup.LayoutParams layoutParams = getLayoutParams();((ViewGroup.MarginLayoutParams)layoutParams).leftMargin += offX;((ViewGroup.MarginLayoutParams)layoutParams).topMargin += offY;setLayoutParams(layoutParams);lastRawX = rawX;lastRawY = rawY;break;}return true;
}

改变View的布局参数需要注意的是这个View(或ViewGroup)必须有一个父布局,同时还需要注意布局参数的类型(如LinearLayout.LayoutParams,FrameLayout.LayoutParams),不过可以使用万能的MarginLayoutParams,这样就可以不用考虑布局参数类型了。

2. 使用scrollTo和scrollBy

 View类有scrollTo和scollBy方法,它们可以改变View内容的位置,scrollTo表示移动到某个坐标点,scrollBy表示移动多少偏移量。我们可以通过scrollBy实现View跟随手指的滑动,代码如下:

private int lastRawX;
private int lastRawY;@Override
public boolean onTouchEvent(MotionEvent event) {int rawX = (int) event.getRawX();int rawY = (int) event.getRawY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.d(TAG,"onTouchEvent() down");lastRawX = rawX;lastRawY = rawY;break;case MotionEvent.ACTION_MOVE:Log.d(TAG,"onTouchEvent() move");int offX = rawX - lastRawX;int offY = rawY -lastRawY;
//                offsetLeftAndRight(offX);
//                offsetTopAndBottom(offY);
//                layout(getLeft()+offX,getTop()+offY,getRight()+offX,getBottom()+offY);
//                ViewGroup.LayoutParams layoutParams = getLayoutParams();
//                ((ViewGroup.MarginLayoutParams)layoutParams).leftMargin += offX;
//                ((ViewGroup.MarginLayoutParams)layoutParams).topMargin += offY;
//                setLayoutParams(layoutParams);((View)getParent()).scrollBy(-offX,-offY);lastRawX = rawX;lastRawY = rawY;break;}return true;
}

这里你可能会有所迷惑,为什么调用scrollBy的是View的父View(ViewGroup)?为什么偏移量是负的?

首先scrollBy移动的是View的内容content,而不是View本身,如TextView的content为文本,ImageView的content为drawable,而ViewGroup的content是View或是ViewGroup,所以要移动当前View本身,我们就需要通过它的ViewGroup改变自己的内容从而改变View本身的位置。其次,我们真正操作的是View的父控件ViewGroup,要让View往左(上/右/下)移,应该要让ViewGroup往相反方向移动,也就是右(下/左/上),所以偏移量就是相反的(负的)。下面贴上一张图,感受一下。

3. 使用Scroller类实现View平滑移动

 Android为View的滑动提供了Scroller辅助类,它本身并不能导致View滑动,需要借助computeScroll和ScrollTo方法完成View的滑动。使用Scroller类完成View的平滑,需要通过以下三个步骤:

(1)创建Scroller类

通常在自定义View的构造方法中完成Scroller类的初始化

mScroller = new Scroller(context);

(2)重写computeScroll方法

 @Overridepublic void computeScroll() {super.computeScroll();// 判断Scroller滑动是否执行完毕if(mScroller.computeScrollOffset()){((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());// 通过重绘让系统调用onDraw,onDraw中又会调用computeScroll,如此不断循环,直到Scroller执行完毕invalidate();}}

这里需要注意的是computeScroll方法在onDraw中会被调用,因此需要调用invalidate方法通知View调用onDraw重绘,然后再调用computeScroll完成View的滑动,过程为invalidate->onDraw->computeScroll->invalidate->…,无限循环直到mScroller的computeScrollOffset返回false,也就是滑动完成。

(3)调用Scroller类的startScroll方法开启滚动过程

public void smoothScrollBy(int dx,int dy){mScroller.startScroll(mScroller.getFinalX(),mScroller.getFinalY(),dx,dy,2000);invalidate(); // 必须调用改方法通知View重绘以便computeScroll方法被调用。
}

接下来就开始模拟滑动过程了,重写onTouchEvent方法,代码如下:

@Override
public boolean onTouchEvent(MotionEvent event) {int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:lastX = x;lastY = y;break;case MotionEvent.ACTION_UP:int offX = x - lastX;int offY = y -lastY;smoothScrollBy(-offX,-offY);break;}return true;
}

计算偏移量的方法和上面一样,这里实现的效果是手指离开时,View会在2秒内平滑到手指离开时的位置。

4. 使用属性动画实现View的滑动

 属性动画可以改变View的属性,那么我们可以通过属性动画改变View的x和y属性从而改变View的位置实现View的滑动,代码如下:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void animationScroll(float dx, float dy){Path path = new Path();path.moveTo(dx,dy);ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this, "x", "y", path);objectAnimator.start();
}

通过执行ObjectAnimator改变x和y属性,我们需要新的x和y属性值,可以通过重写onTouchEvent方法得到新的x和y属性值,代码如下:

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public boolean onTouchEvent(MotionEvent event) {int x = (int) event.getX();int y = (int) event.getY();switch (event.getAction()){case MotionEvent.ACTION_DOWN:lastX = x;lastY = y;break;case MotionEvent.ACTION_UP:int offX = x - lastX;int offY = y -lastY;animationScroll(getX()+offX,getY()+offY);break;}return true;
}

Android实现滑动的几种方式相关推荐

  1. android ui 最新教程,Android更新UI的五种方式,androidui五种

    Android更新UI的五种方式,androidui五种handler.post activity.runOnUiThread view.post handler+Thread AsyncTask 例 ...

  2. android注册广播两种方式,Android 注册广播的两种方式对比

    Android 注册广播的两种方式对比 1.常驻型广播 常驻型广播,当你的应用程序关闭了,如果有广播信息来,你写的广播接收器同样的能接受到, 他的注册方式就是在你的应用程序中的AndroidManif ...

  3. android 图片方法,分享实现Android图片选择的两种方式

    Android选择图片的两种方式: 第一种:单张选取 通过隐式启动activity,跳转到相册选择一张返回结果 关键代码如下: 发送请求: private static final int PICTU ...

  4. android使用其他应用打开方式,Android 启动activity的4种方式及打开其他应用的activity的坑...

    Android启动的四种方式分别为standard,singleTop,singleTask,singleInstence. standard是最常见的activity启动方式,也是默认的启动的方式. ...

  5. android视频播放的方法,Android实现视频播放的几种方式

    Android实现视频播放的3种方式 Android提供了常见的视频编码,解码机制,使用Android自带的MediaPlayer,MediaController等类可以很方便的实现视频播放的功能.支 ...

  6. android开启gps功能,android 打开GPS的几种方式

    1.在讨论打开gps的之前先看下如何检测gps的开关情况: 方式一: boolean gpsEnabled = locationManager.isProviderEnabled(LocationMa ...

  7. [Android开发]Android更新UI的五种方式

    Android更新UI的五种方式: 1.handler.post 2.activity.runOnUiThread 3.view.post 4.handler+Thread 5.AsyncTask 下 ...

  8. 【Android】Android 彩信发送的两种方式+源代码

    Android  彩信发送的两种方式 第一种:直接调用彩信发送接口 实现代码如下, Intent intent = new Intent(Intent.ACTION_SEND); intent.add ...

  9. android两种广播注册区别,Android 注册广播的两种方式对比

    Android 注册广播的两种方式对比 1.常驻型广播 常驻型广播,当你的应用程序关闭了,如果有广播信息来,你写的广播接收器同样的能接受到, 他的注册方式就是在你的应用程序中的AndroidManif ...

最新文章

  1. null in JavaScript
  2. Redis 实战笔记
  3. SLS机器学习最佳实战:批量时序异常检测
  4. VS Code Python 将支持 Jupyter Notebook
  5. android 快捷方式 未安装该应用程序,android,解决手动创建的桌面快捷方式无法跳转到制定的activity的问题,提示未安装应用程序...
  6. Web 实时推送技术如何弥补 HTTP 协议的缺陷? | 技术头条
  7. 惊呆了!JDK1.8竟然打破了我对接口的一切认知...
  8. 为什么软件开发方法论让你觉得糟糕?
  9. 16/4/4二代支付硬盘故障处理
  10. java excel 打勾_在excel中如何打钩
  11. MySQL批量造数据
  12. 网传宝塔“0day”挂马事件—附检测脚本
  13. java中注解 详解
  14. 贴片电阻的封装、功率
  15. Chrome 图片批量下载扩展—— zzllrr Imager(小乐图客)
  16. (2.0版本)企业微信可信域名,个人添加企业微信可信IP方法
  17. 信奥中的数学:集合与子集
  18. 使用八爪鱼采集器抓取上市公司财报,一次1万条
  19. Android开发之漫漫长途 XII——Fragment 详解
  20. 前百度云首席架构师林仕鼎的创业梦:以十年为期,搭建在“云”上、可复制的虚拟学校

热门文章

  1. 2021年中国苹果及苹果加工品进出口情况:我国苹果干进出口均价均有所上涨[图]
  2. 多线程批量获取腾讯云磁盘分区状态
  3. Android判断是否为模拟器(实测夜神通过)
  4. 如何在CAD中快速定位坐标?
  5. spring boot 实现 Kurento 一对一浏览器视频聊天
  6. 简易的学生社团管理(大二课设)
  7. Hive基础08、Hive引入Struct结构体
  8. 3310复刻版 java_诺基亚3310复刻版评测:经典回归 情怀满满
  9. Uniapp壁纸小程序源码/双端微信抖音小程序源码
  10. 软考中级哪个科目比较容易考?