浅谈getWidth()和getMeasureWidth()区别
来源:互联网 发布:ubuntu terminal替换 编辑:程序博客网 时间:2024/06/09 21:00
一个简单的例子:
重写自定义View的onDraw()代码:
oval.left=getMeasuredWidth()/2-radius; //左边 oval.top=getMeasuredHeight()/2 -radius; //上边 oval.right=getMeasuredWidth()/2 +radius; //右边 oval.bottom=getMeasuredHeight()/2 +radius; canvas.drawArc(oval,0,360,true,mPaint);
得到效果图如下:
这么做肯定没问题。
有这么个疑问:
为什么我不用getwidth,getheight方法?
大多数情况下,getwidth和getMeasureWidth方法得到的结果都是一样的。
回到这个getWidth()方法和getMeasureWidth()的区别这个问题上。
网上说法很多,我决定自己一点点从源码里面扣。然后举例说明。
View#getMeasuredWidth():
public final int getMeasuredWidth() { return mMeasuredWidth & MEASURED_SIZE_MASK; }得到的是最近一次调用measure()方法测量后得到的是View的宽度。
大概跟一下源码知道:
平时我们自定义View会重写onMeasure方法:(什么情况下写onMeasure?后续会有解答)
View#onMeasure源码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }View#onMeasure方法会调用View#setMeasuredDimension方法:
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int opticalWidth = insets.left + insets.right; int opticalHeight = insets.top + insets.bottom; measuredWidth += optical ? opticalWidth : -opticalWidth; measuredHeight += optical ? opticalHeight : -opticalHeight; } setMeasuredDimensionRaw(measuredWidth, measuredHeight); }View#setMeasuredDimension方法会调用View#setMeasuredDimensionRaw方法:
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; }看到这个方法代码第一行第二行有个赋值
mMeasuredWidth = measuredWidth;mMeasuredHeight = measuredHeight;这就是我们的getMeasuredWidth()方法的值。
所以当我们重写onMeasure方法时,如果对setMeasuredDimension()这个方法参数直接自定义,如setMeasuredDimension(200,300),那么getMeasuredWidth()的值必然就是200,getMeasuredHeight()的值必然就是300。
当然,我们一般情况下,不会使用直接这种方式写死参数,一般还是对onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法的参数进行处理,再传入setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
什么情况下我们需要对onMeasure方法重写呢?
因为我们自定义view对于属性为wrap_content这种情况,如果不做处理其实是与match_parent是一样效果的。原因是什么呢?参考如下:任玉刚的书中表格:
当父容器的specmode为EXACTLY和AT_MOST时子view不管是wrap_content还是match_parent,它的默认大小都是父容器大小parentSize。
不信?举个栗子:
直接先上一个自定义view为match_parent时的效果图:
再上一个自定义view为wrap_content时的图:
好吧,没有比较就没有伤害。上一个自定义view为40dp的宽高的图。
这回效果很明显了吧。除非是精确值,否则大小都等于父布局的大小。
那么这我当然不能接受。我wrap_content需要有所变化,需要一个默认值大小200*200。
于是有了
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int speSize = MeasureSpec.getSize(heightMeasureSpec); int speMode = MeasureSpec.getMode(heightMeasureSpec); Log.d("MyView", "---speSize = " + speSize + ""); Log.d("MyView", "---speMode = " + speMode + ""); if(speMode == MeasureSpec.AT_MOST){ Log.d("MyView", "---AT_MOST---"); } if(speMode == MeasureSpec.EXACTLY){ Log.d("MyView", "---EXACTLY---"); } if(speMode == MeasureSpec.UNSPECIFIED){ Log.d("MyView", "---UNSPECIFIED---"); } if(speMode==MeasureSpec.AT_MOST){ setMeasuredDimension(100, 100); }else{ setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } }
再看效果图:
所以由此类推,我们之所以重写onMeasure也就是为了wrap_content时能自动按照需求改变。回到原本的话题的第二块:getWidth()方法:
View#getWidth()
源码如下
@ViewDebug.ExportedProperty(category = "layout") public final int getWidth() { return mRight - mLeft; }
这里的mRight和mLeft到底是什么呢?其实它是layout过程传过来的四个参数中的两个:
public void layout(int l, int t, int r, int b) { if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_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 &= ~PFLAG_FORCE_LAYOUT; mPrivateFlags3 |= PFLAG3_IS_LAID_OUT; }有两种测量setOpticalFrame、setFrame,最终都会在其中调用了setFrame方法,它的源码如下:
protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true; //.... mLeft = left; mTop = top; mRight = right; mBottom = bottom; //...... } return changed; }这样就有了mLeft和mRight两个值了。
当然,光知道来处还的会用。我们平时自定义view继承自view时是不会对onlayout方法重写的。只有当重写布局viewGroup时才会对onlayout重写。
什么时候会遇到getWidth()和getMeasureWidth()不一致?
当继承布局viewGroup时,重写onlayout方法。对子view的 childView.layout(0,0,200,200);
我们平时重写onlayout()方法主要是为了对子布局自定义,比如瀑布流,比如放不下换行显示子view这种操作。
举个栗子:
当继承布局viewGroup时,重写onlayout方法,码如下:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int mViewGroupWidth = getMeasuredWidth(); //当前ViewGroup的总宽度 int mPainterPosX = l; //当前绘图光标横坐标位置 int mPainterPosY = t; //当前绘图光标纵坐标位置 int childCount = getChildCount(); for ( int i = 0; i < childCount; i++ ) { View childView = getChildAt(i); int width = childView.getMeasuredWidth(); int height = childView.getMeasuredHeight(); //如果剩余的空间不够,则移到下一行开始位置 if( mPainterPosX + width > mViewGroupWidth ) { mPainterPosX = l; mPainterPosY += height; } //执行ChildView的绘制// childView.layout(mPainterPosX,mPainterPosY,mPainterPosX+width, mPainterPosY+height); childView.layout(0,0,100, 200); //记录当前已经绘制到的横坐标位置 mPainterPosX += width; } }这里对子view布局使用固定值childView.layout(0,0,100, 200);
看下布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.example.user.mytestview.MyViewGroup android:layout_width="match_parent" android:layout_height="match_parent"> <com.example.user.mytestview.MyView android:layout_width="match_parent" android:layout_height="match_parent" /> </com.example.user.mytestview.MyViewGroup></RelativeLayout>在子view种打个log:发现
现在子 view的getWidth和getMeasuredWidth不一样了。
什么时候用getWidth?什么时候用getMeasureWidth()?
由view绘制流程我们知道:顺序是:onMeasure()--》onLayout()--》onDraw();(见源码ViewRootImpl#performTraversals() 方法,下一篇打算讲这个内容)
所以再onMeasure之后可以getMeasuredWidth,在Onlayout()之后 可以用getWitdth().
- 浅谈getWidth()和getMeasureWidth()区别
- getWidth和getMeasureWidth区别
- [例证]浅谈getWidth()和getMeasureWidth()区别
- 关于getRawX和getX、getMeasureWidth和getWidth之间的区别
- getWidth()与getMeasureWidth()的区别
- Android中getWidth()和getMeasureWidth()的区别探究
- android中getMeasureWidth()和getWidth()方法的区别
- Android自定义控件中getWidth()和getMeasureWidth()的区别
- Android自定义控件中getWidth()和getMeasureWidth()的区别
- getWidth与getMeasureWidth
- getWidth() 和 getMeasuredWidth() 区别
- onLayout笔记(mTop,layout,setFrame,getMeasureWidth,getWidth)
- Android getWidth和getMeasuredWidth 区别
- getWidth() 和 getMeasuredWidth()的区别
- Android getWidth和getMeasuredWidth 区别
- getWidth和getMeausuredWidth的区别
- view getwidth() 和getMeasuredWidth()区别
- getwidth和getmeasuredwidth的区别
- OpenSceneGraph实现的NeHe OpenGL教程 - 第四十七课
- poj1321棋盘问题
- git add to index 无效
- hdu1179 Ollivanders: Makers of Fine Wands since 382 BC(二分图模板)
- H264学习(一)之前几种编码介绍
- 浅谈getWidth()和getMeasureWidth()区别
- IOC容器注入依赖
- HashMap的工作原理-hashcode和equals原理的再次深入
- $.ajax()方法详解
- highcharts小技巧
- WebApp <meta>函数
- 对自己别手软
- 4-7 在一个数组中实现两个堆栈 (20分)
- Struts框架面试题