Android View
来源:互联网 发布:学英语软件 编辑:程序博客网 时间:2024/06/08 14:15
1 scrollyTo和ScrollyBy
首先scrollyTo和ScrollyBy移动的都是View里的内容。
源码中:
public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
mScrollX和mScrollY为View现在的位置离初始位置的距离。
所以scrollyTo(x,y)移动距初始位置x和y的长度。
ScrollyBy(x,y)移动距上次位置x和y的长度。
第一个参数x表示相对于当前位置横向移动的距离,正值向左移动,负值向右移动,单位是像素。第二个参数y表示相对于当前位置纵向移动的距离,正值向上移动,负值向下移动,单位是像素。
2 onMeasure 测量视图大小
了解View的测量过程,先了解MeasureSpec。
我们先来瞅瞅官方文档对于MeasureSpec 的介绍:
A MeasureSpec encapsulates the layout requirements passed from parent to child.Each MeasureSpec represents a requirement for either the width or the height.A MeasureSpec is comprised of a size and a mode.
请注意这段话所包含的重要信息点:
1 MeasureSpec封装了父布局传递给子View的布局要求。
2 MeasureSpec可以表示宽和高
3 MeasureSpec由size和mode组成
MeasureSpec通常翻译为”测量规格”,它是一个32位的int数据.
其中高2位代表SpecMode即某种测量模式,低30位为SpecSize代表在该模式下的规格大小.
可以通过如下方式分别获取这两个值:
获取SpecSize
int specSize = MeasureSpec.getSize(measureSpec);
获取specMode
int specMode = MeasureSpec.getMode(measureSpec);
当然,也可以通过这两个值生成新的MeasureSpec
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);
SpecMode一共有三种:
MeasureSpec.EXACTLY , MeasureSpec.AT_MOST , MeasureSpec.UNSPECIFIED
View系统的绘制流程会从ViewRoot的performTraversals()方法中开始,在其内部调用View的measure()方法。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ……… onMeasure(widthMeasureSpec, heightMeasureSpec); ………}
widthMeasureSpec和heightMeasureSpec从何而来?
这两个值都是由根视图经过计算后传递给子视图的,分析ViewRoot中的源码中可以知道,这两个值的mode是MeasureSpec.EXACTLY,size是windowSize,也就意味着根视图总是会充满全屏的。
onMeasure()默认执行
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
public static int getDefaultSize(int size, int measureSpec) { 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: case MeasureSpec.EXACTLY: result = specSize; break; } return result;}
返回specSize,即默认返回windowSize。
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= MEASURED_DIMENSION_SET;}
这样一次measure过程就结束了。
当然,一个界面的展示可能会涉及到很多次的measure,因为一个布局中一般都会包含多个子视图,每个视图都需要经历一次measure过程。ViewGroup中定义了一个measureChildren()方法来去测量子视图的大小,如下所示:
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } }
这里首先会去遍历当前布局下的所有子视图,然后逐个调用measureChild()方法来测量相应子视图的大小,如下所示:
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
这个方法通过调用getChildMeasureSpec(int spec, int padding, int childDimension)计算出子view的MeasureSpec,然后调用子view的measure方法去测量,这就回到了我们上面分析的流程。
在ViewGroup中还有一个方法用来测量子view的MeasureSpec,
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
measureChild和measureChildWithMargins的处理逻辑是一样的,区别在于measureChildWithMargins带上了子view的margin和父view已用空间。
计算子view的MeasureSpec的逻辑在getChildMeasureSpec()这个方法里,方法比较长,就不贴出来了,总结如下图:
一句话,父容器(如LinearLayout)的MeasureSpec和子View的LayoutParams共同决定了子View的MeasureSpec!
在ViewGroup中没有onMeasure方法,而是提供measureChildren等方法供子类选择调用。
需要注意的是,在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。
3 onLayout()确定视图的位置
ViewRoot的performTraversals()方法会在measure结束后继续执行,并调用View的layout()方法来执行此过程,如下所示:
public void layout(int l, int t, int r, int b) { int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight;//判断视图的大小是否发生过变化,以确定有没有必要对当前的视图进行重绘 boolean changed = setFrame(l, t, r, b); if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~LAYOUT_REQUIRED; ListenerInfo li = mListenerInfo; if (li != null && li.mOnLayoutChangeListeners != null) { ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i < numListeners; ++i) { listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } mPrivateFlags &= ~FORCE_LAYOUT; }
onLayout()默认执行:
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {}
ViewGroup会调用onLayout()决定子View的显示位置:
@Overrideprotected abstract void onLayout(boolean changed, int l, int t, int r, int b);
这就是说ViewGroup的子类都必须重写这个方法,实现自己的逻辑。
onLayout()用于指定子View的大小和位置,所以没有子view的view不用实现这个方法,ViewGroup的子类需要去实现这个方法。View用layout()方法确定自己本身在其父View的位置。
getMeasuredWidth()和getWidth()的区别?
getMeasuredWidth()在onMeasure结束后得到,getWidth()在onLayout结束后得到。
getMeasuredWidth()为测量结果,getWidth()为右边坐标 - 左边坐标。
一般情况下,getMeasuredWidth() = getWidth()。
view.getLeft(),view.getRight(),view.getBottom(),view.getTop()?
下图表示:
4 onDraw()视图绘制
measure和layout的过程都结束后,接下来就进入到draw的过程了。
Draw()过程分为6个步骤:
(1) Draw the background 绘制背景
(2) If necessary, save the canvas’ layers to prepare for fading
保存当前画布的堆栈状态并在该画布上创建Layer用于绘制View在滑动时的边框渐变效果,
通常情况下我们是不需要处理这一步的。
(3) Draw view’s content
绘制View的内容,
这一步是整个draw阶段的核心,在此会调用onDraw()方法绘制View的内容。在此onDraw()是一个空方法;因为每个View所要绘制的内容不同,所以需要由具体的子View去实现各自不同的需求。
protected void onDraw(Canvas canvas) {}
(4)Draw children
调用dispatchDraw()绘制View的子View
protected void dispatchDraw(Canvas canvas) {}
有子view的需要去实现。
(5) If necessary, draw the fading edges and restore layers
绘制当前视图在滑动时的边框渐变效果,
通常情况下我们是不需要处理这一步的。
(6) Draw decorations (scrollbars for instance)
绘制View的滚动条,
其实,不单单是常见的ScrollView和ListView等滑动控件任何一个View(比如:TextView,Button)都是有滚动条的,只是一般情况下我们都没有将它显示出来而已。
- android.view.View
- android.view.View---中文
- android.view.View
- android.view.View 相关
- android.view.view
- 有关android.view.View
- Android View---自定义View
- Android View---自定义View
- android.view.View.getImportantForAccessibility()
- android View
- android View
- android View
- Android View
- Android-View
- android-view
- android view
- android view
- Android: View
- 高数Umaru系列(3)——喵星人
- 通过heat创建stack的代码流程分析heat stack-create
- ContentResolver(内容访问者)访问通讯录
- Java HttpUrlConnection多线程下载
- java语言都有哪些优点
- Android View
- ffmpeg--解析h264
- [省选] [线段树] [HLOI2016] 字符串问题
- postgresql字符串函数
- springmvc 配置多视图(jsp,freemarker,HTML等)
- Android四大组件之ContentProvider(内容提供者)02
- 考新郎 递推练习+排列组合。。
- TCP/IP的分析
- ImportError: No module named cv2 解决方法