3.1 View 的基础知识
3.1.1 什么是 View
View 是Android中所有控件的基类,View 是界面层控件的一种抽象,它代表一个控件,ViewGroup 也继承View,View可以是单个控件也可以是有多个控件组成的一组控件,形成了View树的结构。
3.1.2 View 的位置参数
主要由四个顶点来决定,分别对应top、left、right、bottom,这些坐标相对于View的父容器来说的,是相对坐标。View 的宽高和坐标关系
width = right - left
height = bottom - top
left = getLeft(),right = getRight() 以此类推。
从Android 3.0开始,View增加了额外参数,左上角坐标(x,y),左上角相对于父容器的偏移量(translationX,translationY,默认为0)。
x = left + translationX
y = right + translationY
3.1.3 MotionEvent和TouchSlop
- MotionEvent 通过手指接触屏幕所产生的一系列事件
- getX ,getY 返回的是相对于当前View左上角的x,y坐标。
- getRawX,getRawY 返回的是相对于手机屏幕左上角的x,y坐标。
- TouchSlop 系统识别出的被认为是滑动最小距离,常量,不同设备值可能不同
- ViewConfiguration.get(getContext()).getScaledTouchSlop()即可获取这个常量。
3.1.4 VelocityTracker、GestureDetector、Scroller
VelocityTracker 速度追踪,用于追踪手指滑动过程中的速度(水平、竖直)
- 单机速度
VelocityTracker vt = VelocityTracker.obtion();
vt.addMovement(event); - 滑动速度
vt.computeCurrentVelocity(1000);
int xVelocity = (int)vt.getXVelocity();
int yVelocity = (int)vt.getYVelocity();
1000ms内手指滑动的像素数,正值代表从左向右滑,负值代表从右向左滑。
- 单机速度
- GestureDetector 手势检测,辅助检测用户的单击,滑动,双击,长按等行为。
- 监听长按行为
GestureDetector gd = new GestureDetector(this);
gd.setIsLongPressEnabled(false);
boolean consume = gd.onTouchEvent(event);
return consume;
- 监听长按行为
- Scroller 弹性滑动对象,用于View 的弹性滑动,实现过渡效果的滑动,在一定时间间隔内完成
3.2 View的滑动
3.2.1 使用scrollTo/scrollBy
scrollTo源码:
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
scrollBy源码:
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
mScrollX总等于View的左边缘和View内容左边缘水平方向的距离,这两个方法只能改变View内容的位置,不能改变View在布局中的位置。
3.2.2 使用动画
- 使用View动画 在xml中定义动画
- 使用属性动画,代码书写
ObjectAnimator.ofFloat(textview,"tanslationX",0,100).setDuration(100).start()
View 动画并不能真正改变View的位置,保留新位置需要fillAfter属性设置为true,单击新位置无法触发onClick事件,属性动画不存在这个问题,但是Android 3.0以下无法使用属性动画,需要使用动画兼容库nineoldandroids来实现。
3.2.3 改变布局参数
- 改变LayoutParams
MarginLayoutParams params = button.getLayoutParams();
params.width += 100;
button.requestLayout();
或者button.setLayoutParams(params);
3.4 View 的事件分发机制
3.4.1 点击事件传递规则
请看如下伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
对于一个根ViewGroup来说,点击事件产生后dispatchTouchEvent被调用,onInterceptTouchEvent方法返回true,表示拦截当前事件,返回false,表示不拦截当前事件,当前事件会传递给它的子元素,子元素的dispatchTouchEvent方法会被调用。
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
View处理点击事件时判断mOnTouchListener是否为null,不为null时,如果onTouch方法返回false,onTouchEvent()方法被调用,如果onTouchEvent()方法中设置OnClickListener,那么会被调用,返回true,onTouchEvent方法不会被调用。