博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
简单研究Android View绘制三 布局过程
阅读量:5045 次
发布时间:2019-06-12

本文共 28971 字,大约阅读时间需要 96 分钟。

2015-07-28 17:29:19

这一篇主要看看布局过程

一、布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下:

1     /** 2      * Assign a size and position to a view and all of its 3      * descendants 4      * 5      * 

This is the second phase of the layout mechanism. 6 * (The first is measuring). In this phase, each parent calls 7 * layout on all of its children to position them. 8 * This is typically done using the child measurements 9 * that were stored in the measure pass().

10 *11 *

Derived classes should not override this method.12 * Derived classes with children should override13 * onLayout. In that method, they should14 * call layout on each of their children.

15 *16 * @param l Left position, relative to parent17 * @param t Top position, relative to parent18 * @param r Right position, relative to parent19 * @param b Bottom position, relative to parent20 */21 @SuppressWarnings({"unchecked"})22 public void layout(int l, int t, int r, int b) {23 if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {24 onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);25 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;26 }27 28 int oldL = mLeft;29 int oldT = mTop;30 int oldB = mBottom;31 int oldR = mRight;32 33 boolean changed = isLayoutModeOptical(mParent) ?34 setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);35 if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {36 /// M: Monitor onLayout time if longer than 3s print log.37 long logTime = System.currentTimeMillis();38 onLayout(changed, l, t, r, b);39 long nowTime = System.currentTimeMillis();40 if (nowTime - logTime > DBG_TIMEOUT_VALUE) {41 Xlog.d(VIEW_LOG_TAG, "[ANR Warning]onLayout time too long, this =" + this + "time =" + (nowTime - logTime));42 }43 mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;44 45 ListenerInfo li = mListenerInfo;46 if (li != null && li.mOnLayoutChangeListeners != null) {47 ArrayList
listenersCopy =48 (ArrayList
)li.mOnLayoutChangeListeners.clone();49 int numListeners = listenersCopy.size();50 for (int i = 0; i < numListeners; ++i) {51 listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);52 }53 }54 }55 56 mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;57 mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;58 }59 60 /**61 * Called from layout when this view should62 * assign a size and position to each of its children.63 *64 * Derived classes with children should override65 * this method and call layout on each of66 * their children.67 * @param changed This is a new size or position for this view68 * @param left Left position, relative to parent69 * @param top Top position, relative to parent70 * @param right Right position, relative to parent71 * @param bottom Bottom position, relative to parent72 */73 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {74 }

简单的翻译一下layout()方法的那段注释哈~E文不好~

“指定一个view以及它的所有子孙节点的大小和位置,这是布局机制的第二阶段(第一阶段是测量),在这一阶段,父view调用所有子view的layout()方法以确定他们所在的位置,通常是使用子View存储的自身的尺寸。派生类不应该重写此方法,应该重写onLayout()方法,在派生类重写的onLayout()方法中,应该调用每一个子View的layout方法。”啰嗦一句,int l, int t, int r, int b都是相对于父节点的坐标值。

注意layout方法中的红色代码,调用了onLayout。而onLayout在view中实现为空。现在来看看ViewGroup中的这两个方法。

1     /** 2      * {
@inheritDoc} 3 */ 4 @Override 5 public final void layout(int l, int t, int r, int b) { 6 if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) { 7 if (mTransition != null) { 8 mTransition.layoutChange(this); 9 }10 super.layout(l, t, r, b);11 } else {12 // record the fact that we noop'd it; request layout when transition finishes13 mLayoutCalledWhileSuppressed = true;14 }15 }16 17 /**18 * {
@inheritDoc}19 */20 @Override21 protected abstract void onLayout(boolean changed,22 int l, int t, int r, int b);

在ViewGroup的layout方法中,mSuppressLayout用来控制是否禁止调用layout(),该值由如下方法来控制:

1     /** 2      * Tells this ViewGroup to suppress all layout() calls until layout 3      * suppression is disabled with a later call to suppressLayout(false). 4      * When layout suppression is disabled, a requestLayout() call is sent 5      * if layout() was attempted while layout was being suppressed. 6      * 7      * @hide 8      */ 9     public void suppressLayout(boolean suppress) {10         mSuppressLayout = suppress;11         if (!suppress) {12             if (mLayoutCalledWhileSuppressed) {13                 requestLayout();14                 mLayoutCalledWhileSuppressed = false;15             }16         }17     }

