Android中View的知识体系——(1)初识View
作者:黑衣侠客
一、前言
最近在学习MVP的相关知识,但是发现似乎许多东西都是从View开始的,所以今天总结一下View所学的相关知识,在总结中学习,在学习中总结。
二、知识点纲要
1.View和ViewGroup
1.1 View
View是所有控件的基类,下到各种Button、TextView、ImageView,上到LinearLayout、RelateLayout,甚至包括自定义的控件,都是继承View这个基类,所以说,View是代表着界面层的一个抽象控件。
1.2 ViewGroup
Android中的所有UI组件都继承了View类,而View类有一个很重要的子类——ViewGroup,但是ViewGroup通常作为其他组件的容器使用。Android中的所有的UI组件都是建立在View、ViewGroup基础之上的,同时,由于View和ViewGroup的组合器模式,ViewGroup也可以被当做View使用。在Android里,ViewGroup作为容器不仅可以盛装其他普通的View组件,还可以盛装ViewGroup组件,以此类推,ViewGroup再盛装它该装的组件。
2.MotionEvent和TouchSlop函数方法
2.1 MotionEvent
当手指触摸屏幕时(View或ViewGroup派生的控件),将产生Touch事件。Touch事件的相关细节(触摸发生的位置、事件及怎么触摸的)被封装成MotionEvent对象。
事件类型 | 具体动作 |
---|---|
MotionEvent.ACTION_DOWN | 手指刚接触到屏幕 |
MotionEvent.ACTION_UP | 手指从屏幕刚松开的一瞬间 |
MotionEvent.ACTION_MOVE | 手指在屏幕上移动 |
MotionEvent.ACTION_CANCEL | 动作取消(非人为) |
正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件,通常分两种:
- 点击屏幕:事件为DOWN -> UP
- 点击屏幕滑动后在松开: 事件为DOWN -> MOVE -> MOVE ->…-> MOVE -> UP
在上述的两种典型的Touch事件中,我们可以通过MotionEvent对象得到点击事件发生的x和y坐标。为此,系统提供了两组方法:
- getX/getY : 返回的是相对于当前View左上角的x和y的坐标
- getRawX/getRawY : 返回的是相对于手机屏幕左上角的x和y坐标
2.2TouchSlop
TouchSlop是系统所能识别的被认为是滑动的最小距离,即当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个距离常量,那么系统就认为你没有再进行滑动操作,而这个常量与设备有关,在不同的设备这个值是不同的,因此,可通过==ViewConfiguration.get(getContext()).getScaledTouchSlop()==来获取这个常量。该方法可以做到有更好的用户体验。
3.手势(Gesture)
所谓手势,就是用户手指在触摸屏上的连续碰撞行为,例如:在屏幕上画出从左至右的动作,在比如:在屏幕上画出一个圆圈也是手势。手势这种连续的碰撞会形成某个方向上的移动趋势,也会形成一个不规则的几何图形。Android对这两种手势都提供了支持。
第一种手势行为:Android提供了手势检测,并为手势检测提供了相应的监听器
第二种手势行为:Android允许开发者添加手势,并提供相应的API识别用户的手势
下面是一些重要的类:
3.1 VelocityTracker
速度追踪,用于追踪手指划动过程中的速度,可以是水平也可以是垂直方向的速度。
首先,在View的onTouchEvent方法中追踪当前点击事件的速度。
VelocityTracker tracker = VelocityTracker.obtain();
tracker.addMovement(event);
接着,当我们想知道当前的滑动速度时,这个时候可以采用以下方式获取当前的速度。
tracker.computeCurrentVelocity(1000);//时间间隔的单位是:毫秒(ms)
//意思是1s时间内运动了多少个像素
float xVelocity = tracker.getXVelocity();
float yVelocity = tracker.getYVelocity();
注意:
- 在通过getXVelocity和getYVelocity获取速度之前应先调用computeCurrentVelocity()方法初始化时间。
- 这里的速度指的是一段时间内手指所滑过的像素。
- 例如:时间间隔设置为1000ms时,在1s内,手指在水平方向上从左至右滑动100像素,那么水平速度就是100,同样需要注意的是,速度可以是负值,当手指从右至左滑动时,水平方向值为负值,速度计算公式如下:
速度 = (终点位置 — 起点位置)/ 时间段
3.2 GestureDetector
Android为手势提供了一个GestureDetector类,GestureDetector实例代表了一个手势检测器,用于辅助检测用户的单击、滑动、长按、双击等行为。GestureDetector有3个Listener接口,用来回调不同的触摸事件,具体如下:
GestureDetector这个类对外提供了3个接口和一个静态内部类下面简单的介绍一下这4个监听器:
监听器 | 简介 |
---|---|
OnGestureListener | 手势检测,主要有:按下(Down)、快速滑动(Fling)、长按(LongPress)、滚动(Scroll)、触摸反馈(ShowPress)和单击抬起(SingleTapUp) |
OnDoubleTapListener | 双击检测,主要有三个回调类型:双击(DoubleTap)、单击确认(SingleTapConfirmed)和双击事件回调(DoubleTapEvent) |
OnContextClickListener | 这是Android6.0(23)才添加的,用于检测外部设备上按钮是否按下,一般情况下可以忽略 |
SimpleOnGestureListener | 该类是GestureDetector提供给我们的一个更方便非响应不同手势的类,这个类实现了上述三个接口(但是所有方法都是空的),该类是static类,也就是说它是一个外部类。可以在外部继承这个类,重写里面的手势处理方法。 |
public static class SimpleOnGestureListener implements OnGestureListener , OnDoubleTapListener , OnContextClickListener
另外通过GestureDetector的构造方法可以将SimpleOnGestureListener对象传递进去,这样GestureDetector能处理不同的手势了。
构造函数
GestureDetector有5种构造函数,在这里我们讨论两个主要的构造函数:
//第一种构造函数
public GestureDetector (Context context , OnGestureListener listener){
this(context,listener,null);
}
在第一种构造函数中,需要传入两个参数,context(上下文)和OnGestureListener(手势监听器),我们经常用的就是这种方式。
//第二种构造函数
public GestureDetector(Context context , OnGestureListener listener , Handler handler){
if(handler!=null){
mHandler = new GestureHandler(handler);
}else{
mHandler = new Gesturehandler();
}
mListener = listener;
if(listener instanceof OnDoubleTapListener){
setOnDoubleTapListener((OnDoubleTapListener) listener);
}
if(listener instanceof OnContextClickListener){
setContextClickListener((OnContextClickListener) listener);
}
init(context);
}
(第二种构造函数)在通常情况下是不需要Handler的,因为它会在内部自动创建一个Handler用于处理数据,如果在主线程中创建GestureDetector,那么它所创建的Handler会自动获取主线程的Looper,如果在没有创建Looper的子线程中创建GestureDetector,那么就会因为无法获取Looper导致创建GestureDetector失败,那么,此时,我们应该传递一个带有Looper的Handler。==
重要方法介绍
方法名 | 描述 | 所属接口 |
---|---|---|
onDown | 手指触摸屏幕的一瞬间,由一个ACTION_DOWN触发 | OnGestureListener |
onShowPress | 手指触摸屏幕,尚未松开或拖动,由一个ACTION_DOWN触发,注意:它强调的是没有松开或拖动的状态 | OnGestureListener |
onSingleTapUp | 手指触摸屏幕后松开,伴随着1个ACTION_UP而触发这是单击行为 | OnGestureListener |
onScroll | 手指按下屏幕并拖动,由一个ACTION_DOWN和多个ACTION_MOVE触发,这是拖动行为 | |
onLongPress | 用户长按着屏幕不放,即长按 | OnGestureListener |
onFling | 用户按下触摸屏、快速滑动后松开,由1个ACTION_DOWN和多个ACTION_MOVE以及1个ACTION_UP触发,这是快速滑动行为 | OnGestureListener |
onDoubleTap | 双击、有两次连续的单击组成,它不可能和onSingleTapConfirmed共存 | OnDoubleTapListener |
onSingleTapConfirmed | 严格的单击行为。注意:它和onSingleTapUp的区别,如果触发了onSingleTapConfirmed,那么后面不可能在紧跟着另一个单击行为,即这次可能是单击,而不可能是双击中的一次单击 | OnDoubleTapListener |
onDoubleTapEvent | 表示发生了双击行为,在双击的期间,ACTION_DOWN、ACTION_MOVE和ACTION_UP都会触发此回调 | OnDoubleTapListener |
3.3 Scroller
Scroller是有过渡效果的滑动,它的过程不是瞬间完成的,而是在一定的时间间隔内完成的。Scroller本身无法让View弹性滑动,它需要和View的computeScroll方法配合使用才能完成这个功能。
使用方法:
Scroller scroller = new Scroller(mContext);
//缓慢滚动到指定的位置
private void smoothScrollTo(int destX,int destY){
int scrollX = getScrollX();
int delta = destX - scrollX;
//1000ms内滑向destX,效果就是慢慢滑动
mScroller.startScroll(scrollX,0,delta,0,1000);
invalidate();
}
@Override
public void computeScroll(){ //需要与Scroll()配合使用
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}