View 类绘制
View 类的基本绘制流程
-
基本属性:4个方向位置,m
mLeft; mTop; mBottom; mRight;
-
修改属性的方法:修改了上述的4个值
- setFrame(int left, int top, int right, int bottom)
-
基本读取属性方法:
-
getHeight()
-
getWidth()
-
-
layout文件中的设置宽高的属性转换
match_parent --计算出屏幕上的值->MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY)
wrap_parent --计算出屏幕上的值->MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST)
default(layout_width="48dp"指定大小) ---> MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
- onLayout(xxx)方法:只给ViewGroup调用
- 如果该View是一个ViewGroup时,可以调用View中onLayout(xxx)方法,assign a size and position to each of its children
ImageView
-
onMeasure(int widthMeasureSpec, int heightMeasureSpec):用来测量自身的大小
- 测量出自身的真实宽高(mDrawableWidth+pleft + pright)
- 与自身被设置的最小值比较,取最大值
- 与布局指定的大小比较 widthMeasureSpec ,根据Mode得到最终的合适值
由参数可得Mode是否可以父类指定,还是由子类指定:
final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);可以知道其他参数:自身drawable的宽高,四个方向的padding
w = mDrawableWidth;
h = mDrawableHeight;
int pleft = mPaddingLeft;
int pright = mPaddingRight;
int ptop = mPaddingTop;
int pbottom = mPaddingBottom;
w += pleft + pright;
h += ptop + pbottom;最后计算出:w,h 为自身的宽高,加上相应的padding值:
w = Math.max(w, getSuggestedMinimumWidth());//getSuggestedMinimumWidth 由layout布局父类调用,决定该view的最小值。 h = Math.max(h, getSuggestedMinimumHeight());
根据指定的大小 widthMeasureSpec 和 测量的大小w,获取最终的结果:
widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);
heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);调用设置方法,传入最终值:
setMeasuredDimension(widthSize, heightSize);
View类的resolveSizeAndState方法:
-
根据measureSpec得到Mode,由Mode去决定真实的值
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED: //随子类想要
result = size;
break;
case MeasureSpec.AT_MOST: //尽量满足子类所要的大小
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY: //由父类制定的情况
result = specSize;
break;
}
return result | (childMeasuredState&MEASURED_STATE_MASK);
} -
onDraw(Canvas canvas):绘制自身的样子
总结
-
流程:measure(计算自身大小) ----> layout(计算在布局的位置)
-
首先getMeasureWidth()方法在measure()过程结束后就可以获取到了,而getWidth()方法要在layout()过程结束后才能获取到。
-
另外,getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的,而getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。
measure() {
onMeasure() {
setMeasuredDimension()
};
}layout(){
onMeasure(){setMeasuredDimension()};
setFrame(l, t, r, b);
} -
复写子类的流程:
- onMeasure():计算自身的大小,最后要调用setMeasuredDimension
- onLayout(l,t,r,b):计算自身在布局中的位置,其中的参数(l,t,r,b)是相对父布局而言,这个只能给ViewGroup的子类实现调用。
- onDraw():描绘自己的样子。
视图状态
-
View focusSearch(int direction):
* @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT * Find the nearest view in the specified direction that can take focus. * This does not actually give focus to that view. * * @return The nearest focusable in the specified direction, or null if none * can be found.
View 类的监听事件(与用户,外界的交流)
-
static class ListenerInfo :一个监听器的域(容器),包含了各种类型的监听器对象
-
内部监听
//监听View的focus状态的改变 protected OnFocusChangeListener mOnFocusChangeListener; //监听View 绘制过程中的layout改变(在layout()方法中调用) private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners; private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
-
外部监听
//监听用户按下实体键,如menu键 private OnKeyListener mOnKeyListener; //监听用户的手势 private OnGenericMotionListener mOnGenericMotionListener; public OnClickListener mOnClickListener; protected OnLongClickListener mOnLongClickListener; protected OnCreateContextMenuListener mOnCreateContextMenuListener; private OnTouchListener mOnTouchListener; private OnHoverListener mOnHoverListener; private OnDragListener mOnDragListener; private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;