Android自定义一个view ViewRootImpl绘制流程示例

这篇文章主要为大家介绍了Android自定义一个view ViewRootImpl绘制流程示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

Android如何自定义一个view ViewRootImpl绘制流程

1、自定义属性

在res/values/attrs.xml文件里定义一个declare-styleable name:自定义view的名字

   //定义属性:名称 + 格式类型---颜色、尺寸、整形、字符串类型 

2、在res/layout/activity_main.xml文件里使用自定义view

  

3、构造CustomView名称的view,读取自定义属性

//通过AttributeSet直接获取

public CustomView(Context context, AttributeSet attrs) { super(context, attrs); int n = attrs.getAttributeCount(); //获得自定义view的属性数量 for (int i =0; i 

//getAttributeName来获取对应index处的属性名称,以sting返回。

//getAttributeValue来获取相应index处属性的值,以string返回。

//getAttributeIntValue来获取相应attribute属性的值

public String getAttributeValue(String namespace, String attribute);

public int getAttributeIntValue(String namespace, String attribute, int defaultValue);

namespace代表自定义属性的命名空间(与xml中的使用方法相同)

public class CustomView extends View { private Paint mPaint; private int mHeight; private int mWidth; private int custom_size; private float scale = 1f; private final int SIZE = 30; public CustomView(Context context) { this(context, null); } public CustomView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, R.style.AppTheme); //通过obtainStyledAttributes()方法获取属性值 int custon_background = a.getColor(R.styleable.CustomView_background, Color.BLUE); custom_size = a.getDimensionPixelSize(R.styleable.CustomView_size, 10); int AttrFirst = arr.getInt(R.styleable.CustomView_AttrFirst, 100); String AttrSecond = arr.getString(R.styleable.CustomView_AttrSecond); a.recycle(); mPaint = new Paint(); mPaint.setColor(custom_background); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //计算出实际的高(mMeasuredHeight)和宽(mMeasureWidth)传入setMeasuredDimension()方法完成单个View的测量 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int measuredHeight, measuredWidth; if (widthMode == MeasureSpec.EXACTLY) { measuredWidth = widthSize; } else { measuredWidth = SIZE; } if (heightMode == MeasureSpec.EXACTLY) { measuredHeight = heightSize; } else { measuredHeight = SIZE; } setMeasuredDimension(measuredWidth, measuredHeight); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { //onLayout决定具体View的大小和位置, 对当前视图和其所有子View设置它们在父视图中具体位置 super.onLayout(changed, left, top, right, bottom); mHeight = getHeight(); //getWidth,和getLeft等这些函数都是View相对于其父View的位置。而getMeasuredWidth,getMeasuredHeight是测量后该View的实际值 mWidth = getWidth(); } @Override protected void onDraw(Canvas canvas) { canvas.drawCircle(mWidth/2, mHeight/2, custom_size * scale, mPaint); } private ValueAnimator mAnimator; public void startAnimation() { mAnimator = ValueAnimator.ofFloat(1, 2); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { scale = (float) animation.getAnimatedValue(); postInvalidate(); } }); mAnimator.setRepeatCount(-1); //重复次数,-1表示无限循环 mAnimator.setRepeatMode(ValueAnimator.REVERSE); //重复模式:RESTART---重新开始,REVERSE---恢复初始状态再开始 mAnimator.start(); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mAnimator.end();  //关闭动画 } @Override protected Parcelable onSaveInstanceState() { return super.onSaveInstanceState(); } @Override protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); } }  

4、自定义view应用属性、刷新显示

Android里,一个view的绘制流程包括: Measure、Layout、Draw 测量——onMeasure():决定View的大小,确定view及它所有子节点需要的尺寸 布局——onLayout():决定View在ViewGroup中的位置, 当view需要为它的所有子节点指定大小和布局时,调用此方法 绘制——onDraw():绘制View。 自定义View中,onLayout配合onMeasure方法一起使用,可以实现自定义View的复杂布局。

自定义view,刷新view的方法: requestLayout()、invalidate()、postInvalidate(), 其实invalidate和postInvalidate这两个方法作用是一样的,唯一不同: invalidate用在主线程 postInvalidate用在异步线程,它最后会通过handler调用invalidate实现

requestLayout和invalidate的内部实现: requestLayout 会调用measure和layout 等一系列操作,然后根据布局是否发生改变,surface是否被销毁,来决定是否调用draw,也就是说requestlayout肯定会调用measure和layout, 但不一定调用draw,读者可以试着改下我上面写的那个小程序,将postInvalidate改成requestlayout,动画效果就消失了,因为布局没有发生改变。 invalidate 只会调用draw,而且肯定会调,即使什么都没有发生改变,它也会重新绘制。 所以如果有布局需要发生改变,需要调用requestlayout方法,如果只是刷新动画,则只需要调用invalidate方法

postInvalidate()方法程序调用流程,从View.java到ViewRootImpl.java,最终由performTraversals()实现 postInvalidate => postInvalidateDelayed => dispatchInvalidateDelayed => invalidate => scheduleTraversals() => doTraversal => performTraversals

requestLayout()方法程序调用流程 requestLayout() => scheduleTraversals() => doTraversal => performTraversals

performTraversals()方法调用:(真正实现view的measure、layout、draw) performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); performMeasure() => measure() => onMeasure() 调用该方法来确定view及它所有子节点需要的尺寸 performLayout(lp, desiredWindowWidth, desiredWindowHeight); performLayout() => layout() => onLayout() 当view需要为它的所有子节点指定大小和布局时,调用此方法 performDraw(); performDraw() => draw() => onDraw() => dispatchDraw() (drawSoftware())