这个方法不是对外公开的,所以不了解它也行。可以简单地理解ViewGroup的layout方法,它直接调用了父类View的layout()方法即可。至于onLayout方法,竟然被搞成了abstract的,这是逼着ViewGroup的子类必须得去实现啊~当然了,你必须得实现啊,你作为一个容器类,如何摆放你的子孙控件,是你义不容辞的责任啊。

至此我们已经明白了几点:

1. 派生类不需要重写layout(),而应该重写onLayout()方法,因为在layout()方法中就调用了onLayout()。

2. 在重写onLayout()方法时,我们需要显式的调用每一个childView的layout方法,把它摆放在合适的位置上。前提是在调用之前,得先计算好该childView的坐标。

3. 如果直接继承自View,那么可以不用重写onLayout()方法,比如ImageView、ImageButton等都没有重写该方法,所以不重写这个方法对于自定义View影响不大,至于TextView比较特殊,它重写了该方法,如下:

1     @Override  2     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  3         super.onLayout(changed, left, top, right, bottom);  4         if (mDeferScroll >= 0) {  5             int curs = mDeferScroll;  6             mDeferScroll = -1;  7             bringPointIntoView(Math.min(curs, mText.length()));  8         }  9     } 10  11     /** 12      * Move the point, specified by the offset, into the view if it is needed. 13      * This has to be called after layout. Returns true if anything changed. 14      */ 15     public boolean bringPointIntoView(int offset) { 16         if (isLayoutRequested()) { 17             mDeferScroll = offset; 18             return false; 19         } 20         boolean changed = false; 21  22         Layout layout = isShowingHint() ? mHintLayout: mLayout; 23  24         if (layout == null) return changed; 25  26         int line = layout.getLineForOffset(offset); 27  28         int grav; 29  30         switch (layout.getParagraphAlignment(line)) { 31             case ALIGN_LEFT: 32                 grav = 1; 33                 break; 34             case ALIGN_RIGHT: 35                 grav = -1; 36                 break; 37             case ALIGN_NORMAL: 38                 grav = layout.getParagraphDirection(line); 39                 break; 40             case ALIGN_OPPOSITE: 41                 grav = -layout.getParagraphDirection(line); 42                 break; 43             case ALIGN_CENTER: 44             default: 45                 grav = 0; 46                 break; 47         } 48  49         // We only want to clamp the cursor to fit within the layout width 50         // in left-to-right modes, because in a right to left alignment, 51         // we want to scroll to keep the line-right on the screen, as other 52         // lines are likely to have text flush with the right margin, which 53         // we want to keep visible. 54         // A better long-term solution would probably be to measure both 55         // the full line and a blank-trimmed version, and, for example, use 56         // the latter measurement for centering and right alignment, but for 57         // the time being we only implement the cursor clamping in left to 58         // right where it is most likely to be annoying. 59         final boolean clamped = grav > 0; 60         // FIXME: Is it okay to truncate this, or should we round? 61         final int x = (int)layout.getPrimaryHorizontal(offset, clamped); 62         final int top = layout.getLineTop(line); 63         final int bottom = layout.getLineTop(line + 1); 64  65         int left = (int) FloatMath.floor(layout.getLineLeft(line)); 66         int right = (int) FloatMath.ceil(layout.getLineRight(line)); 67         int ht = layout.getHeight(); 68  69         int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(); 70         int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom(); 71         if (!mHorizontallyScrolling && right - left > hspace && right > x) { 72             // If cursor has been clamped, make sure we don't scroll. 73             right = Math.max(x, left + hspace); 74         } 75  76         int hslack = (bottom - top) / 2; 77         int vslack = hslack; 78  79         if (vslack > vspace / 4) 80             vslack = vspace / 4; 81         if (hslack > hspace / 4) 82             hslack = hspace / 4; 83  84         int hs = mScrollX; 85         int vs = mScrollY; 86  87         if (top - vs < vslack) 88             vs = top - vslack; 89         if (bottom - vs > vspace - vslack) 90             vs = bottom - (vspace - vslack); 91         if (ht - vs < vspace) 92             vs = ht - vspace; 93         if (0 - vs > 0) 94             vs = 0; 95  96         if (grav != 0) { 97             if (x - hs < hslack) { 98                 hs = x - hslack; 99             }100             if (x - hs > hspace - hslack) {101                 hs = x - (hspace - hslack);102             }103         }104 105         if (grav < 0) {106             if (left - hs > 0)107                 hs = left;108             if (right - hs < hspace)109                 hs = right - hspace;110         } else if (grav > 0) {111             if (right - hs < hspace)112                 hs = right - hspace;113             if (left - hs > 0)114                 hs = left;115         } else /* grav == 0 */ {116             if (right - left <= hspace) {117                 /*118                  * If the entire text fits, center it exactly.119                  */120                 hs = left - (hspace - (right - left)) / 2;121             } else if (x > right - hslack) {122                 /*123                  * If we are near the right edge, keep the right edge124                  * at the edge of the view.125                  */126                 hs = right - hspace;127             } else if (x < left + hslack) {128                 /*129                  * If we are near the left edge, keep the left edge130                  * at the edge of the view.131                  */132                 hs = left;133             } else if (left > hs) {134                 /*135                  * Is there whitespace visible at the left?  Fix it if so.136                  */137                 hs = left;138             } else if (right < hs + hspace) {139                 /*140                  * Is there whitespace visible at the right?  Fix it if so.141                  */142                 hs = right - hspace;143             } else {144                 /*145                  * Otherwise, float as needed.146                  */147                 if (x - hs < hslack) {148                     hs = x - hslack;149                 }150                 if (x - hs > hspace - hslack) {151                     hs = x - (hspace - hslack);152                 }153             }154         }155 156         if (hs != mScrollX || vs != mScrollY) {157             if (mScroller == null) {158                 scrollTo(hs, vs);159             } else {160                 long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;161                 int dx = hs - mScrollX;162                 int dy = vs - mScrollY;163 164                 if (duration > ANIMATED_SCROLL_GAP) {165                     mScroller.startScroll(mScrollX, mScrollY, dx, dy);166                     awakenScrollBars(mScroller.getDuration());167                     invalidate();168                 } else {169                     if (!mScroller.isFinished()) {170                         mScroller.abortAnimation();171                     }172 173                     scrollBy(dx, dy);174                 }175 176                 mLastScroll = AnimationUtils.currentAnimationTimeMillis();177             }178 179             changed = true;180         }181 182         if (isFocused()) {183             // This offsets because getInterestingRect() is in terms of viewport coordinates, but184             // requestRectangleOnScreen() is in terms of content coordinates.185 186             // The offsets here are to ensure the rectangle we are using is187             // within our view bounds, in case the cursor is on the far left188             // or right.  If it isn't withing the bounds, then this request189             // will be ignored.190             if (mTempRect == null) mTempRect = new Rect();191             mTempRect.set(x - 2, top, x + 2, bottom);192             getInterestingRect(mTempRect, line);193             ///M: ALPS00605613 requestRectangleOnScreen() will return error result if setting the mTempRect to mScrollX, mScrollY194             //mTempRect.offset(mScrollX, mScrollY);195 196             if (requestRectangleOnScreen(mTempRect)) {197                 changed = true;198             }199         }200 201         return changed;202     }
View Code

有点没看明白bringPointIntoView方法的作用是什么,还得再研究。不过这不影响我们分析布局过程。

二、结合自己写的一个小Demo继续分析,代码如下:

MyLinear2.java

1 public class MyLinear2 extends ViewGroup {  2     private static final String TAG = "David_MyLinear2";  3   4     public MyLinear2(Context context) {  5         super(context);  6     }  7   8     public MyLinear2(Context context, AttributeSet attrs) {  9         super(context, attrs); 10     } 11  12     @Override 13     protected void onLayout(boolean changed, int l, int t, int r, int b) { 14         int count = getChildCount(); 15         int cWidth = 0; 16         int cHeight = 0; 17         int top = 0; 18         MarginLayoutParams params = null; 19         Log.e(TAG + " onLayout", "l = " + l); 20         Log.e(TAG + " onLayout", "t = " + t); 21         Log.e(TAG + " onLayout", "r = " + r); 22         Log.e(TAG + " onLayout", "b = " + b); 23  24         for (int i = 0; i < count; i++) { 25             Log.e(TAG + " onLayout", "====================i = " + i); 26             View childView = getChildAt(i); 27             cWidth = childView.getMeasuredWidth(); 28             cHeight = childView.getMeasuredHeight(); 29             params = (MarginLayoutParams) childView.getLayoutParams(); 30  31             Log.e(TAG + " onLayout", "params.height = " + params.height); 32             Log.e(TAG + " onLayout", "params.width = " + params.width); 33             int leftMargin = params.leftMargin; 34             Log.e(TAG + " onLayout", "leftMargin = " + leftMargin); 35             Log.e(TAG + " onLayout", "params.rightMargin = " + params.rightMargin); 36             /*Log.e(TAG + " onLayout", "cWidth = " + cWidth); 37             Log.e(TAG + " onLayout", "cHeight = " + cHeight); 38             Log.e(TAG + " onLayout", "getWidth() = " + childView.getWidth()); 39             Log.e(TAG + " onLayout", "getHeight() = " + childView.getHeight());*/ 40             int cl = 0, ct = 0, cr = 0, cb = 0; 41             cl = leftMargin; 42             cr = cl + cWidth; 43             ct = top; 44             cb = cHeight + ct; 45             /*Log.e(TAG + " onLayout", "cr = " + cr); 46             Log.e(TAG + " onLayout", "ct = " + ct); 47             Log.e(TAG + " onLayout", "cb = " + cb); 48             Log.e(TAG + " onLayout", "top = " + top);*/ 49             childView.layout(cl, ct, cr, cb); 50             top += cHeight; 51         } 52     } 53  54     @Override 55     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 56         Log.e(TAG, "generateLayoutParams attrs"); 57         return new MarginLayoutParams(getContext(), attrs); 58     } 59  60     @Override 61     protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 62         Log.e(TAG, "generateDefaultLayoutParams"); 63         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 64     } 65  66     @Override 67     protected boolean checkLayoutParams(LayoutParams p) { 68         return super.checkLayoutParams(p); 69     } 70  71     @Override 72     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 73         Log.e(TAG, "generateLayoutParams p"); 74         return new MarginLayoutParams(p); 75     } 76  77     @Override 78     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 79         int measuredHeight = measureHeight(heightMeasureSpec); 80         int measuredWidth = measureWidth(widthMeasureSpec); 81         Log.e(TAG, "onMeasure measuredHeight = " + measuredHeight); 82         Log.e(TAG, "onMeasure measuredWidth = " + measuredWidth); 83         setMeasuredDimension(measuredWidth, measuredHeight); 84         measureChildren(widthMeasureSpec, heightMeasureSpec); 85     } 86  87     private int measureHeight(int measureSpec) { 88         int specMode = MeasureSpec.getMode(measureSpec); 89         int specSize = MeasureSpec.getSize(measureSpec); 90  91         int result = 500; 92         if (specMode == MeasureSpec.AT_MOST){ 93             result = specSize; 94         } else if (specMode == MeasureSpec.EXACTLY){ 95             result = specSize; 96         } 97         return result; 98     } 99 100     private int measureWidth(int measureSpec) {101         int specMode = MeasureSpec.getMode(measureSpec);102         int specSize = MeasureSpec.getSize(measureSpec);103 104         int result = 100;105         if (specMode == MeasureSpec.AT_MOST){106             result = specSize;107         } else if (specMode == MeasureSpec.EXACTLY){108             result = specSize;109         }110         return result;111     }112 }

