android View绘制源码分析(下)
来源:互联网 发布:淘宝刷单群 编辑:程序博客网 时间:2024/05/21 19:37
Layout的过程
viewGroup会遍历所有子元素并调用 其layout方法,layout方法来确定子元素的位置。viewgroup如下:
protected abstract void onLayout(boolean changed, int l , int t, int r, int b) ;
需要子类自己实现。看下view的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;}
在setFrame中确定了view的四个顶点坐标。mleft等。onLayout view也没有具体实现,要看具体的。以LinearLayout为例:
protected void onLayout(boolean changed, int l , int t, int r, int b) { if (mOrientation == VERTICAL) { layoutVertical(l, t, r , b); } else { layoutHorizontal(l, t, r , b); }}
以layoutVertical为例:
void layoutVertical(int left, int top , int right, int bottom) { final int paddingLeft = mPaddingLeft ; int childTop ; int childLeft ; // Where right end of child should go final int width = right - left; int childRight = width - mPaddingRight ; // Space available for child int childSpace = width - paddingLeft - mPaddingRight ; final int count = getVirtualChildCount() ; final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK; switch (majorGravity) { case Gravity.BOTTOM: // mTotalLength contains the padding already childTop = mPaddingTop + bottom - top - mTotalLength; break; // mTotalLength contains the padding already case Gravity.CENTER_VERTICAL: childTop = mPaddingTop + (bottom - top - mTotalLength) / 2; break; case Gravity. TOP: default : childTop = mPaddingTop; break; } for (int i = 0; i < count ; i++) { final View child = getVirtualChildAt(i) ; if (child == null) { childTop += measureNullChild(i); } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth() ; final int childHeight = child.getMeasuredHeight(); final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams() ; int gravity = lp. gravity; if (gravity < 0) { gravity = minorGravity; } final int layoutDirection = getLayoutDirection() ; final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection) ; switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity. CENTER_HORIZONTAL : childLeft = paddingLeft + ((childSpace - childWidth) / 2) + lp. leftMargin - lp.rightMargin ; break; case Gravity.RIGHT: childLeft = childRight - childWidth - lp. rightMargin; break; case Gravity.LEFT: default: childLeft = paddingLeft + lp. leftMargin; break; } if (hasDividerBeforeChildAt(i)) { childTop += mDividerHeight; } childTop += lp.topMargin; setChildFrame(child , childLeft, childTop + getLocationOffset(child) , childWidth, childHeight); childTop += childHeight + lp. bottomMargin + getNextLocationOffset(child); i += getChildrenSkipCount(child , i); } }}
主要看以下代码:
final int childWidth = child.getMeasuredWidth() ;final int childHeight = child.getMeasuredHeight() ;setChildFrame(child , childLeft, childTop + getLocationOffset(child) , childWidth , childHeight);childTop += childHeight + lp. bottomMargin + getNextLocationOffset(child);
top会逐渐增大,所以会往下排。setChildFrame仅仅是调用子元素的layout方法。
private void setChildFrame(View child, int left, int top , int width, int height) { child.layout(left, top, left + width , top + height);}
通过子元素的layout来确定自身。
draw过程
它有以下几步:
绘制背景,(canvas)
绘制自己。(onDraw)
绘制children(dispatchDraw)
绘制装饰(onDrawScrollBars)
看下view的draw源码:
public void draw(Canvas canvas) { final int privateFlags = mPrivateFlags; final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK ) == PFLAG_DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo .mIgnoreDirtyState ); mPrivateFlags = (privateFlags & ~ PFLAG_DIRTY_MASK ) | PFLAG_DRAWN; /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */ // Step 1, draw the background, if needed int saveCount; if (!dirtyOpaque) { drawBackground(canvas); } // skip step 2 & 5 if possible (common case) final int viewFlags = mViewFlags; boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL ) != 0; boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL ) != 0; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas) ; // Step 4, draw the children dispatchDraw(canvas) ; // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas) ; if ( mOverlay != null && !mOverlay.isEmpty()) { mOverlay .getOverlayView().dispatchDraw(canvas) ; } // we're done... return; }
viewgroup中的dispatchDraw用于遍历子view并调用子view的draw方法。这样就一层层的传下去。到此源码分析就结束了。在绘制view的时候经常会在activity中获得view的宽高,因为activity的生命周期和view不同步,在oncreate中无法获取到view的宽高,接下来讲讲activity中如何获取view。
三、view宽高确定
onWindowFocusChanged:view己经初始化完毕,宽高己经准备好。当Activity得到焦点和失去焦点均会被调用,所以它会调用多次。
通过view.post,将一个runnable投弟到消息队列尾部,等待looper调用时,view己经初始化好。
protected void onStart(){super.onStart();view.post(new Runnable(){public void run(){int width = view.getMeasuredWidth();int height = new .getMeasuredHeight();}})}
ViewTreeObserver:
使用ViewTreeObserver众多回调接口来完成,如OnGlobalLayoutListener,当view树状态发生改变时或内部view可见性发生改变时会回调。
ViewObserver obserber = view.getViewObserver ();obserber.addOnGlobalLayoutListener(new OnGlobalLayoutListener(){public void onGlobalLayout(){obserber.removeOnGlobalLayoutListener(this);int width = view.getMeasuredWidth();int height = new .getMeasuredHeight();}})
通过view进行measure来得到view的宽高。
int width = MeasureSpec.makeMeasureSpec(100,Measure.EXACTLY);//确定值int height= MeasureSpec.makeMeasureSpec(100,Measure.EXACTLY);//确定值view.measure(width,height);对于wrap_content:int width = MeasureSpec.makeMeasureSpec((1<<30)-1,Measure.AT_MOST);//wrap_contentint height= MeasureSpec.makeMeasureSpec((1<<30)-1,Measure.AT_MOST);//wrap_contentview.measure(width,height);
四、自定义view中注意事项
自定义View需要注意的事项:
如果是继承view或者viewGroup,让view支持wrap_content。
如果有必要,让view支持padding。
View中如果有动画或者线程,要在onDetachedFromWindow中及时停止。当view的Activity退出或者当前view被remove时,调用它。
- android View绘制源码分析(下)
- android View绘制源码分析
- android view绘制源码分析
- Android View绘制流程(结合源码分析)上
- 从Android源码分析View绘制
- android View绘制源码分析(上)
- Android view绘制源码分析总结
- Android 中View的绘制机制源码分析 三
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android应用层View绘制流程与源码分析
- Android 中View的绘制机制源码分析 一
- Android 中View的绘制机制源码分析 二
- Android 中View的绘制机制源码分析 四
- Android应用层View绘制流程与源码分析
- android View绘制源码分析(上)
- Java中常用的设计模式介绍
- Java动态代理
- QT之GUI学习笔记( 二十三)---绘图设备
- Leetcode-537. Complex Number Multiplication
- android View绘制源码分析(下)
- jsonp的原理,应用场景,优缺点
- Stirng、StringBuffer、StringBuilder的区别浅谈
- C++标准库类型string用法小结
- 机器学习与数据挖掘之决策树
- 动态代理
- c++实现双向链表
- 从此再有不愁自定义View——Android自定义view详解
- 长沙麓山风湿医院