onMeasure(int widthMeasureSpec, int heightMeasureSpec) 功能:通过测量知道一个view要占的大小, 参数:宽高测量规格,int型的值(java中int型由4个字节(32bit)组成),在MeasureSpce中,其前两位表示mode,后30位表示size MeasureSpce的mode有三种:EXACTLY、AT_MOST、UNSPECIFIED

1、当父布局是EXACTLY时, 子控件确定大小或者match_parent,mode都是EXACTLY, 子控件是wrap_content时,mode为AT_MOST;

2、当父布局是AT_MOST时, 子控件确定大小,mode为EXACTLY, 子控件wrap_content或者match_parent时,mode为AT_MOST。 所以在确定控件大小时,需要判断MeasureSpec的mode,不能直接用MeasureSpec的size

View.java:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource { ... public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); //ViewParent接口的requestLayout()方法 } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } } public final void measure(int widthMeasureSpec, int heightMeasureSpec) { //为整个View树计算实际的大小, 需要递归的去计算每一个子视图的大小 boolean optical = isLayoutModeOptical(this); if (optical != isLayoutModeOptical(mParent)) { Insets insets = getOpticalInsets(); int oWidth  = insets.left + insets.right; int oHeight = insets.top  + insets.bottom; widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth); heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight); } // Suppress sign extension for the low bytes long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL; if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2); final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT; final boolean isExactly   = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY && MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY; final boolean matchingSize = isExactly && getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec) && getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec); if (forceLayout || !matchingSize && (widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec)) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key); if (cacheIndex <0 || signoremeasurecache) {>> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension } //对于每个View的实际宽高都是由父视图和本身视图决定的 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //计算出实际的高(mMeasuredHeight)和宽(mMeasureWidth)传入setMeasuredDimension()方法完成单个View的测量 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } 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); } private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; } public void layout(int l, int t, int r, int b) { //为整个View树计算实际的位置, 需要递归的去计算每一个子视图的位置 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回调方法,具体实现由重写了onLayout方法的ViewGroup的子类去实现 onLayout(changed, l, t, r, b); //l,t,r,b四个值是子View相对于父View的值 mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ListenerInfo li = mListenerInfo; if (li != null && li.mOnLayoutChangeListeners != null) { ArrayList listenersCopy = (ArrayList)li.mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i 