MyLinear2时模仿纵向布局的LinearLayout。

MyTextView.java

1 public class MyTextView extends View { 2     private static final String TAG = "David___MyTextView"; 3  4     public MyTextView(Context context, AttributeSet attrs) { 5         super(context, attrs); 6     } 7  8     public MyTextView(Context context) { 9         super(context);10     }11 12     @Override13     protected void onDraw(Canvas canvas) {14         super.onDraw(canvas);15         Paint paint = new Paint();16         paint.setTextSize(22);17         paint.setTextAlign(Align.CENTER);18         Log.e(TAG, "onDraw getTop() = " + getTop());19         Log.e(TAG, "onDraw getLeft() = " + getLeft());20         canvas.drawText("nihao ----", getTop(), getLeft(), paint);21     }22 23     @Override24     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {25         Log.e(TAG, "onLayout left = " + left);26         Log.e(TAG, "onLayout top = " + top);27         Log.e(TAG, "onLayout right = " + right);28         Log.e(TAG, "onLayout bottom = " + bottom);29         //super.onLayout(changed, left, top, right, bottom);30     }31 32     @Override33     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {34         int measuredHeight = measureHeight(heightMeasureSpec);35         int measuredWidth = measureWidth(widthMeasureSpec);36         Log.e(TAG, "onMeasure measuredHeight = " + measuredHeight);37         Log.e(TAG, "onMeasure measuredWidth = " + measuredWidth);38         setMeasuredDimension(measuredWidth, measuredHeight);39     }40 41     private int measureHeight(int measureSpec) {42         int specMode = MeasureSpec.getMode(measureSpec);43         int specSize = MeasureSpec.getSize(measureSpec);44 45         int result = 500;46         if (specMode == MeasureSpec.AT_MOST){47             result = specSize;48         } else if (specMode == MeasureSpec.EXACTLY){49             result = specSize;50         }51         return result;52     }53 54     private int measureWidth(int measureSpec) {55         int specMode = MeasureSpec.getMode(measureSpec);56         int specSize = MeasureSpec.getSize(measureSpec);57 58         int result = 500;59         if (specMode == MeasureSpec.AT_MOST){60             result = specSize;61         } else if (specMode == MeasureSpec.EXACTLY){62             result = specSize;63         }64         return result;65     }66 }

这是一个简易的View,先不要关心onDraw()方法。布局文件如下:

1 
6 7
14 15
23 24 25
30 31

OK,运行打印的日志如下:

这段日志其实是会被打印好几遍的,我只截取了完整的一次日志。从日志可以看出,首先执行的是测量过程,这个之前分析过了,请参考http://www.cnblogs.com/wlrhnh/p/4680636.html。然后才是布局过程,由于我们在MyLinear2的onLayout()中显式调用childView的layout()方法,代码如下:

1     @Override 2     protected void onLayout(boolean changed, int l, int t, int r, int b) { 3         int count = getChildCount(); 4         int cWidth = 0; 5         int cHeight = 0; 6         int top = 0; 7         MarginLayoutParams params = null; 8         Log.e(TAG + " onLayout", "l = " + l); 9         Log.e(TAG + " onLayout", "t = " + t);10         Log.e(TAG + " onLayout", "r = " + r);11         Log.e(TAG + " onLayout", "b = " + b);12 13         for (int i = 0; i < count; i++) {14             Log.e(TAG + " onLayout", "====================i = " + i);15             View childView = getChildAt(i);16             cWidth = childView.getMeasuredWidth();17             cHeight = childView.getMeasuredHeight();18             params = (MarginLayoutParams) childView.getLayoutParams();19 20             Log.e(TAG + " onLayout", "params.height = " + params.height);21             Log.e(TAG + " onLayout", "params.width = " + params.width);22             int leftMargin = params.leftMargin;23             Log.e(TAG + " onLayout", "leftMargin = " + leftMargin);24             Log.e(TAG + " onLayout", "params.rightMargin = " + params.rightMargin);25             int cl = 0, ct = 0, cr = 0, cb = 0;26             cl = leftMargin;27             cr = cl + cWidth;28             ct = top;29             cb = cHeight + ct;30             childView.layout(cl, ct, cr, cb);31             top += cHeight;32         }33     }

所以日志中显示:先调用了ViewGroup的onLayout,然后遍历每一个childView,取出它们已经计算好的坐标值,按照ViewGroup的既定布局策略,给childView布局,调用他们的layout()方法,进而调用了onLayout()。而很多继承自View的组件,比如ImageView等,由于没有实现onLayout()方法,那么其实只调用了View的layout()和View的空实现的onLayout()方法。当然了,我在MyTextView中重写onLayout()只是为了打印日志而已,并没有做什么实际操作。在日志的最后,打印了onDraw()方法,可见,先测量、后布局、最后才统一draw,并不是布局完一个就draw一个。而且我发现一个有意思的现象,那就是如果你的父控件拥有的space不足以显示所有的子View,那么不能显示出来的子View的onDraw方法是不会被调用的,这点好理解~

三、关于LayoutParams

注意上面的一行红色代码,每个childView都有LayoutParams,而且可以强转为MarginLayoutParams呢?注意看这几行代码:

1     @Override 2     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 3         Log.e(TAG, "generateLayoutParams attrs"); 4         return new MarginLayoutParams(getContext(), attrs); 5     } 6  7     @Override 8     protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 9         Log.e(TAG, "generateDefaultLayoutParams");10         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);11     }12 13     @Override14     protected boolean checkLayoutParams(LayoutParams p) {15         return super.checkLayoutParams(p);16     }17 18     @Override19     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {20         Log.e(TAG, "generateLayoutParams p");21         return new MarginLayoutParams(p);22     }

