您的位置:首页 > 移动开发 > Android开发

[置顶] Android开发艺术探索总结之View篇

2016-11-05 12:32 441 查看

Android开发艺术探索总结之View篇

View的基础知识

什么是View

View是Android中所有控件的基类,可以说View是一种界面层的控件的一种抽象,它代表了一个控件,除了View还有ViewGroup,它被称为控件组,言外之意ViewGroup内部包含了许多控件,即一组View,在Android设计中,ViewGroup也是继承自View,这意味着View本身就可以是单个控件也可以是多个控件组成的一组控件,通过这种关系就形成了View树的结构,这跟Web前端中的DOM树概念类似

View的位置参数

View的位置主要由它的四个顶点决定,分别对应于View的四个属性:top,left,right,bottom,其中top是左上角纵坐标,left是左上角横坐标,right是右下角横坐标,bottom是右下角纵坐标,
不过这些都是相对于View的父容器来说的
因为它是一种相对坐标,如下图,在Android中,x轴,y轴的正方向对应为右下



因此可以得到View的宽高跟位置的关系

width = right - left
height = bottom - top


调用View的getWidth,getHeight的时候,源码就是这样计算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;
}


在代码中如何获取View的这些参数?

Left = getLeft();
Right = getRight();
Top = getTop();
Bottom = getBottom();


从Android3.0开始,View增加了额外的几个参数:x,y,translationX,translationY,其中
x,y是View的左上角坐标
,
translationX和translationY是View左上角相对于父容器的偏移量
.这几个参数也是相对于父容器的坐标,并且translationX和translationY的默认值是0,和View的四个基本的位置参数一样,View也为它提供了set/get方法,对应关系如下:

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

上述三种情况是典型的事件序列,通过MotionEvent对象我们可以得到点击事件发生的x,y坐标,系统提供了两组方法:
getX()/getY()
getRawX()/getRawY()
,他们的区别为getX()/getY()返回的是相对于
当前View左上角的x和y坐标
,而getRawX()/getRawY()返回的是相对于
手机屏幕左上角的x和y坐标


ToushSlop

TouchSlop是系统所能识别出的
滑动最小距离
,当手指在屏幕上滑动时,如果两次滑动之间的距离小于这个常量,那么系统就不认为它是滑动。这是一个常量,和设备有关,在不同设备上这个值可能不同,如何在代码中获取这个常量?

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


这个常量存在的目的是为了让我们在处理滑动时,可以利用它来做一些过滤,比如当两次滑动事件的滑动距离小于这个值,我们就可以认为未达到滑动距离的临界值,因此可以认为它们不是滑动,达到更好的用户体验。

源码中对应的位置:frameworks/base/core/res/res/values/config.xml

<!-- Base "touch slop" value used by ViewConfiguration as a movement threshold where scrolling should begin. -->
<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>


VelocityTrackerGestureDetector

VelocityTracker

速度追踪,用于追踪用户手指在滑动过程中的速度,包括水平和竖直方向的速度。使用方法:

在View的onTouchEvent方法追踪当前单击事件的速度:

@Override
public boolean onTouchEvent(MotionEvent event) {
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
return super.onTouchEvent(event);
}


接下来,当我们想知道当前的滑动速度时,可以采用以下方法来获取当前速度

@Override
public boolean onTouchEvent(MotionEvent event) {
VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
velocityTracker.computeCurrentVelocity(1000);
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();
Log.e(TAG, "xVelocity = " + xVelocity + " yVelocity = " + yVelocity);
break;
case MotionEvent.ACTION_UP:
velocityTracker.clear();
velocityTracker.recycle();
break;
}
return true;
}


注意:

第一:在获取速度之前必须先计算速度,必须先调用
velocityTracker.computeCurrentVelocity(1000);


第二:这里的速度是指一段时间内手指所滑过的像素数,比如将时间间隔设置为1000ms,手指在这个时间内水平滑动了100像素,那么水平速度为100,当然速度可能为负数,手指向左滑动时,速度值为负数,速度的计算公式:

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


根据公式再加上Android系统的坐标系,手指逆着坐标系的正方向滑动,所产生的速度就为负值。另外,computeCurrentVelccity(int units), units代表时间单元或者说是时间间隔,单位为ms,计算速度时得到的速度就是在这个时间间隔内手指在水平或竖直方向上所滑动的像素数。

最后,不需要时该对象时,需要手动回收:

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


GestureDetector

手势检测,用户辅助检测用户的单击,滑动,长按,双击等行为,使用流程如下:

首先创建一个GestureDetector对象,并实现OnGestureListener

GestureDetector mGestureDetector = new GestureDetector(getContext(), new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}

@Override
public void onShowPress(MotionEvent e) {

}

@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}

@Override
public void onLongPress(MotionEvent e) {

}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});


然后接管目标View的onTouchEvent

@Override
public boolean onTouchEvent(MotionEvent event) {
boolean consume = mGestureDetector.onTouchEvent(event);
return consume;
}


实现双击:

GestureDetector  mGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onDown(MotionEvent e) {
return true;
}

@Override
public boolean onDoubleTap(MotionEvent e) {
Log.e(TAG,"onDoubleTap");
return super.onDoubleTap(e);
}
});


OnGestureListener

方法名描述
onDown手指轻轻触摸屏幕的一瞬间,由1个ACTION_DOWN触发
onShowPress手指轻轻触摸屏幕,尚未松开,由1个ACTION_DOWN触发,跟onDown的区别在于,它强调的是没有松开或者拖动的状态
onSingleTapUp手指(轻轻触摸屏幕后)松开,伴随着1个ACTION_UP触发,这是单击行为
onScroll手指按下屏幕并拖动,由1个ACTION_DOWN多个ACTION_MOVE触发,,这是拖动行为
onLongPress用户按着屏幕不放,即长按
onFling用户按下屏幕,快速滑动后松开,由1个ACTION_DOWN,多个ACTION_MOVE和1个ACTION_UP触发,这是快速滑动行为
OnDoubleTapListener

方法名描述
onSingleTapConfirmed严格的单击行为,与onSingleTapUp的区别,如果触发了onSingleTapConfirmed,那么后面不可能再紧跟着另一个单击行为,即这只可能是单击,不可能是双击中的任意单击
onDoubleTap双击,由两个连续的单击组成,它不可能和onSingleTapConfirmed共存
onDoubleTapEvent表示发生了双击行为,在双击期间,ACTION_DOWN、ACTION_MOVE和ACTION_UP都会触发此回调
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: