深入理解Android View(1)
做android其实也有一段时间了,我们每个人都会碰到一些这样或那样的问题,碰到问题了就拼命百度,可是发现,我们解决问题的能力并没有提升很多,所以我才有想总结一下我项目中所用过的相关知识,并了解一下Android源代码中是如何定义这些属性的,如何去实现的。以后再碰到类似的问题,我该如何实现。本人也不常写博客,希望各位博友能指点,分享,并提出博客中不正确的地方,共勉!
首先我发一份我做的关于Android View深入实现的的XMind的思维导图,可以帮助我一起整理思路,若是博友有什么想到的地方可以帮我一起补充。
今天首先来介绍下View,View字面意思理解就是视图,什么是视图,说白了UI,你在屏幕上看到的,呈现在你眼前的东西都是属于视图。但是有个问题:我们在做刷新视图的时候,要用Handler机制来对UI组件进行相应的刷新,为什么不能直接在UI线程中做相关的刷新呢?若是在做一些比较费时的操作,比如读取数据库,网络访问,这个时候我们基本都会在UI线程中开一个子线程,然后利用Handler机制去子线程中取相应的数据,利用消息通知机制来UI主线程中刷新数据。我的理解是:其实UI,View,这些可以看得到的组件在运行的过程中,首先他要创建相关的UI的属性,比如要显示的图形的坐标点,大小,长宽,然后有一个画笔和画布,画上去的。就是说,电脑屏幕上你所有看到的东西都是画上去的。但是你发现,一旦断电,屏幕上的东西立马就不见了,是的!屏幕其实一直在不断的重绘,主要是利用了人眼的视觉暂留效果,两次重绘在0.05秒-0.2秒的时候,人不会发现发现屏幕在闪烁!计算机里有一个叫UI线程,其实他只做绘画,只管不停地在屏幕上不断画画,而且要再那么短的时间不断画,他是一个死循环,他很繁忙,还有一点,比如你操作数据库的数据,通过网络访问数据这些都是比较慢的操作,内存取数据>文件取数据>网络访问请求取数据。这个时候若是在UI主线程中去做非时操作,UI就阻塞了。那这个时候需要开辟一个子线程,他来做数据相关的操作,Handler的作用就好像是UI线程和子线程之间的信使,UI主线程这个时候发出一个通知:郑打理朝政繁忙,哪个子民可以为我*息边疆的动乱,这个时候,主线程不是直接把这个话通知到下面的所有臣子,而是用了个中间的对象,就好比皇帝身边的人,先告诉他:最*要*息边疆动乱,你给我告诉传达到每一个下面的臣子,务必传达到!好了,于是Handler就忙起来了,他忙着通知,接受大臣的反馈,大臣接受到这个间接的通知之后,带兵大战,驰骋沙场。那老皇帝等的着急吧,战争胜利了还是失败了呢?还是通过Handler把子线程运行的结果告诉Handler,Handler把结果拿给UI线程,一做刷新就完美呈现了!这个过程有什么好处呢,就是保证每个事物,每个人都分工很明确,各干各的事情。UI线程只做UI组件的创建,渲染,重绘,而Handler只管理UI线程和子线程之间的交互和反馈,子线程只管做自己擅长的具体事情就好了,他唯一要做的是把运行的结果告诉Handler,Handler再拿着结果反馈给UI线程,UI再拿着这个结果去刷新屏幕。
其实不光光是UI绘制这个过程是这样的,比如:我们在点击一个Button的时候,会给他注册一个setOnClickListener(),但是UI组件自己是不知道自己干什么的,UI是不知道自己是被点击了,还是被滑动了,他只管自己绘画,而真正的处理这个事情的过程是由另一个接口来处理的,相应的接口做相应的处理,处理的结果再由回调来响应。
说了那么多,都只是刚开始,其实这是一种很好的思想,叫做委托,比如你因为各种原因,没时间,没有资源,我委托一个中间的代理来帮我找能帮我做相应事情的人,然后这个代理再把找到的结果反馈给你。你要租房子,但是找不到很多的房源,那就要通过第三方比如中介来做这个事情了。
在说View之前,我提出了很多问题:我的学习方法是带着问题从源代码中去找答案。这样才能知其然而知其所以然,然后用理论再在结合实际的一些小例子
(1)首先一个View要绘制之前,肯定要定义相应的属性和方法,比如坐标点,长,宽,高,绘制的方式,绘制的风格,这些是哪里定义的呢?
(2)View是如何被创建出来的呢,绘图绘制的机制是如何的?是如何把相应的属性结合起来呈现视图效果的?
(3)一个View他要占地方,他的位置如何确定的。他的大小如何确定的?
(4)一个View画出来之后,其实就像水氢原子+氧原子+电产生的,可是产生了之后用什么来存放呢?
(5)就一个图形放在那里,我们不知道他如何和外部交互啊?UI叫做UserInterface,用户接口,既然是给用户用的,那我怎么和计算机交互呢?怎么样赋予它生命呢,和外界感知?
(6)如何用这些知识在项目中实战
总的来说,分成三个方面,(1)UI的外部形态?(2)谁来产生UI,谁来去创造它?(3)事件机制,能被外界感知到
先来说第一个问题,在说道UI的外部形态的时候,有三个决定他外部形态的函数
-> onDraw()方法
-> onMeasure()方法
-> onLayout()方法
从字面上来 onDraw()和onLayout()很好理解 一个是用来绘制的,一个是用来确定他的位置的,但是这个onMeasure()我查了下英文字典,measure是测量,尺寸的意思,就是测量UI组件的大小的。
还是先来看onDraw()的源代码
咋一看
1 /**
2 * Implement this to do your drawing.
3 *
4 * @param canvas the canvas on which the background will be drawn
5 */
6 protected void onDraw(Canvas canvas) {
7
8 }
其中什么代码也没有?是不是很奇怪,这个时候就应该想是哪个地方调用这个函数的。可以在AndroidStudio下按下ctrl+g
你可以看到有一个方法里调用了onDraw()方法,这个方法就是draw()方法。
首先来看一下英文的解释:注意其中的2点,draw()是通过Canvas来实现的(2)当这个方法draw()方法被调用的时候,这个View其实已经生成了一个完整的布局了,然后后面说的,在编程的时候如果你要在一个子类中需要调用重写该方法,要使用onDraw()方法来替代这个draw()方法
这里要注意2点,第一,View的相关绘制追踪到再底层是由Canvas绘制的,Canvas是画布,可以在画布上绘制各种雏形,然后可以通过跟踪代码的方式按F3,其实在绘制View的时候会首先绘制一个矩形这样的雏形。第二,我有问我网友,我为什么不能重写draw(),若是重写父类的draw()方法我要重写这个onDraw()方法呢,他给我的解释是这样的:
draw()方法是系统的方法,是一个framework层的方法,onDraw()方法是用户的方法,是给用户间接调用实现特定用户需求的方法的
如果要重写draw()方法的话就变成了
1 public void draw(){
2 //framework code
3 //custom code
4 //framework code
5 }
但是若是采用重写onDraw()方法的话就变成了
1 public void draw(){
2 //framework code
3 onDraw();
4 //framework code
其实后面要说到的layout()方法,官方中也是这么解释的,要你使用onLayout()方法来重写,而不是调用layout()方法。这个就是所谓的钩子函数!不知道说的准不准确,欢迎博友一起纠正!继续往下读代码,你可以知道要绘制一个View 需要6个步骤:我简要总结一下,
1。绘制背景。
2.绘制View或者该View存在的子View。
3.还要对View加上相应的滚动条,比如有些View ScrollView就是带有滚动条的,还有有些列表也是带有的。
1 /**
2 * Manually render this view (and all of its children) to the given Canvas.
3 * The view must have already done a full layout before this function is
4 * called. When implementing a view, implement
5 * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
6 * If you do need to override this method, call the superclass version.
7 *
8 * @param canvas The Canvas to which the View is rendered.
9 */
10 public void draw(Canvas canvas) {
11 final int privateFlags = mPrivateFlags;
12 final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
13 (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
14 mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
15
16 /*
17 * Draw traversal performs several drawing steps which must be executed
18 * in the appropriate order:
19 *
20 * 1. Draw the background
21 * 2. If necessary, save the canvas' layers to prepare for fading
22 * 3. Draw view's content
23 * 4. Draw children
24 * 5. If necessary, draw the fading edges and restore layers
25 * 6. Draw decorations (scrollbars for instance)
26 */
27
28 // Step 1, draw the background, if needed
29 int saveCount;
30
31 if (!dirtyOpaque) {
32 drawBackground(canvas);
33 }
34
35 // skip step 2 & 5 if possible (common case)
36 final int viewFlags = mViewFlags;
37 boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
38 boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
39 if (!verticalEdges && !horizontalEdges) {
40 // Step 3, draw the content
41 if (!dirtyOpaque) onDraw(canvas);
42
43 // Step 4, draw the children
44 dispatchDraw(canvas);
45
46 // Step 6, draw decorations (scrollbars)
47 onDrawScrollBars(canvas);
48
49 if (mOverlay != null && !mOverlay.isEmpty()) {
50 mOverlay.getOverlayView().dispatchDraw(canvas);
51 }
52
53 // we're done...
54 return;
55 }
56
57 /*
58 * Here we do the full fledged routine...
59 * (this is an uncommon case where speed matters less,
60 * this is why we repeat some of the tests that have been
61 * done above)
62 */
63
64 boolean drawTop = false;
65 boolean drawBottom = false;
66 boolean drawLeft = false;
67 boolean drawRight = false;
68
69 float topFadeStrength = 0.0f;
70 float bottomFadeStrength = 0.0f;
71 float leftFadeStrength = 0.0f;
72 float rightFadeStrength = 0.0f;
73
74 // Step 2, save the canvas' layers
75 int paddingLeft = mPaddingLeft;
76
77 final boolean offsetRequired = isPaddingOffsetRequired();
78 if (offsetRequired) {
79 paddingLeft += getLeftPaddingOffset();
80 }
81
82 int left = mScrollX + paddingLeft;
83 int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
84 int top = mScrollY + getFadeTop(offsetRequired);
85 int bottom = top + getFadeHeight(offsetRequired);
86
87 if (offsetRequired) {
88 right += getRightPaddingOffset();
89 bottom += getBottomPaddingOffset();
90 }
91
92 final ScrollabilityCache scrollabilityCache = mScrollCache;
93 final float fadeHeight = scrollabilityCache.fadingEdgeLength;
94 int length = (int) fadeHeight;
95
96 // clip the fade length if top and bottom fades overlap
97 // overlapping fades produce odd-looking artifacts
98 if (verticalEdges && (top + length > bottom - length)) {
99 length = (bottom - top) / 2;
100 }
101
102 // also clip horizontal fades if necessary
103 if (horizontalEdges && (left + length > right - length)) {
104 length = (right - left) / 2;
105 }
106
107 if (verticalEdges) {
108 topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
109 drawTop = topFadeStrength * fadeHeight > 1.0f;
110 bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
111 drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
112 }
113
114 if (horizontalEdges) {
115 leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
116 drawLeft = leftFadeStrength * fadeHeight > 1.0f;
117 rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
118 drawRight = rightFadeStrength * fadeHeight > 1.0f;
119 }
120
121 saveCount = canvas.getSaveCount();
122
123 int solidColor = getSolidColor();
124 if (solidColor == 0) {
125 final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
126
127 if (drawTop) {
128 canvas.saveLayer(left, top, right, top + length, null, flags);
129 }
130
131 if (drawBottom) {
132 canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
133 }
134
135 if (drawLeft) {
136 canvas.saveLayer(left, top, left + length, bottom, null, flags);
137 }
138
139 if (drawRight) {
140 canvas.saveLayer(right - length, top, right, bottom, null, flags);
141 }
142 } else {
143 scrollabilityCache.setFadeColor(solidColor);
144 }
145
146 // Step 3, draw the content
147 if (!dirtyOpaque) onDraw(canvas);
148
149 // Step 4, draw the children
150 dispatchDraw(canvas);
151
152 // Step 5, draw the fade effect and restore layers
153 final Paint p = scrollabilityCache.paint;
154 final Matrix matrix = scrollabilityCache.matrix;
155 final Shader fade = scrollabilityCache.shader;
156
157 if (drawTop) {
158 matrix.setScale(1, fadeHeight * topFadeStrength);
159 matrix.postTranslate(left, top);
160 fade.setLocalMatrix(matrix);
161 p.setShader(fade);
162 canvas.drawRect(left, top, right, top + length, p);
163 }
164
165 if (drawBottom) {
166 matrix.setScale(1, fadeHeight * bottomFadeStrength);
167 matrix.postRotate(180);
168 matrix.postTranslate(left, bottom);
169 fade.setLocalMatrix(matrix);
170 p.setShader(fade);
171 canvas.drawRect(left, bottom - length, right, bottom, p);
172 }
173
174 if (drawLeft) {
175 matrix.setScale(1, fadeHeight * leftFadeStrength);
176 matrix.postRotate(-90);
177 matrix.postTranslate(left, top);
178 fade.setLocalMatrix(matrix);
179 p.setShader(fade);
180 canvas.drawRect(left, top, left + length, bottom, p);
181 }
182
183 if (drawRight) {
184 matrix.setScale(1, fadeHeight * rightFadeStrength);
185 matrix.postRotate(90);
186 matrix.postTranslate(right, top);
187 fade.setLocalMatrix(matrix);
188 p.setShader(fade);
189 canvas.drawRect(right - length, top, right, bottom, p);
190 }
191
192 canvas.restoreToCount(saveCount);
193
194 // Step 6, draw decorations (scrollbars)
195 onDrawScrollBars(canvas);
196
197 if (mOverlay != null && !mOverlay.isEmpty()) {
198 mOverlay.getOverlayView().dispatchDraw(canvas);
199 }
200 }
201 那倒这里我又有一个疑惑了,为什么有些View是带有滚动条的呢,而有些View就是没有呢,比如我在项目中用一个ListView,若是ListView中有很多很多条数据,加入数据超过了相应的显示长度,这个时候我们是不是要在ListView里加一个滚动条组件,这样就可以允许用户通过滚动条的滑动来查看下面的数据,但是:有些组件,比如我要上传自己的一个照片到服务器端,但是照片太大了 ,我要进行相应的裁剪,这个时候View其实是做了一个裁剪的操作,就是说:每个不同的View其实根据实际的应用需求来对View做相应的处理。那具体是根据什么方式来划分的呢?别着急,这些源代码里都有下面有一个方法确定View大小的方法 onMeasure()里就有涉及到这方面的处理!
202 当然draw()方法还有一个重载的方法 ,这个英文的描述:
203 这个方法是被ViewGroup.drawChild()方法来调用的,用来绘制依次在ViewGroup里的childView。这样一说其实也就明白了,原来Viewgroup就是一个View的容器,他是用来装View的地方的。那可以联想到我们常用的LinearLayout,RelativeLayout布局容器,他们其实也不就是ViewGroup嘛,在写XML的时候他们是作为根节点的,他们下面有很多子节点作为父容器的孩子节点。
204 再来看一下确定View位置的相关方法Layout()方法,为什么把这2个方法放在一起说呢,就是因为刚才说到一点,layout()方法和draw()方法一样,若是子类要重写父类这个方法的时候,也是要调用onLayout()方法来重写,所以他的代码结构其实和draw()方法是一样的。
205 ublic void layout(int l, int t, int r, int b) {
206 if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
207 onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
208 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
209 }
210
211 int oldL = mLeft;
212 int oldT = mTop;
213 int oldB = mBottom;
214 int oldR = mRight;
215
216 boolean changed = isLayoutModeOptical(mParent) ?
217 setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
218
219 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
220 onLayout(changed, l, t, r, b);
221 mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
222
223 ListenerInfo li = mListenerInfo;
224 if (li != null && li.mOnLayoutChangeListeners != null) {
225 ArrayList<OnLayoutChangeListener> listenersCopy =
226 (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
227 int numListeners = listenersCopy.size();
228 for (int i = 0; i < numListeners; ++i) {
229 listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
230 }
231 }
232 }
233
234 mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
235 mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
236 }
在这个方法里要分三段来看,(1)setFrame() 要设置改组件距离左边,上边,底部和右边的距离。这几个值构成的矩形的区域就是该View显示的位置,不过这里的具体位置都是相对父容器的(2)此处有一个判断位置是否改变的返回boolean值的函数,若是位置改变,则进行相应的重绘。然后回调onLayout函数,最后回调所有注册过的listener的onLayoutChange函数。对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数:
然后在看代码的时候我发现一个细节要特别注意,setFrame()有一个这个方法中notifySubtreeAccessibilityStateChangedIfNeeded()
该方法我单从字面意思上去理解就是说,这个方法是父容器通知子容器的有效重绘的通知,为了不必要的系统开销而建立的通知机制。
然后按F3跟踪进去
1 **
2 * Notifies that the accessibility state of this view changed. The change
3 * is *not* local to this view and does represent structural changes such
4 * as children and parent. For example, the view size changed. The
5 * notification is at at most once every
6 * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
7 * to avoid unnecessary load to the system. Also once a view has a pending
8 * notification this method is a NOP until the notification has been sent.
9 *
10 * @hide
11 */
12 public void notifySubtreeAccessibilityStateChangedIfNeeded() {
13 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
14 return;
15 }
16 if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
17 mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
18 if (mParent != null) {
19 try {
20 mParent.notifySubtreeAccessibilityStateChanged(
21 this, this, AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
22 } catch (AbstractMethodError e) {
23 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
24 " does not fully implement ViewParent", e);
25 }
26 }
27 }
28 }
29 三,再来看view中非常重要的方法 onMeasure()方法。这个方法从字面意思上来猜,就是测量View的长度的方法
30 其实有没有观察到这个细节:注释里面有用html标签<p></p>这个也很帮助你阅读代码,<p></p>就是划分段落,要理解这个代码,一个段落代表一个意思:首先是整体概括:这个方法被他的子类所重写,目的是为子类的内容来提供高效,准确的测量。
31 <1> 当内容被重写的时候,你必须调用setMeasuredDimension(int,int)去存储测量measure的宽度和高度的值,如果该操作失败了,就会抛出一个IllegalStateException,但是有一个前提:要确保父类的onMeasure(int,int)调用是一个有效的调用。所以第一步做的操作其实很简单,就是由View的子类通过调用父类的setMeasuredDimension(int,int)方法,来存储当前View的宽度和高度值,若是没有存储成功,那就会抛出相应的异常,知道了这个原理了之后,以后碰到这个IllegalStateException再也不会恐慌了!
32 <2> 除非是有更大的尺寸允许,否则这个父类会实现测量一个默认的大小值。当父类容器的默认尺寸容纳不下子View的时候,那会重新调用onMeasure()进行重绘。
33 <3> 一但width和height计算好了,就应该调用View.setMeasuredDimension(int width,int height)方法,否则将导致抛出异常.
34 然后这三步步骤挑一个我看来比较重要的,就是第二步。
35 测量需要2个关键的东西:大小(size)和模式(mode)
36 measureSpec,size,mode他们三个的关系,都封装在View类中的一个内部类里,名叫MeasureSpec
37 此处有一个静态的MeasureSpec的内部类。
38 /**
39 * MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求
40 * MeasureSpec由size和mode组成。
41 * 三种Mode:
42 * 1.UNSPECIFIED
43 * 父没有对子施加任何约束,子可以是任意大小(也就是未指定)
44 * (UNSPECIFIED在源码中的处理和EXACTLY一样。当View的宽高值设置为0的时候或者没有设置宽高时,模式为UNSPECIFIED
45 * 2.EXACTLY
46 * 父决定子的确切大小,子被限定在给定的边界里,忽略本身想要的大小。
47 * (当设置width或height为match_parent时,模式为EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的)
48 * 3.AT_MOST
49 * 子最大可以达到的指定大小
50 * (当设置为wrap_content时,模式为AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸)
51 *
52 * MeasureSpecs使用了二进制去减少对象的分配。
53 */
54 通过查看其中的源代码可以很清楚的知道,原来,他们设置了三种规定如何处理View大小边界的模式,
55 <1>当没有超过默认限制的大小,那就按该多大就多大处理
56 <2>超过默认限制的大小里又分了2种情况,一种是超过默认大小直接给裁剪掉
57 <3>第二种是超过默认大小的时候,会加入滚动条这样的属性。
58 /**
59 * <p>
60 * Measure the view and its content to determine the measured width and the
61 * measured height. This method is invoked by {@link #measure(int, int)} and
62 * should be overriden by subclasses to provide accurate and efficient
63 * measurement of their contents.
64 * </p>
65 *
66 * <p>
67 * <strong>CONTRACT:</strong> When overriding this method, you
68 * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
69 * measured width and height of this view. Failure to do so will trigger an
70 * <code>IllegalStateException</code>, thrown by
71 * {@link #measure(int, int)}. Calling the superclass'
72 * {@link #onMeasure(int, int)} is a valid use.
73 * </p>
74 *
75 * <p>
76 * The base class implementation of measure defaults to the background size,
77 * unless a larger size is allowed by the MeasureSpec. Subclasses should
78 * override {@link #onMeasure(int, int)} to provide better measurements of
79 * their content.
80 * </p>
81 *
82 * <p>
83 * If this method is overridden, it is the subclass's responsibility to make
84 * sure the measured height and width are at least the view's minimum height
85 * and width ({@link #getSuggestedMinimumHeight()} and
86 * {@link #getSuggestedMinimumWidth()}).
87 * </p>
88 *
89 * @param widthMeasureSpec horizontal space requirements as imposed by the parent.
90 * The requirements are encoded with
91 * {@link android.view.View.MeasureSpec}.
92 * @param heightMeasureSpec vertical space requirements as imposed by the parent.
93 * The requirements are encoded with
94 * {@link android.view.View.MeasureSpec}.
95 *
96 * @see #getMeasuredWidth()
97 * @see #getMeasuredHeight()
98 * @see #setMeasuredDimension(int, int)
99 * @see #getSuggestedMinimumHeight()
100 * @see #getSuggestedMinimumWidth()
101 * @see android.view.View.MeasureSpec#getMode(int)
102 * @see android.view.View.MeasureSpec#getSize(int)
103 */
104 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
105 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
106 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
107 }
谈到这里算是结束了最基本的View的三个比较重要的方法的阐述。
然后,我这里还会提出一个问题:
你在创建对象的时候,都会用该对象的构造方法。我们每次都是通过构造方法来实现对该对象的内存分配,属性赋值等相关操作,那View的构造方法上有什么可以值得推敲的地方呢,这个时候促使我想起,View是怎么来的,如何被创建的。
先小看一下View的三个构造方法:
1.public void CustomView(Context context) {}
2.public void CustomView(Context context, AttributeSet attrs) {}
3.public void CustomView(Context context, AttributeSet attrs, int defStyle) {}
那这个时候我们需要好好的问问自己:为什么要定义三个构造方法呢,2个不够用嘛?然后再猜想一下这个AttributeSet和 defStyle是用来干嘛的?从名字看是关于View的设计风格的文件的,那他又是如何加载这些设计风格的文件呢?程序员去编程的时候如何使用这些用户接口呢?将再下一块涉及到这些问题。
深入理解Android View(1)的更多相关文章
- 深入理解Android View(转)
做android其实也有一段时间了,我们每个人都会碰到一些这样或那样的问题,碰到问题了就拼命百度,可是发现,我们解决问题的能力并没有提升很多,所以我才有想总结一下我项目中所用过的相关知识,并了解一下A ...
- 深入理解android view 生命周期
作为自定义 view 的基础,如果不了解Android view 的生命周期 , 那么你将会在后期的维护中发现这样那样的问题 ....... 做过一段时间android 开发的同学都知道,一般 on ...
- 【转载】快速理解android View的测量onMeasure()与MeasureSpec
笔者之前有一篇文章已经使用onMeasure()解决了listview与scollview的显示冲突问题,博客地址如下: onMeasure简单方法 完美解决ListView与ScollView冲突问 ...
- 浅析Android View(二)
深入理解Android View(一) View的位置參数信息 二.View的绘制过程 View的绘制过程一共分为三个部分: - measure(測量View的大小) - layout(确定View的 ...
- 深入理解 Android 之 View 的绘制流程
概述 本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定 ...
- [转]Android View.onMeasure方法的理解
转自:http://blog.sina.com.cn/s/blog_61fbf8d10100zzoy.html Android View.onMeasure方法的理解 View在屏幕上显示出来要先经过 ...
- 深入理解Android中View
文章目录 [隐藏] 一.View是什么? 二.View创建的一个概述: 三.View的标志(Flag)系统 四.MeasureSpec 五.几个重要方法简介 5.1 onFinishInflate ...
- 【转】深入理解Android之View的绘制流程
概述 本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定 ...
- 谈谈我对Android View事件分发的理解
写这篇博客的缘由.近期因为项目中用到相似一个LinearLayout中水平布局中,有一个TextView和Button,然后对该LinearLayout布局设置点击事件.点击TextView能够触发该 ...
- 虾扯蛋:Android View动画 Animation不完全解析
本文结合一些周知的概念和源码片段,对View动画的工作原理进行挖掘和分析.以下不是对源码一丝不苟的分析过程,只是以搞清楚Animation的执行过程.如何被周期性调用为目标粗略分析下相关方法的执行细节 ...
随机推荐
- [GPT] Vue 的 methods 中使用了 addEventListener,如何在 addEventListener 的匿名函数参数中访问 Vue data 变量
在 Vue 的 methods 方法中使用 addEventListener时,你可以使用 箭头函数 来访问 Vue 实例的数据. 箭头函数不会创建自己的作用域,而是继承父级作用域的上下文.以下是 ...
- 使用 Docker 自建一款怀旧游戏之 - 超级马里奥
1)超级马里奥 简介 < 超级马里奥 >(Super Mario)是任天堂公司创造的一款经典游戏系列,是世界上最知名.最成功的游戏之一.这个系列由日本设计师宫本茂于 1985 年创造,最初 ...
- EasyRepro与测试自动化( 一) 概览
EasyRepro是一个框架,允许在特定的Dynamics 365组织上执行自动化UI测试.你可以使用它来自动化冒烟测试.回归测试和负载测试等.该框架是由开源项目Selenium构建的,Seleniu ...
- Goland 的配置
目录 下载安装 设置好go的系统环境变量 设置 GOROOT 设置 GOPATH 设置 MODULES 设置 工作面板里的字体缩放大小快捷键 安装主题包 安装中文中包 Redis Mannager 读 ...
- Java 获取两个时间的时间差
前言 在平时的工作中,难免会遇到获取两个时间相差多少天.小时.分钟.秒.毫秒,现在我将自己获取的方法总结如下: 一.导入需要的依赖 <dependency> <groupId> ...
- C数据结构:循环队列的顺序存储结构
顺序队列目录 队列的定义 定义 假溢出 空间浪费的缺点 如何解决 循环队列的缺点 *==主要的算法思想(重要)==* 如何理解循环队列(必看) 结构体代码 两种实现方法 **①循环队列,队头和队尾指针 ...
- 30分钟带你搞定Dokcer部署Kafka集群
docker网络规划 docker network create kafka-net --subnet 172.20.0.0/16 docker network ls zookeeper1(172.2 ...
- 物联网平台选型葵花宝典:盘点开源、SaaS及通用型平台的优劣对比
随着工业物联网领域和智慧物联领域的发展,大大小小的物联项目和物联场景需求层出不穷,物联网平台作为技术底座型软件,是不可或缺的项目地基. 市场需求下,物联网平台提供商越来越多,"打地基&quo ...
- Linux安装ElastSearch
Linux安装ES 准备好Linux系统,软件安装前需要对当前系统做一些优化配置 系统配置修改 一.内存优化 在/etc/sysctl.conf添加如下内容: fs.file-max=655360 系 ...
- 将外部jar打入本地maven仓库
1.将jar包放入某不含中文的路径下 ,例如:E:\file\zip4j-1.3.2.jar 2.在命令行输入操作命令 mvn install:install-file -DgroupId=zip4j ...