Android View的作用不亚于其四大主件。这里说下其使用过程中的一些小细节:
View的属性参数:
View的位置由四个顶点来决定,分别对应View的四个属性:top(左上角纵坐标),left(左上角横坐标),right(右下角横坐标),bottom(右下角纵坐标),即
通过getTop,getLeft,getRight,getBottom得到四个坐标
则view的宽为:width = getRight -getLeft
高为 hidth = getBottom - getTop
同时View还有x,y ,translateX, translateY,
x和y表示View左上角的坐标
translateX和translateY是view左上角相对于父容器的偏移量,其默认值是0(表示未移动)
即换算关系为:x = left+translationX;
y = top+translationY;
View在平移过程中,top和left表示的是原始左上角的信息,,其值并不会发生变化,变化的是x,y,translatex,translatey。
View在滑动过程中产生的getX和getY,以及getRawX和getRawY
其中getX和getY表示返回的是相对于当前View左上角的x和y坐标
getRawX和getRawY表示返回的是相对于手机屏幕左上角的x和y的坐标
TouchSlop:指的是系统所能识别出的被认为是滑动的最小距离,当2次滑动的距离小于这个值时,系统则认为其不是滑动。其常量值获取方式为:ViewConfiguration.get(getContext()).getScaledTouchSlop().可用于优化滑动体验。
一般如果只是监听滑动相关的,建议在OnTouchEvent中实现,如果要监听双击这种行为的,就需要使用GestureDetector手势识别器。
View的滑动实现的三种方式:
1,通过View本身提供的SrcollBy和SrcollTo来实现,SrcollBy实际上也调用了SrcollTo方法,在滑动过程中,mSrcollX的值总是等于其左View左边缘和View内容左边缘在水平方向的距离,mSrcollY的值总是等于其View上边缘和View内容上边缘在竖直方向的距离,View的边缘位置表示View的位置,四个顶点构成。同时其2者的滑动只能改变View的内容的位置而不能改变其View在布局中的位置。适用于对view内容的滑动。
2,通过动画给View施加平移的效果来实现滑动。适用于没有交互的view和实现复杂的动画效果
3,通过改变View的LayoutParams使得View重新布局而实现滑动。使用于有交互的view。
View事件分发机制
dispatchTouchEvent:用于事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用,返回的结果受当前View的OnTouchEvent和下级View的onIntercepTouchEvent方法的影响,表示是否消耗当前事件。
onInterceptTouchEvent:用于判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件的序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
onTouchEvent:在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。
View事件分发机制论点:
0,当一个View需要处理事件时,如果设置了OnTouchListener,那么其onTouch方法会被回调,如果其返回false,则当前View的onTouchEvent方法会被调用,反之,不会。说明了给View设置的onTouchListener事件比onTouchEvent事件优先级别高。
1,正常情况下,一个事件序列只能被一个View拦截且消耗。
2,某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递的化),并且它的onIntercepTouchEvent不会被再调用。
3,某个View一旦开始处理事件,如果它不消耗ACTION_DOWN(onTouchEvent返回了false),那么同一个事件序列中的其他事件都不会再交给它来处理,并且事件将重新交给它的父元素处理,即父元素的onTouchEvent会被调用。
4,ViewGroup默认不拦截任何事件,其ViewGourp的onInterceptTouchEvent方法默认返回了false。
5,View没有onInterceptTouchEvent方法,一旦有点击事件传递给它,那么其onTouchEvent方法就会被调用。
6,View的onTouchEvent默认都会消耗事件(返回true),除非它时不可点击的(clickable和longclickable同时为false)。View的longclickable属性默认都为false。一般来说可点击的View的clickable默认是false,可点击的View的clickable属性为true。TestView的clickable默认属性为false,button的clickable属性默认时ture。
7,View的enable属性不影响onTouchEvent的默认返回值,默认返回ture。
8,onclick会发生的前提时当前View是可点击的,并且它收到了down和up的事件
9,事件传递过程是由外向内的,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。一旦设置了此方法,则ViewGourp将无法拦截除了ACTION_DOWN以外的其他事件,,因为ACTION_DOWN会重置其标记位,导致其方法失效。
10,传递过程是Activity--->window(phoneWindow)---->view(decorView顶层的view,其通过setContentView获得,一般顶层的View都是ViewGroup,看布局xml可得知)
View滑动冲突的场景:
1,外部滑动方向和内部滑动方向不一致,处理规则:当用户左右滑动时,需要让外部的View拦截点击事件,当用户上下滑动时,需要让内部的View拦截点击事件。
**解决方案:**
外部拦截法:指点击事件都先经过父容器的拦截处理,其需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可。其ACTION_DOWN(一旦返回了true,后续的移动和抬起事件都会交给父容器处理)和ACTION_UP都必须返回false。
内部拦截法:指的时父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交由父容器进行处理,需要配合requestDIsallowInterceptTouchEvent方法才能进行处理,需要重写子元素的dispatchTouchEvent方法
parent.requestDIsallowInterceptTouchEvent(true)表示通知父容器不拦截此事件。
2,外部滑动方向和内部滑动方向一致
3,以上2种情况的综合
针对外部滑动方向和内部滑动不一致解决方法之一(外部拦截法):
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
//父容器 默认不拦截按下事件
intercepted = false;
if (!mScroller.isFinished()) {
Log.d(TAG,"SSS");
mScroller.abortAnimation();
intercepted = true;
}
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
}
case MotionEvent.ACTION_UP: {
intercepted = false;
break;
}
default:
break;
}
Log.d(TAG, "intercepted=" + intercepted);
mLastX = x;
mLastY = y;
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}
View的工作细节:
View的显示需要经过onDraw,onmeasure,onlayout三个过程,其measure决定了View的宽和高,可以通过getMeasureWidth和getMeasureHeight方法获取到View测量后的宽和高
MeasureSpec(策略规格):MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SpecSize。SpecMode指的是测量模式,SpecSize指的是某种策略模式下的测量大小。
SpecMode三种模式:
1,UNSPECIFIED: 父容器不对View有任何限制,要多大给多大,一般用于系统内部
2,EXACTLY: 父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这二种模式
3,AT_MOST: 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体的是什么值要看不同View的具体实现,它对应于LayoutParams中的wrap_content .
具体的View的测量模式和大小 | |
---|---|
自定义View须知:
1,让View支持wrap_content,因为直接在布局中使用wrap_content是不生效的,所以需要在onMeasure中对wrap_content进行处理
2,如果有必要,让View支持padding属性。如果不在onDraw方法中处理padding,那么padding是无法起到作用的
3,尽量不要在View中使用Handle
4,View中如果有线程或者动画,需要及时停止,
以上就是View的一些认知。