看一下日志:

看黄色部分日志,主要执行了generateLayoutParams(AttributeSet attrs)方法,而且是先于测量过程执行的,这个方法我们有自己的实现,具体的请参考http://www.cnblogs.com/wlrhnh/p/4683542.html

简单的总结一下布局过程:

Android View绘制系统首先取到Activity布局的根View,当然这一般是一个ViewGroup了。先测量,后布局。布局的时候调用ViewGroup子类比如MyLinear2的layout()方法,由于ViewGroup强制子类实现onLayout()方法,所以会调到MyLinear2的onLayout()方法,在这个方法中,需要遍历子View,按照布局策略,计算每一个子View的坐标,然后将它放在合适的位置上。至于继承自View的子类,则不需要实现onLayout,毕竟onLayout的作是布局,这是容器类该干的事情。而子View要做的就是根据父View在指定给自己的空间中draw。

通过以上三篇文章,想必应该对Android系统测量、布局View的流程有了一个大概的了解,这是一条主线。当然了,这里面涉及到的细节其实有很多,最好的办法是自己去看源码喽~

项目代码和View.java ViewGroup.java LinearLayout.java Button.java等源码请下载

后记:分析完三步骤之后觉得不过瘾,想起网上有人写过这样的布局,就顺手写了个,抄袭人家的创意了,但是代码是自己的写的~

 

