View体系与自定义View(一)—— 什么是View
1 View和ViewGroup
View是Android中所有控件的基类,不管是简单的Button、TextView还是复杂的RelativeLayout和ListView,它们的共同基类都是View。所以说,View是一种界面层的控件的一种抽象,它代表了一个控件。除了View还有ViewGroup,从名字上看,可以翻译成控件组,即一组View。ViewGroup可以包含很多View以及ViewGroup,而它包含的ViewGroup又可以包含View和ViewGroup,以此类推,形成一个View树。

在Android的设计中,ViewGroup也继承了View,这就意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件。
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { }public abstract class ViewGroup extends View implements ViewParent, ViewManager { }public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { }public class Button extends TextView { }public class ImageView extends View { }public class LinearLayout extends ViewGroup { }public class RelativeLayout extends ViewGroup { }public class ListView extends AbsListView { }public abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher,ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener,ViewTreeObserver.OnTouchModeChangeListener,RemoteViewsAdapter.RemoteAdapterConnectionCallback { }public abstract class AdapterView<T extends Adapter> extends ViewGroup { }

2 坐标系
Android系统中有两种坐标系,分别为Android坐标系和View坐标系。
2.1 Android坐标系
在Andriod中,将屏幕左上角的顶点作为Android坐标系的原点,这个原点向右是X轴正方向,向下是Y轴正方向。在触控事件中,使用getRawX()和getRawY()方法获得的坐标是Android坐标系的坐标。
raw [rɔː] 生的;未加工的;阴冷的;刺痛的;擦掉皮的;无经验的;(在艺术等方面)不成熟的

2.2 View坐标系
View坐标系,它与Android坐标系并不冲突,两者是共同存在的。它们可以帮助开发者更好的控制 View。

2.2.1 View自身的坐标
View的位置主要是由它的四个顶点来决定的,分别对应View的四个属性:top、left、right、bottom, 其中top是左上角的纵坐标,left是左上角横坐标,right是右下角很坐标,bottom是右下角纵坐标。这些坐标都是相对于View的父容器来说的,是一种相对坐标。
View的四个参数,在源码中分别对应于mLeft、mRight、mTop和mBottom这四个成员变量:
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {protected int mLeft;protected int mRight;protected int mTop;protected int mBottom;public final int getLeft() { return mLeft; }public final int getRight() { return mRight; } public final int getTop() { return mTop; } public final int getBottom() { return mBottom; }
}
通过如下方法可以获得View到其父控件(ViewGroup)的距离:
getLeft():获取View自身左边到其父布局左边的距离getRight():获取View自身右边到其父布局左边的距离getTop():获取View自身顶边到其父布局顶边的距离getBottom():获取View自身底边到父布局顶边的距离
从Android 3.0开始,View增加了几个额外的参数:x、y、translationX和translationY,其中x和y是view相对于父容器左上角的坐标,而translationX和translationY是View左上角相对于父容器的偏移量,并且translationX和translationY的默认值是0, 几个参数的换算关系如下:
x = left + translationX
y = top + translationYpublic float getX() {return mLeft + getTranslationX();
}public float getY() {return mTop + getTranslationY();
}
在View的平移过程中,top和left表示的是原始左上角的位置信息,其值并不会发生改变,发生改变的是x、y、translationX和translationY。
2.2.2 View获取自身的宽高
系统提供了获取View宽和高的方法,以下是View的源码:
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {public final int getWidth() {return mRight - mLeft;}public final int getHeight() {return mBottom - mTop;}
}
getWidth()用来获取View的宽度getHeight()用来获取View自身的高度
3 MotionEvent和TouchSlop
motion [ˈmoʊʃn] 动作;移动;手势;请求;意向;议案 slop [slɑːp] 漫不经心地涂(或敷)
3.1 MotionEvent
无论是View还是ViewGroup,最终的点击事件都会由onTouchEvent(MotionEvent event)方法来处理。MotionEvent在用户交互过程中作用重大,其内部提供了很多事件常量,典型的事件类型有如下几种:
ACTION_DOWN:手指刚接触屏幕ACTION_MOVE:手指在屏幕上移动ACTION_UP:手指从屏幕上松开的一瞬间
正常情况下,一次手指触摸屏幕的行为会出发一系列点击事件,考虑如下几种情况:
- 点击屏幕后离开,事件序列为
DOWN->UP; - 点击屏幕滑动一会后离开,事件序列为
DOWN->MOVE...MOVE->MOVE->UP
此外,MotionEvent也提供了获取焦点坐标的各种方法:
getX():获取点击事件距离控件左边的距离,即视图坐标getY():获取点击事件距离控件定边的距离,即视图坐标getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标
3.2 TouchSlop
TouchSlop是系统所能识别出的被认为是滑动的最小距离,如果两次滑动之间的距离小于这个常量,那么系统不认为是在进行滑动操作。这个常量和设备相关,在不同的设备上这个值是不同的。通过以下方式可以获取这个常量ViewConfiguration.get(getContext()).getScaledTouchSlop();
4 VelocityTracker速度追踪
velocity [vəˈlɑːsəti]【物】速度 tracker [ˈtrækər] 拉纤者,纤夫;追踪系统,[自] 跟踪装置;追踪者
速度追踪,用于追踪手指在滑动过程中的速度,包括水平和竖直方向。 它的使用过程很简单:
@Override
public boolean onTouchEvent(MotionEvent event) {// 1. 在onTouchEvent事件中追踪当前点击事件的速度VelocityTracker velocityTracker = VelocityTracker.obtain();velocityTracker.addMovement(event);// 2. 获取当前速度,获取之前先计算速度,需要设置时间间隔(单位是毫秒),注意,速度可以是负数velocityTracker.computeCurrentVelocity(1000);int xVelocity = (int) velocityTracker.getXVelocity();int yVelocity = (int) velocityTracker.getYVelocity();return super.onTouchEvent(event);
}
当不需要它的时候,需要调用clear方法来重制并回收内存:
velocityTracker.clear();
velocityTracker.recycle();
5 GestureDetector手势检测
gesture [ˈdʒestʃər] 手势,姿势;姿态,表示 detector [dɪˈtektər] 探测器;检测器;发现者;侦察器
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。
首先,实现实现GestureDetector.OnGestureListener接口,根据需要还可以实现GestureDetector.OnDoubleTapListener从而能监听双击行为:
public class RoundImageView extends AppCompatImageView implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {@Overridepublic boolean onDown(MotionEvent e) {return false;}@Overridepublic void onShowPress(MotionEvent e) {}@Overridepublic boolean onSingleTapUp(MotionEvent e) {return false;}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {return false;}@Overridepublic void onLongPress(MotionEvent e) {}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {return false;}@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {return false;}@Overridepublic boolean onDoubleTap(MotionEvent e) {return false;}@Overridepublic boolean onDoubleTapEvent(MotionEvent e) {return false;}
}
其次,创建GestureDetector对象:
GestureDetector gestureDetector = new GestureDetector(this);
gestureDetector.setIsLongpressEnabled(false); // 解决长按屏幕后无法拖动的现象
之后,接管目标View的onTouchEvent方法,在View的onTouchEvent方法中添加如下实现:
@Override
public boolean onTouchEvent(MotionEvent event) { return gestureDetector.onTouchEvent(event);
}
完成之后,就可以有选择的实现GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener中的方法了。

在实际开发中,如果只是监听滑动相关的,建议在onTouchEvent中实现,如果要监听双击这种行为的话,就使用GestureDetector。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
