View 是一个界面层控件的一种抽象,它代表一个控件。由于 ViewGroup 也是继承 View ,这也就意味这 View 本身可以是单个控件也可以是多个控件组成的一组控件。

决定 View 的位置参数

一种方式,相对于父容器的来说

  • top getTop()
  • left getLeft()
  • right getRight()
  • bottom getBottom()

关系如下: Alt text

View 的宽高和他们的关系是

width=right-left   
height=bottom-top

额外增加的参数 ,也是相对于父容器的坐标

  • x
  • y
  • translationX
  • translationY
 x , y 表示 View 左上角的坐标
translationX 和 translationY 表示 View 左上角相对于父容器的偏移量`

换算关系

x=left+translationX

y=top+translationY

注意,在 View 的平移过程中, top 和 left 表示原始左上角的位置信息,其值不会发生改变,改变的只有 x , y translationX 和 translationY 这四个参数

MotionEvent和TouchSlop

MotionEvent

手指接触屏幕产生的一些了时间,典型的事件类型有如下

  • ACTION_DOWN 手指刚接触屏幕,就是刚按下
  • ACTION_MOVE 手指在屏幕上移动
  • ACTION_UP 手指从屏幕上抬起那一瞬间

  • 点击屏幕后离开 事件顺序:DOWN->UP
  • 滑动屏幕 事件顺序:DOWN->MOVE->…->MOVE->UP

得到点击时间发生的 x 和 y 坐标,系统提供两种方法:

  1. getX()和 getY() 返回的是相对 当前 View 左上角 的 x 和 y 坐标
  2. getRawX()和 getRawY() 返回相对于的 手机屏幕左上角 x和 y 坐标

TouchSlop

系统所能识别出的被认为是滑动的最小距离,也就是说如果两次滑动距离小于这个值,系统不认为你是在进行滑动操作。这是一个常量,和设备有关。获取这个常量的代码:

 ViewConfiguration.get(getContext()).getScaledDoubleTapSlop()

这个常量的意义在于做一些过滤,小于这个值的滑动认为无效。在源码中定义的位置为

framework/base/core/res/res/values/config.xml中

<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>

VelocityTracker, GestrureDetecotr 和Scroller

速度追踪VelocityTracker

用于追踪手指在滑动过程中的速度。包括水平和竖直方向的速度。 使用步骤主要包括以下几步

  1. 在 onTouchEvent 中追踪当前事件的速度
  2. 根据时间计算速度
  3. 得到水平或者竖直方向的速度,
  4. 清理并回收资源 注意: 速度可以为负数,从右往左滑动,水平方向速度为负值
public boolean onTouchEvent(MotionEvent event) {
   VelocityTracker velocityTracker=VelocityTracker.obtain();
   velocityTracker.addMovement(event);

   //获取速度之前必须计算速度,速度是一段事件内手指所滑动过的像素数,
   velocityTracker.computeCurrentVelocity(1000);//计算 1000ms 内手指滑动过的像素数
   int xVeleocity=(int) velocityTracker.getXVelocity();//水平方向的速度,速度可以为负数,从右往左滑动,水平方向速度为负值
   int yVeleocity=(int) velocityTracker.getYVelocity();

   //不需要使用的时候,清理并回收内存
   velocityTracker.clear();
   velocityTracker.recycle();
    ...
}

手势检测 GestureDetector

主要用于辅助检测用户的单击,滑动,长按,双击等行为, 使用过程:

  1. 创建 GextureDetector 对象,并实现 OnGestureListener 接口,还可以根据需要实现 OnDoubleTapListener 从而能监听双击行为。
     GestureDetector gestureDetector=new GestureDetector(this);
     //解决长按屏幕后无法拖动的现象
     gestureDetector.setIsLongpressEnabled(false);
    
  2. 在监听 View 的 onTouchEvent 方法中实现
    public boolean onTouchEvent(MotionEvent event) {
      boolean consume =gestureDetector.onTouchEvent(event);
      return consume
    }
    
  3. OnGestureListener和 OnDoubleTapListener 的方法介绍 Alt text

常用方法:

  • onSingleUp 单击
  • onFling 快速滑动
  • onScroll 拖动
  • onLongPress 长按
  • onDoubleTap 双击

使用建议:如果只用监听滑动相关的,建议自己在 onTouchEvent 中实现,如果要监听双击这种行为的话,那么就使用GestureDetector

弹性滑动对象 Scroller

用于实现 View 的弹性滑动。因为在使用scrollTo()/scrollBy()方法进行滑动,是没有滑动效果的,用户体验不好,可以使用 Scroller 配合 View 的 computeScroll() 方法才实现过渡动画效果。典型代码固定格式

Scroller scroller=new Scroller(context)
public void computeScroll() {
    if(scroller.computeScrollOffset()){
     smoothScrollTo(scroller.getCurrX(),scroller.getCurrY());
     postInvalidate();
    }
}

//缓慢平滑的移动到指定位置
private void smoothScrollTo(int destX,int destY){
    int scrollX=getScrollX();
    int delta=destX-scrollX;
    //1000ms内滑动 destx ,效果就是慢慢滑动
    scroller.startScroll(scrollX, 0 , delta , 0 ,1000);
    invalidate();
}

View 的滑动

不管多么绚丽的滑动效果,归根到底,都是由不同的滑动外加一些特效所组成的。因此掌握滑动的方法是实现绚丽的自定义控件的基础,通过一下三种方式可以实现 View 的滑动

  • 通过 View 本身的 scrollTo() 和 scrollBy() 方法
  • 通过给 View 施加平移效果
  • 通过改变 View 的 LayoutParams 使得 View 重新布局而实现滑动

scrollTo()/scrollBy()

public void scrollTo(int x, int y) {
    if (mScrollX != x || mScrollY != y) {
        int oldX = mScrollX;
        int oldY = mScrollY;
        mScrollX = x;
        mScrollY = y;
        invalidateParentCaches();
        onScrollChanged(mScrollX, mScrollY , oldX , oldY);
        if (!awakenScrollBars()) {
            postInvalidateOnAnimation();
        }
    }
}
public void scrollBy(int x, int y) {
    scrollTo(mScrollX + x, mScrollY + y);
}

说明以下几点:

  • scrollBy() 实际上是调用的 scrollTo() ,而 scrollTo() 是实现了基于所传递的参数的绝对滑动。只能改变 View 内容的位置,而不能改变 View 在布局中的位置。
  • View边缘: View的位置,由四个顶点组成,
  • View内容边缘:View中内容的边缘,
  • mScrollX : 等于 View 左边缘和 View 内容左边缘在水平方向的距离,单位像素,当 View 左边缘在 View 内容左边缘的右边时候,值为正,反之为负,换句话说从左向右滑动,值为负
  • mScrollY : 等于 View 上边缘和 View 内容上边缘在竖直方向的距离,单位像素,从上往下滑,值为负

动画

通过动画我们能让一个 View 进行平移,而平移就是一种滑动。主要操作就是 View 的 translationX 和 translationY 可以使用传统 View 动画,也可以采用属性动画。采用属性动画为了兼容3.0以下版本,需要使用开源动画库 nineoldandroids ,例如把一个 VIew 在 100ms 从原始位置移动 100 个像素

  1. 传统 View 动画 Alt text

  2. 属性动画

    ObjectAnimator.ofFloat(targetView,"translationX", 0 ,100).setDuration(100).start();
    

    说明:

    • 使用动画并不能真正改变 View 的位置参数,只是对 View 影像的操作。

改变布局参数

即改变 layoutParams ,如果想把一个 Button 向右平移100px.只需要将这个 Button 的 layoutParams 的 marginLeft 参数值增加100px Alt text

三种滑动方式的对比

方式 优点 缺点 使用场景
scrollTo()/scrollBy() 操作简单,很方便的实现滑动效果且不影响内部元素的点击事件 只能滑动 view 的内容,不能滑动 View 本身 适合对 View 内容的滑动
动画 android 3.0以上并采用属性动画,没有缺点 传统动画或者android3.0一下使用属性动画,都不能改变 View 本身属性 没有交互的 VIew 和实现赋值的动画效果
改变布局参数 既能滑动 View 的内容,又能滑动 View 本身 使用麻烦 适用于有交互的View

搬运地址:

Android 开发艺术探索