- What is view?
View代表用户界面最基本组成部分。一个View在屏幕上占据一个矩形区域并负责绘图和相应地事件处理。View是所有控件的基类,View的子类被用于交互UI的组件。View有个很重要的子类ViewGroup,它是用于布局的基类。ViewGroup是一个不可见的容器,里面有其他的View或者ViewGroup并且定义它们的布局属性。View和ViewGroup采用了典型的组合模式思想,若有兴趣大家可以去了解下组合模式。
- View的位置参数
View的位置由以下四个属性决定。
/**
* The distance in pixels from the left edge of this view's parent
* to the left edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
protected int mLeft;
/**
* The distance in pixels from the left edge of this view's parent
* to the right edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
protected int mRight;
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
protected int mBottom;
如下图所示,这里坐标是一种相对坐标,是相对于父容器而言的。
根据上图的,可以得出View的高宽和坐标的关系。
/**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
/**
* Return the height of your view.
*
* @return The height of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
Android3.0开始,View增加了几个额外的参数:x、y、translationX、translationY,其中x、y是View左上角的坐标,而translationX、translationY是分别相对于getLeft()、getTop()位置。
/**
* The horizontal location of this view relative to its {@link #getLeft() left} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The horizontal position of this view relative to its left position, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getTranslationX() {
return mRenderNode.getTranslationX();
}
/**
* The vertical location of this view relative to its {@link #getTop() top} position.
* This position is post-layout, in addition to wherever the object's
* layout placed it.
*
* @return The vertical position of this view relative to its top position,
* in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getTranslationY() {
return mRenderNode.getTranslationY();
}
View的坐标x、y的计算公式如下。
/**
* The visual x position of this view, in pixels. This is equivalent to the
* {@link #setTranslationX(float) translationX} property plus the current
* {@link #getLeft() left} property.
*
* @return The visual x position of this view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getX() {
return mLeft + getTranslationX();
}
/**
* The visual y position of this view, in pixels. This is equivalent to the
* {@link #setTranslationY(float) translationY} property plus the current
* {@link #getTop() top} property.
*
* @return The visual y position of this view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "drawing")
public float getY() {
return mTop + getTranslationY();
}
通过下图我们来解释下这个translationX、translationY到底怎么回事。
假如View通过动画平移到View’的位置,那么这时候View平移的位置就是translationX、translationY。因为View的横向是右移,因此translationX的值为正,而View的纵向是上移,因此translationY的值为负。
- MotionEvent
MotionEvent是用来报告运动事件
(鼠标, 笔, 手指, 轨迹球)。在MotionEvent中提供了很多触摸事件,典型的触摸事件类型如下几种:
/**
* Constant for {@link #getActionMasked}: A pressed gesture has started, the
* motion contains the initial starting location.
* <p>
* This is also a good time to check the button state to distinguish
* secondary and tertiary button clicks and handle them appropriately.
* Use {@link #getButtonState} to retrieve the button state.
* </p>
*/
public static final int ACTION_DOWN = 0;
/**
* Constant for {@link #getActionMasked}: A pressed gesture has finished, the
* motion contains the final release location as well as any intermediate
* points since the last down or move event.
*/
public static final int ACTION_UP = 1;
/**
* Constant for {@link #getActionMasked}: A change has happened during a
* press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}).
* The motion contains the most recent point, as well as any intermediate
* points since the last down or move event.
*/
public static final int ACTION_MOVE = 2;
一个典型的触摸事件会经历,DOWN->MOVE->UP。通过MotionEvent我们可以得到相应触摸事件发生的坐标x、y。为此,系统提供了两组方法获取事件坐标。
/**
* {@link #getX(int)} for the first pointer index (may be an
* arbitrary pointer identifier).
*
* @see #AXIS_X
*/
public final float getX() {
return nativeGetAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
/**
* {@link #getY(int)} for the first pointer index (may be an
* arbitrary pointer identifier).
*
* @see #AXIS_Y
*/
public final float getY() {
return nativeGetAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
}
和
/**
* Returns the original raw X coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views.
*
* @see #getX(int)
* @see #AXIS_X
*/
public final float getRawX() {
return nativeGetRawAxisValue(mNativePtr, AXIS_X, 0, HISTORY_CURRENT);
}
/**
* Returns the original raw Y coordinate of this event. For touch
* events on the screen, this is the original location of the event
* on the screen, before it had been adjusted for the containing window
* and views.
*
* @see #getY(int)
* @see #AXIS_Y
*/
public final float getRawY() {
return nativeGetRawAxisValue(mNativePtr, AXIS_Y, 0, HISTORY_CURRENT);
}
这两组方法是有区别的,getX/getY返回的是相对于当前View左上角的x和y坐标,而getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标。
- TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离,如果手指在屏幕滑动时,如果两次滑动之间的距离小于这个常量,那么系统就认为你不是在进行滑动操作。TouchSlop在不同的设备中,其值可能不同。我们可以通过ViewConfiguration的getScaledTouchSlop()获取TouchSlop的值。
/**
* @return Distance in pixels a touch can wander before we think the user is scrolling
*/
public int getScaledTouchSlop() {
return mTouchSlop;
}
mTouchSlop的值是配置在config.xml中。
<!-- Base "touch slop" value used by ViewConfiguration as a
movement threshold where scrolling should begin. -->
<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
- VelocityTracker
VelocityTracker用于跟踪手指滑动过程中的速度,包括水平和竖直方向的速度。当你想要开始追踪滑动速度时,可以通过obtain()方法获取VelocityTracker类的实例。
/**
* Retrieve a new VelocityTracker object to watch the velocity of a
* motion. Be sure to call {@link #recycle} when done. You should
* generally only maintain an active object while tracking a movement,
* so that the VelocityTracker can be re-used elsewhere.
*
* @return Returns a new VelocityTracker.
*/
static public VelocityTracker obtain() {
VelocityTracker instance = sPool.acquire();
return (instance != null) ? instance : new VelocityTracker(null);
}
然后通过addMovement(MotionEvent)将你接收到的MotionEvent添加到VelocityTracker类中。
/**
* Add a user's movement to the tracker. You should call this for the
* initial {@link MotionEvent#ACTION_DOWN}, the following
* {@link MotionEvent#ACTION_MOVE} events that you receive, and the
* final {@link MotionEvent#ACTION_UP}. You can, however, call this
* for whichever events you desire.
*
* @param event The MotionEvent you received and would like to track.
*/
public void addMovement(MotionEvent event) {
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
nativeAddMovement(mPtr, event);
}
如果我们想要计算当前滑动速度时,首先调用computeCurrentVelocity(int),接着调用getXVelocity()和getYVelocity()。这里需要注意,速度有正负值的。
/**
* Compute the current velocity based on the points that have been
* collected. Only call this when you actually want to retrieve velocity
* information, as it is relatively expensive. You can then retrieve
* the velocity with {@link #getXVelocity()} and
* {@link #getYVelocity()}.
*
* @param units The units you would like the velocity in. A value of 1
* provides pixels per millisecond, 1000 provides pixels per second, etc.
* @param maxVelocity The maximum velocity that can be computed by this method.
* This value must be declared in the same unit as the units parameter. This value
* must be positive.
*/
public void computeCurrentVelocity(int units, float maxVelocity) {
nativeComputeCurrentVelocity(mPtr, units, maxVelocity);
}
这里有一个入参需要解释下,int值表示单位,1表示一毫秒每像素,传1000的话代表的是1秒每像素,依次类推。
最后很重要的一点,当你不需要VelocityTracker实例时,需要按顺序调用其clear()和recycle()方法,一般是在ACTION_DOWN和ACTION_CANCEL中使用。
/**
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
nativeClear(mPtr);
}
clear()方法是用来恢复VelocityTracker的初始状态。
/**
* Return a VelocityTracker object back to be re-used by others. You must
* not touch the object after calling this function.
*/
public void recycle() {
if (mStrategy == null) {
clear();
sPool.release(this);
}
}
回收VelocityTracker实例,方便重新使用。