1 public class MyLinear1 extends ViewGroup {  2     private static final String TAG = "David_MyLinear1";  3   4     public MyLinear1(Context context) {  5         super(context);  6     }  7   8     public MyLinear1(Context context, AttributeSet attrs) {  9         super(context, attrs); 10     } 11  12     @Override 13     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 14         int measuredHeightSize = MeasureSpec.getSize(heightMeasureSpec); 15         int measuredWidthSize = MeasureSpec.getSize(widthMeasureSpec); 16  17         int measuredHeightMode = MeasureSpec.getMode(heightMeasureSpec); 18         int measuredWidthMode = MeasureSpec.getMode(widthMeasureSpec); 19  20         measureChildren(widthMeasureSpec, heightMeasureSpec); 21  22         int lHeight = 0, rHeight = 0; 23         int tWidth = 0, bWidth = 0; 24  25         View childView = null; 26         MarginLayoutParams params = null; 27         for (int i = 0; i < getChildCount(); i++) { 28             childView = getChildAt(i); 29             params = (MarginLayoutParams) childView.getLayoutParams(); 30  31             switch (i) { 32                 case 0: 33                     lHeight += childView.getMeasuredHeight(); 34                     tWidth += childView.getMeasuredWidth(); 35                     break; 36                 case 1: 37                     rHeight += childView.getMeasuredHeight(); 38                     tWidth += childView.getMeasuredWidth(); 39                     break; 40                 case 2: 41                     lHeight += childView.getMeasuredHeight(); 42                     bWidth += childView.getMeasuredWidth(); 43                     break; 44                 case 3: 45                     rHeight += childView.getMeasuredHeight(); 46                     bWidth += childView.getMeasuredWidth(); 47                     break; 48  49                 default: 50                     break; 51             } 52         } 53  54         int realHeightSize = Math.max(lHeight, rHeight); 55         int realWidthSize = Math.max(bWidth, tWidth); 56         int h = measuredHeightMode == MeasureSpec.EXACTLY ? measuredHeightSize : realHeightSize; 57         int w = measuredWidthMode == MeasureSpec.EXACTLY ? measuredWidthSize : realWidthSize; 58         setMeasuredDimension(w, h); 59     } 60  61     @Override 62     protected void onLayout(boolean changed, int l, int t, int r, int b) { 63         int realLeft = 0; 64         int realTop = 0; 65         int realRight = 0; 66         int realBottom = 0; 67         int count = getChildCount(); 68  69         View childView = null; 70         MarginLayoutParams params = null; 71         for (int i = 0; i < count; i++) { 72             Log.e(TAG, "------------------------- i = " + i); 73             childView = getChildAt(i); 74             params = (MarginLayoutParams) childView.getLayoutParams(); 75             Log.d(TAG, "childView.getMeasuredWidth() = " + childView.getMeasuredWidth()); 76             Log.d(TAG, "childView.getMeasuredHeight() = " + childView.getMeasuredHeight()); 77             switch (i) { 78                 case 0: 79                     realLeft = l; 80                     realTop = t; 81                     realRight = realLeft + childView.getMeasuredWidth(); 82                     realBottom = realTop + childView.getMeasuredHeight(); 83                     break; 84                 case 1: 85                     realLeft = r - childView.getMeasuredWidth(); 86                     realTop = t; 87                     realRight = realLeft + childView.getMeasuredWidth(); 88                     realBottom = realTop + childView.getMeasuredHeight(); 89                     break; 90                 case 2: 91                     realLeft = l; 92                     realTop = b - childView.getMeasuredHeight(); 93                     realRight = realLeft + childView.getMeasuredWidth(); 94                     realBottom = realTop + childView.getMeasuredHeight(); 95                     break; 96                 case 3: 97                     realLeft = r - childView.getMeasuredWidth(); 98                     realTop = b - childView.getMeasuredHeight(); 99                     realRight = realLeft + childView.getMeasuredWidth();100                     realBottom = realTop + childView.getMeasuredHeight();101                     break;102 103                 default:104                     break;105             }106             childView.layout(realLeft, realTop, realRight, realBottom);107         }108     }109 110     @Override111     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {112         return new MarginLayoutParams(getContext(), attrs);113     }114 }

 