ViewRootImpl.java

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { ... final class ViewRootHandler extends Handler { ... @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_INVALIDATE: ((View) msg.obj).invalidate(); break; case MSG_INVALIDATE_RECT: final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; info.target.invalidate(info.left, info.top, info.right, info.bottom); info.recycle(); break; case MSG_PROCESS_INPUT_EVENTS: mProcessInputEventsScheduled = false; doProcessInputEvents(); break; case MSG_DISPATCH_APP_VISIBILITY: handleAppVisibility(msg.arg1 != 0); break; case MSG_DISPATCH_GET_NEW_SURFACE: handleGetNewSurface(); break; case MSG_RESIZED: { // Recycled in the fall through... SomeArgs args = (SomeArgs) msg.obj; if (mWinFrame.equals(args.arg1) && mPendingOverscanInsets.equals(args.arg5) && mPendingContentInsets.equals(args.arg2) && mPendingStableInsets.equals(args.arg6) && mPendingVisibleInsets.equals(args.arg3) && args.arg4 == null) { break; } } // fall through... case MSG_RESIZED_REPORT: if (mAdded) { ... requestLayout(); } break; case MSG_WINDOW_MOVED: if (mAdded) { ... requestLayout(); } break; case MSG_WINDOW_FOCUS_CHANGED: { if (mAdded) { boolean hasWindowFocus = msg.arg1 != 0; mAttachInfo.mHasWindowFocus = hasWindowFocus; profileRendering(hasWindowFocus); if (hasWindowFocus) { boolean inTouchMode = msg.arg2 != 0; ensureTouchModeLocally(inTouchMode); if (mAttachInfo.mHardwareRenderer != null && mSurface.isValid()){ mFullRedrawNeeded = true; try { final WindowManager.LayoutParams lp = mWindowAttributes; final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null; mAttachInfo.mHardwareRenderer.initializeIfNeeded( mWidth, mHeight, mSurface, surfaceInsets); } catch (OutOfResourcesException e) { Log.e(TAG, "OutOfResourcesException locking surface", e); try { if (!mWindowSession.outOfMemory(mWindow)) { Slog.w(TAG, "No processes killed for memory; killing self"); Process.killProcess(Process.myPid()); } } catch (RemoteException ex) { } // Retry in a bit. sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); return; } } } mLastWasImTarget = WindowManager.LayoutParams .mayUseInputMethod(mWindowAttributes.flags); InputMethodManager imm = InputMethodManager.peekInstance(); if (mView != null) { if (hasWindowFocus && imm != null && mLastWasImTarget && !isInLocalFocusMode()) { imm.startGettingWindowFocus(mView); } mAttachInfo.mKeyDispatchState.reset(); mView.dispatchWindowFocusChanged(hasWindowFocus); mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); } // Note: must be done after the focus change callbacks, // so all of the view state is set up correctly. if (hasWindowFocus) { if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) { imm.onWindowFocus(mView, mView.findFocus(), mWindowAttributes.softInputMode, !mHasHadWindowFocus, mWindowAttributes.flags); } // Clear the forward bit.  We can just do this directly, since // the window manager doesn't care about it. mWindowAttributes.softInputMode &= ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; ((WindowManager.LayoutParams)mView.getLayoutParams()) .softInputMode &= ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; mHasHadWindowFocus = true; } if (mView != null && mAccessibilityManager.isEnabled()) { if (hasWindowFocus) { mView.sendAccessibilityEvent( AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } } } } break; ... } } } final ViewRootHandler mHandler = new ViewRootHandler(); public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds); } @Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; // scheduleTraversals(); // mTraversalRunnable => doTraversal => performTraversals } } @Override public boolean isLayoutRequested() { return mLayoutRequested; } void invalidate() { mDirty.set(0, 0, mWidth, mHeight); if (!mWillDrawSoon) { scheduleTraversals(); // mTraversalRunnable => doTraversal => performTraversals } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); } } void unscheduleTraversals() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); mChoreographer.removeCallbacks( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); } } void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals"); try { performTraversals(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } } private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); // 调用用View.java的measure()方法 } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { mLayoutRequested = false; mScrollMayChange = true; mInLayout = true; final View host = mView; if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { Log.v(TAG, "Laying out " + host + " to (" + host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout"); try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); // 调用用View.java的layout()方法 mInLayout = false; int numViewsRequestingLayout = mLayoutRequesters.size(); if (numViewsRequestingLayout > 0) { // requestLayout() was called during layout. // If no layout-request flags are set on the requesting views, there is no problem. // If some requests are still pending, then we need to clear those flags and do // a full request/measure/layout pass to handle this situation. ArrayList validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, false); if (validLayoutRequesters != null) { // Set this flag to indicate that any further requests are happening during // the second pass, which may result in posting those requests to the next // frame instead mHandlingLayoutInLayoutRequest = true; // Process fresh layout requests, then measure and layout int numValidRequests = validLayoutRequesters.size(); for (int i = 0; i  finalRequesters = validLayoutRequesters; // Post second-pass requests to the next frame getRunQueue().post(new Runnable() { @Override public void run() { int numValidRequests = finalRequesters.size(); for (int i = 0; i  draw() => drawSoftware() private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; } final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); try { draw(fullRedrawNeeded); // draw()调用 drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty) // private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) } finally { mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } // For whatever reason we didn't create a HardwareRenderer, end any // hardware animations that are now dangling if (mAttachInfo.mPendingAnimatingRenderNodes != null) { final int count = mAttachInfo.mPendingAnimatingRenderNodes.size(); for (int i = 0; i 

以上就是Android自定义一个view ViewRootImpl绘制流程示例的详细内容,更多关于Android view ViewRootImpl绘制的资料请关注0133技术站其它相关文章!

以上就是Android自定义一个view ViewRootImpl绘制流程示例的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 移动