学习笔记:Android中的View

返回本站首页

“Android中的View” 知识点学习梳理,包括了View事件的处理、View绘制3大流程等内容。

用XMind画的思维导图,导出为HTML格式了,点开页面可以查看清晰的图片,以及各部分的导出文字。

点击这里下载 View知识点 XMind原始文件

因为图片太大,如果渲染为与本站一致的主题会导致图片看不清,所以本页面未进行样式渲染,比较丑,凑合着看吧。😊






Android中的View



View点击、滑动事件相关



 基础知识



  view坐标



   top、left、right、bottom:
分别是view左上角的纵坐标、横坐标,右下角的横坐标、纵坐标。它们都是相对于父容器的,并且在view平移过程中,这4个值不会改变。



   x、y:
view的左上角坐标,当view移动时这两个值会改变。



   translationX、translationY:
view左上角相对于父容器的偏移量,会随着view的移动过而改变。



   3者的联系:
x = left + translationX
y = top + translationY


(top、left、right、bottom:
分别是view左上角的纵坐标、横坐标,右下角的横坐标、纵坐标。它们都是相对于父容器的,并且在view平移过程中,这4个值不会改变。
, x、y:
view的左上角坐标,当view移动时这两个值会改变。
, translationX、translationY:
view左上角相对于父容器的偏移量,会随着view的移动过而改变。
)



  触摸事件坐标:
通过MotionEvent对象获取坐标



   getX/getY返回的是相对于当前View左上角的x和y坐标



   getRawX/getRawY返回的是相对于手机屏幕左上角的x和y坐标



  工具类



   VelocityTracker:速度追踪



   GestureDectector:手势检测,可用于检测单击、滑动、长按、双击等



   Scroller:弹性滑动



 View的滑动



  使用scrollTo/scrollBy



   在滑动过程中,mScrollX的值总是等于View左边缘和View内容左边缘在水平方向的距离,mScrollY亦然。
View边缘是指View的位置,由四个顶点组成,而View内容边缘是指View中的内容的边缘。scrollTo和scrollBy只能改变View内容的位置而不能改变View在布局中的位置。当View左边缘在View内容左边缘的右边时,mScrollX为正值,反之为负值。换句话说,如果从左向右滑动,那么mScrollX为负值,反之为正值。mScrollY亦然。



  使用动画



   View动画:只能移动影像,实际view的位置并没有变化。这就导致,用view动画使一个按钮向右移动了100dp,但实际去点击按钮所在的新位置时并不会有响应,而点击按钮原来的位置(现在已近不再此位置)却会有响应。



   属性动画:可以解决上面的问题,但从Android3.0才支持属性动画。



  改变布局参数。即修改LayoutParams。



 View的事件分发机制



  点击事件产生后按照如下顺序传递:
Activity -> Window -> View



  View点击事件传递顺序:
dispatchTouchEvent -> onInterceptTouchEvent -> onTouchEvent



  顶级View对点击事件的详细分发过程如图所示


参见: 从顶级View开始的分发过程



  View没有onInterceptTouchEvent方法,ViewGroup才有此方法。
一旦有点击事件传递给View,它的onTouchEvent方法就会被调用。



  当前界面的底层容器是decor view,即setContentView所设置的View的父容器,通过Activity.getWindow.getDecorView()可以获得。



  当ViewGroup决定拦截事件后,后续的点击事件将会默认交给它处理并且不再调用它的onInterceptTouchEvent方法。



View工作原理



 View绘制相关



  View的工作流程:
measure –> layout –> draw
其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw将View绘制到屏幕上。



  DecorView是顶级View,本质是一个FrameLayout,一般包含一个竖直方向的LinearLayout,这个LinearLayout有上下两部分(与Android版本和主题有关),上面是标题栏,下面是内容栏。在Activity中我们通过setContentView所设置的布局文件就是被加入到内容栏中,内容栏可以通过findViewById(android.R.id.content)来获得。



  MeasureSpec



   MeasureSpec是一个32位值,高2位代表SpecMode,低30位代表SpecSize。



   SpecMode有3类



    UNSPECIFIED:父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量状态。



    EXACTLY:父容器已经检测出View所需的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中的match_parent和具体的数值这两种模式。



    AT_MOST:父容器制定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content。



   MeasureSpec转换过程



    顶级View(DecorView):MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定。



    普通View:MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定


参见: 普通View的MeasureSpec创建规则



    MeasureSpec一旦确定,onMeasure中就可以确定View的测量宽/高。


(顶级View(DecorView):MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定。, 普通View:MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定)



   直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时自身大小,否则在布局中使用wrap_content就相当于使用match_parent。



  如果想在Activity一启动的时候就做一件任务,但这一任务需要获取某个View的宽/高,由于View的measure过程和Activity的生命周期方法不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经测量完毕了,那么获得的宽/高就是0。有以下4种方法可以解决这个问题。



   Activity/View#onWindowFocusChanged:此方法会被调用多次,当Activity的window得到或失去焦点时均会被调用一次。



   view.post(runnable):通过post可以将一个runnable投递到消息队列尾部,等待Looper调用此runnable的时候,View已经初始化好了,在run方法体中获取宽/高。



   ViewTreeObserver:使用ViewTreeObserver的众多回调都可以实现。比如使用OnGlobalLayoutListener接口,当View树状态发生改变或者View树内部的View的可见性发生改变时,onGlobalLayout会被回调,可在这里获取View的宽/高,会被回调多次。



   view.measure:根据View的LayoutParams的类型来分别处理,较复杂。



  View默认没有启用setWillNotDraw,而ViewGroup默认启用了。



 自定义View



  尽量不要在View中使用Handler,因为View内部本身提供了post系列的方法,完全可以替代Handler的作用,除非你很明确地要使用Handler来发送消息。



  View如果有现成或者动画需要停止时,那么onDetachedFromWindow是一个很好的时机。


参见: 当包含此View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用,和此方法对应的是onAttachedToWindow方法会被调用。同时,当View变得不可见时我们也需要停止线程和动画,如果不及时处理这种问题,有可能会造成内存泄露。



从顶级View开始的分发过程




参见: 顶级View对点击事件的详细分发过程如图所示



普通View的MeasureSpec创建规则




参见: 普通View:MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定



当包含此View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用,和此方法对应的是onAttachedToWindow方法会被调用。同时,当View变得不可见时我们也需要停止线程和动画,如果不及时处理这种问题,有可能会造成内存泄露。


参见: View如果有现成或者动画需要停止时,那么onDetachedFromWindow是一个很好的时机。