android view draw原始碼過程分析
FrameLayout.java
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mForeground != null) {
final Drawable foreground = mForeground;
if (mForegroundBoundsChanged) {
mForegroundBoundsChanged = false;
final Rect selfBounds = mSelfBounds;
final Rect overlayBounds = mOverlayBounds;
final int w = mRight-mLeft;
final int h = mBottom-mTop;
if (mForegroundInPadding) {
selfBounds.set(0, 0, w, h);
} else {
selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
}
final int layoutDirection = getLayoutDirection();
Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
layoutDirection);
foreground.setBounds(overlayBounds);
}
foreground.draw(canvas);
}
}
super.draw()直接呼叫view.draw(). viewgroup沒實現重寫draw方法
有mForeground時繪製mForeground,
view.java
/**
* Manually render this view (and all of its children) to the given Canvas.
* The view must have already done a full layout before this function is
* called. When implementing a view, implement
* {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
* If you do need to override this method, call the superclass version.
*
* @param canvas The Canvas to which the View is rendered.
*/
public void draw(Canvas canvas) {
if (mClipBounds != null) {
canvas.clipRect(mClipBounds);
}
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) {
final Drawable background = mBackground;
if (background != null) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
}
if ((scrollX | scrollY) == 0) {
background.draw(canvas);
} else {
canvas.translate(scrollX, scrollY);
background.draw(canvas);
canvas.translate(-scrollX, -scrollY);
}
}
}
// 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;
}
onDraw(canvas); dispatchDraw(canvas); 都是空方法,對具體view重寫
ViewGroup.java
/**
* {@inheritDoc}
*/
@Override
protected void dispatchDraw(Canvas canvas) {
final int count = mChildrenCount;
final View[] children = mChildren;
int flags = mGroupFlags;
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
final boolean buildCache = !isHardwareAccelerated();
for (int i = 0; i < count; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
final LayoutParams params = child.getLayoutParams();
attachLayoutAnimationParameters(child, params, i, count);
bindLayoutAnimation(child);
if (cache) {
child.setDrawingCacheEnabled(true);
if (buildCache) {
child.buildDrawingCache(true);
}
}
}
}
final LayoutAnimationController controller = mLayoutAnimationController;
if (controller.willOverlap()) {
mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
}
controller.start();
mGroupFlags &= ~FLAG_RUN_ANIMATION;
mGroupFlags &= ~FLAG_ANIMATION_DONE;
if (cache) {
mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
}
if (mAnimationListener != null) {
mAnimationListener.onAnimationStart(controller.getAnimation());
}
}
int saveCount = 0;
final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
if (clipToPadding) {
saveCount = canvas.save();
canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
mScrollX + mRight - mLeft - mPaddingRight,
mScrollY + mBottom - mTop - mPaddingBottom);
}
// We will draw our child's animation, let's reset the flag
mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
boolean more = false;
final long drawingTime = getDrawingTime();
if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
for (int i = 0; i < count; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
} else {
for (int i = 0; i < count; i++) {
final View child = children[getChildDrawingOrder(count, i)];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
}
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
final ArrayList<View> disappearingChildren = mDisappearingChildren;
final int disappearingCount = disappearingChildren.size() - 1;
// Go backwards -- we may delete as animations finish
for (int i = disappearingCount; i >= 0; i--) {
final View child = disappearingChildren.get(i);
more |= drawChild(canvas, child, drawingTime);
}
}
if (debugDraw()) {
onDebugDraw(canvas);
}
if (clipToPadding) {
canvas.restoreToCount(saveCount);
}
// mGroupFlags might have been updated by drawChild()
flags = mGroupFlags;
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
invalidate(true);
}
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
mLayoutAnimationController.isDone() && !more) {
// We want to erase the drawing cache and notify the listener after the
// next frame is drawn because one extra invalidate() is caused by
// drawChild() after the animation is over
mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
final Runnable end = new Runnable() {
public void run() {
notifyAnimationListener();
}
};
post(end);
}
}
/**
* Draw one child of this View Group. This method is responsible for getting
* the canvas in the right state. This includes clipping, translating so
* that the child's scrolled origin is at 0, 0, and applying any animation
* transformations.
*
* @param canvas The canvas on which to draw the child
* @param child Who to draw
* @param drawingTime The time at which draw is occurring
* @return True if an invalidate() was issued
*/
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
dispatchDraw()的核心程式碼就是通過for迴圈呼叫drawChild()對ViewGroup的每個子檢視進行繪製,上述程式碼中如果FLAG_USE_CHILD_DRAWING_ORDER為true,則子檢視的繪製順序通過getChildDrawingOrder來決定,預設的繪製順序即是子檢視加入ViewGroup的順序,而我們可以過載getChildDrawingOrder函式來更改預設的繪製順序,這會影響到子檢視之間的重疊關係。
drawChild()的核心過程就是為子檢視分配合適的cavas剪下區,剪下區的大小正是由layout過程決定的,而剪下區的位置取決於滾動值以及子檢視當前的動畫。設定完剪下區後就會呼叫子檢視的draw()函式進行具體的繪製,如果子檢視的包含SKIP_DRAW標識,那麼僅呼叫dispatchDraw(),即跳過子檢視本身的繪製,但要繪製檢視可能包含的字檢視
相關文章
- android View measure過程原始碼解析AndroidView原始碼
- Android View 原始碼解析(三) – View的繪製過程AndroidView原始碼
- View draw流程分析View
- Android原始碼完全解析——View的Measure過程Android原始碼View
- android view layout原始碼分析AndroidView原始碼
- 原始碼解析Android中View的layout佈局過程原始碼AndroidView
- 原始碼解析Android中View的measure量算過程原始碼AndroidView
- Android View 事件分發原始碼分析AndroidView事件原始碼
- Android 原始碼分析(一)專案構建過程Android原始碼
- Android系統原始碼分析--Activity啟動過程Android原始碼
- Android系統原始碼分析--Process啟動過程Android原始碼
- Android應用程式啟動過程原始碼分析Android原始碼
- MySQL • 原始碼分析 • SHUTDOWN過程MySql原始碼
- Android原始碼分析之View繪製流程Android原始碼View
- Android View繪製原始碼分析 MeasureAndroidView原始碼
- 【Android原始碼】View的繪製流程分析Android原始碼View
- React Native Android 原始碼分析之啟動過程React NativeAndroid原始碼
- Android View 繪製流程(Draw) 完全解析AndroidView
- Android系統原始碼分析–Zygote和SystemServer啟動過程Android原始碼GoServer
- Android系統原始碼分析--Zygote和SystemServer啟動過程Android原始碼GoServer
- Android View的繪製過程AndroidView
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- Netty NioEventLoop 建立過程原始碼分析NettyOOP原始碼
- Spring啟動過程——原始碼分析Spring原始碼
- 原始碼分析OKHttp的執行過程原始碼HTTP
- knockout原始碼分析之執行過程原始碼
- Android系統程式Zygote啟動過程的原始碼分析(3)AndroidGo原始碼
- 初探Android的View繪製過程AndroidView
- android apk安裝過程原始碼解析AndroidAPK原始碼
- 【Android原始碼】BroadcastReceiver的工作過程Android原始碼AST
- 【Android原始碼】Service的啟動過程Android原始碼
- 【Android原始碼】Service的繫結過程Android原始碼
- Android面試複習之View事件體系(原始碼分析)Android面試View事件原始碼
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- View繪製流程原始碼分析View原始碼
- Android 原始碼分析之旅2 1 IPC以及Service的啟動過程Android原始碼
- SOFA 原始碼分析 —— 服務釋出過程原始碼
- Spring Boot原始碼分析-啟動過程Spring Boot原始碼