01_View基础知识

158 阅读6分钟

1. View的位置参数

1. Android坐标系

在Android中,屏幕左上角的顶点是Android坐标系的原点(0, 0),x轴和y轴的正方向分别为向右和向下。

2. View的位置参数

1. View的四个顶点

View的位置由它的四个顶点决定,分别对应View的四个属性:left、top、right、bottom。其中:

  • left:左上角的x坐标
  • top:左上角的y坐标
  • right:右下角的x坐标
  • bottom:右下角的y坐标

这些坐标相对于View的父容器,因此是一种相对坐标。

获取这些属性的方法如下:

  • getTop():获取View顶边到其父布局顶边的距离
  • getLeft():获取View左边到其父布局左边的距离
  • getRight():获取View右边到其父布局左边的距离
  • getBottom():获取View底边到其父布局顶边的距离

2. 其它参数

从Android 3.0开始,View增加了以下几个参数,并为其提供了get/set方法:

  • getX():View相对于父容器左上角的x坐标
  • getY():View相对于父容器左上角的y坐标
  • getTranslationX():View左上角相对于父容器的x坐标偏移量,默认值为0
  • getTranslationY():View左上角相对于父容器的y坐标偏移量,默认值为0

上述参数之间的换算关系如下:

x = left + translationX
y = top + translationY

需要注意的是,View在平移过程中,top和left表示的是原始左上角的位置,它们的值不会发生改变,而x、y、translationX和translationY这四个参数会改变。

2. MotionEvent

当我们点击屏幕时,会产生点击事件,这个点击事件被封装成一个类:MotionEvent。典型的事件类型有:

  • ACTION_DOWN:手指刚接触屏幕
  • ACTION_MOVE:手指在屏幕上移动
  • ACTION_UP:手指从屏幕上松开的一瞬间

一次手指触摸屏幕的行为会触发一系列点击事件,如:

  • 点击屏幕后立即松开,事件序列为DOWN -> UP
  • 点击屏幕滑动一会再松开,事件序列为DOWN -> MOVE -> ... -> MOVE -> UP

通过MotionEvent对象,我们可以得到点击事件发生的坐标:

  • getX()/getY():返回相对于当前View左上角的x和y坐标
  • getRawX()/getRawY():返回相对于手机屏幕左上角的x和y坐标

3. TouchSlop

TouchSlop是系统识别出的被认为是滑动的最小距离,这是一个常量,设备之间可能不同。当滑动距离小于这个常量时,系统不会认为是在滑动。获取TouchSlop常量的方法如下:

int touchSlop = ViewConfiguration.get(this).getScaledTouchSlop();

这个常量的意义在于处理滑动时,可以利用它进行过滤。例如,当滑动距离小于这个值时,可以认为未达到滑动的临界值,从而不是滑动。这样可以提升用户体验。在源码中,可以在frameworks/base/core/res/res/values/config.xml文件中找到这个常量的定义:

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

4. VelocityTracker

VelocityTracker用于追踪手指在滑动过程中的速度,包括水平和垂直方向的速度。使用它的过程如下:

首先,在View的onTouchEvent方法中追踪当前事件的速度:

VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);

然后,可以获取当前滑动速度:

// 设置速度计算的时间间隔,例1000ms
velocityTracker.computeCurrentVelocity(1000);
// 获取水平方向滑动速度,单位:像素/1000ms
int xVelocity = (int) velocityTracker.getXVelocity();
// 获取垂直方向滑动速度,单位:像素/1000ms
int yVelocity = (int) velocityTracker.getYVelocity();

注意:

  1. 获取速度之前必须先计算速度,即调用getXVelocitygetYVelocity之前必须调用computeCurrentVelocity方法。
  2. 速度可以为负数,当手指从右向左滑动时,水平方向速度为负值。

速度计算公式:

速度 = (终点位置 - 起点位置) / 时间段

computeCurrentVelocity方法的参数表示时间间隔,单位是毫秒(ms)。

例如,通过velocityTracker.computeCurrentVelocity(100);获取速度,那么得到的速度就是手指在100ms内滑过的像素数。

最后,不再使用VelocityTracker时,需要调用clear方法重置并回收内存:

velocityTracker.clear();
velocityTracker.recycle();

5. GestureDetector

GestureDetector用于检测用户的单击、滑动、长按、双击等手势行为。使用步骤如下:

首先,创建GestureDetector对象并实现onGestureListener接口,如有需要,还可以实现onDoubleTapListener监听双击行为:

GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.OnGestureListener() {
    // 实现所需的方法
});
gestureDetector.setIsLongpressEnabled(false); // 解决长按屏幕后无法拖动的问题

然后,在目标View的onTouchEvent方法中处理触摸事件:

@Override
public boolean onTouchEvent(MotionEvent event) {
    return gestureDetector.onTouchEvent(event);
}

onGestureListeneronDoubleTapListener接口方法如下:

方法名描述所属接口
onDown手指轻轻触摸屏幕的一瞬间,由1个ACTION_DOWN触发onGestureListener
onShowPress手指轻轻触摸屏幕,尚未松开或拖动,由1个ACTION_DOWN触发。强调没有松开或拖动的状态onGestureListener
onSingleTapUp手指轻轻触摸屏幕后松开,伴随着1个ACTION_UP触发,这是单击行为onGestureListener
onScroll手指按下屏幕并拖动,由1个ACTION_DOWN和多个ACTION_MOVE触发,这是拖动行为onGestureListener
onLongPress用户长久按着屏幕不放,即长按onGestureListener
onFling用户按下触摸屏、快速滑动后松开,由1个ACTION_DOWN、多个ACTION_MOVE和1个ACTION_UP触发,这是快速滑动行为onGestureListener
onDoubleTap双击,由2次连续单击组成,不可能与onSingleTapConfirmed共存onDoubleTapListener
onSingleTapConfirmed严格单击行为,与onSingleTapUp区别在于,后者可能是双击中的一次单击,而前者不可能紧跟另一个单击onDoubleTapListener
onDoubleTapEvent双击行为期间,ACTION_DOWN、ACTION_MOVE和ACTION_UP都会触发此回调onDoubleTapListener

常用方法包括:onSingleTapUp(单击)、onFling(快速滑动)、onScroll(拖动)、onLongPress(长按)、onDoubleTap(双击)。

6. Scroller

Scroller用于实现View的弹性滑动,避免View使用scrollTo/scrollBy方法瞬间滑动,提供平滑过渡效果。Scroller与View的computeScroll方法配合使用。

实现步骤如下:

  1. 定义平滑滑动方法:
public void smoothScrollTo(int destX, int destY){
    int scrollX = getScrollX();
    int delta = destX - scrollX;
    // startScroll保存滑动参数,滑动效果由invalidate方法触发
    mScroller.startScroll(scrollX, 0, delta, 0, 1000);
    // invalidate方法导致View重绘,间接调用computeScroll方法
    invalidate();
}
  1. 重写computeScroll方法完成滑动:
@Override
public void computeScroll() {
    // 计算当前滑动位置
    if (mScroller.computeScrollOffset()){
        // 滑动View到当前计算位置
        scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
        // 继续调用computeScroll方法,完成整个滑动过程
        postInvalidate();
    }
}

scrollTo方法瞬间滑动View,而computeScroll方法根据时间流逝逐步完成滑动,使滑动过程更加平滑自然。


View系列文章

01_View基础知识

02_View的滑动

03_View的事件分发机制

04_View的工作流程

05_自定义View

05_自定义ViewGroup

06_View滑动冲突处理