代码随手写的,有些问题考虑的还不够周全,各位自己完善吧~而且没有做注释,但是相信看完这三篇文章,不需要看注释了吧~

 

转载于:https://www.cnblogs.com/wlrhnh/p/4685812.html

你可能感兴趣的文章
上周热点回顾(10.20-10.26)
查看>>
C#正则表达式引发的CPU跑高问题以及解决方法
查看>>
云计算之路-阿里云上:“黑色30秒”走了,“黑色1秒”来了,真相也许大白了...
查看>>
APScheduler调度器
查看>>
设计模式——原型模式
查看>>
【jQuery UI 1.8 The User Interface Library for jQuery】.学习笔记.1.CSS框架和其他功能
查看>>
如何一个pdf文件拆分为若干个pdf文件
查看>>
web.xml中listener、 filter、servlet 加载顺序及其详解
查看>>
前端chrome浏览器调试总结
查看>>
获取手机验证码修改
查看>>
数据库连接
查看>>
python中数据的变量和字符串的常用使用方法
查看>>
等价类划分进阶篇
查看>>
delphi.指针.PChar
查看>>
Objective - C基础: 第四天 - 10.SEL类型的基本认识
查看>>
java 字符串转json,json转对象等等...
查看>>
极客前端部分题目收集【索引】
查看>>
第四天 selenium的安装及使用
查看>>
关于js的设计模式(简单工厂模式,构造函数模式,原型模式,混合模式,动态模式)...
查看>>
KMPnext数组循环节理解 HDU1358
查看>>