ソース分析:onAttach,onMeasure,onLayout,onDrawの順序.
26331 ワード
前文「ソース解析:dialog、popupwindow、activityの最初のviewはどうやって来たの?」の中でactivityの最初のviewあるいはルートviewあるいはmDecorViewが実は1つのFrameLayoutであることを知っていて、およびシステムhandleResumeの時にシステムwindowManagerの中に加入して、そしてframeworkの中のViewRootImplが引き継いで、ViewRootImplを通じて(通って)setView()は、表示プロセス全体を開始します.今回はviewの表示プロセス(onAttach,onMeasure,onLayout,onDraw)のソースコードにおけるプロセスを重点的に整理します.
ViewRootImplからsetviewが始まります。
ViewRootImplからsetviewが始まります。 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
は、最初にmViewを割り当てるときに、ViewRootImplを呼び出す.requestLayout(); public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
さらにscheduleTraversals(); public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//noinspection ConstantConditions
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
final long now = System.nanoTime();
Log.d(TAG, "Latency: Scheduled traversal, it has been "
+ ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
+ "ms since the last traversal finished.");
}
sendEmptyMessage(DO_TRAVERSAL);
}
}
さらにhandleMessage()に @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL:
performTraversals();
}
さらにperformTraversals()であり,今回の分析の重点である.
この関数は長いので、すべての関数コードを貼るのに適していません.分段について述べる.
1.関数の最初の部分は、後で使用する変数とフラグビットを初期化します。 final View host = mView;
WindowManager.LayoutParams lp = mWindowAttributes;
final View.AttachInfo attachInfo = mAttachInfo;
CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
Rect frame = mWinFrame;
mTraversalScheduled = false;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean fullRedrawNeeded = mFullRedrawNeeded;
boolean newSurface = false;
boolean surfaceChanged = false;
2.次に、初回実行であればhostを呼び出す重要な判断である.dispatchAttachedToWindow(attachInfo, 0); if (mFirst) {
//
mLastConfiguration.setTo(host.getResources().getConfiguration());
host.dispatchAttachedToWindow(attachInfo, 0);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
host.fitSystemWindows(mAttachInfo.mContentInsets);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
はdispatchAttachedToWindow()で3つのことを重点的に処理した.
2.1. onAttachedToWindow();
2.2. listener.onViewAttachedToWindow(this);
2.3 onWindowVisibilityChanged(vis); void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
mWindowAttachCount++;
onAttachedToWindow();
final CopyOnWriteArrayList listeners =
mOnAttachStateChangeListeners;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
}
}
ここでonAttachedToWindow()のコメントに問題があります.
onMeasureの前または後にonAttachedToWindowが呼び出されることを保証するのではなく、onDrawの前に呼び出されます. /**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
* guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
* however it may be called any time before the first onDraw -- including
* before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
protected void onAttachedToWindow() {
protected void onAttachedToWindow() {
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
resolveLayoutDirectionIfNeeded();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
}
}
3 fitSystemWindows()は、システムwindowであればpaddingの値を使用してinsetsを計算し、androidを開始する.view.View.requestLayout() protected boolean fitSystemWindows(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
mPaddingLeft = insets.left;
mPaddingTop = insets.top;
mPaddingRight = insets.right;
mPaddingBottom = insets.bottom;
requestLayout();
return true;
}
return false;
}
ここにいるよview.View.requestLayout()では、parentを呼び出すrequestLayout()が存在する場合、単純な1層1層上にparentが存在するかどうかをチェックします. /**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree.
*/
public void requestLayout() {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
}
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
if (mParent != null) {
if (mLayoutParams != null) {
mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
}
if (!mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
}
しかし、各層の呼び出しが完了し、layout操作を直ちに実行するのではなく、フラグビットmPrivateFlags|=FORCE_を付与することによってLAYOUT;,表示しに来ただけです.本当のlayoutプロセスは後ろにあります.
4.コードがあります。 if (mLayoutRequested && !mStopped) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
RunQueueは、handlerが初期化されていないときにイベントを処理するためのメッセージキューです.ViewRootImpl postに与えるイベントタイプをrunnableとし,handlerが構築されてからhandler処理に送る.本文のポイントではないので、簡単に言ってください.
5次はmeasureの部分です boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(TAG, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
のこのコードは、基本的には、場合によっては特定のパラメータでmeasureを使用する、場合によっては別の値でmeasureを使用する.
すべて呼び出しのhostです.measure(childWidthMeasureSpec, childHeightMeasureSpec); ただし状況が異なり、使用するパラメータが異なります.
計算値のこのセクションは、前述の「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」を参照してください.
次にmeasure()メソッドを呼び出す public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
は、いくつかの判定およびフラグビットmPrivateFlags付与後にonMeasure()メソッドを呼び出す.onMeasure()の議論も前の文書「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」に進んでください.
メソッドの最後にフラグ位置ビットmPrivateFlags|=LAYOUT_REQUIRED;
6それから複雑な論理と賦値が山積みになった後、relayoutWindow()を呼び出した。これはまだ何が起こっているのか分からない//TODO relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
7次に論理と付与値の山であり、場合によってはhostも呼び出される.measure()。measureが完成しました
8.そしてlayoutの部分です。 final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
layout()では、まずFrameをセットし、その後ステータスフラグビットを判断し、さらにonLayout()をコールバックする.つまりカスタマイズされた部分です. public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
if (mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList) 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 &= ~FORCE_LAYOUT;
}
setFrame()では、新しい位置と古い位置が一致するかどうかを判断します. protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mPrivateFlags |= HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
一致しない場合はinvalidate()を呼び出す. void invalidate(boolean invalidateCache) {
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}
親コントロールのp.invalidateChild()を呼び出してdirty領域を計算し、識別します.領域範囲はサブviewが位置する領域です. public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
setframeの戻り値がtrueの場合、領域の内容が変化したことを示し、さらにonLayout()メソッドとmOnLayoutChangeListenersのonLayoutChange()をコールバックする. /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
onLayout()では、viewは、各サブviewのlayout()メソッドを呼び出すことによって、各サブviewのサイズと位置を指定しなければならない.
9次は分からない部分があるsetInsets // TODO if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
10,処理過程の一部を省略し,焦点のコールバックや処理,入力法の処理などがある.
11.drawの部分に入ります。 if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する. private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.
尻尾
しっぽ、次は事件の流れを渡りましょう.
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets,
mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//noinspection ConstantConditions
if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
final long now = System.nanoTime();
Log.d(TAG, "Latency: Scheduled traversal, it has been "
+ ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
+ "ms since the last traversal finished.");
}
sendEmptyMessage(DO_TRAVERSAL);
}
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL:
performTraversals();
}
final View host = mView;
WindowManager.LayoutParams lp = mWindowAttributes;
final View.AttachInfo attachInfo = mAttachInfo;
CompatibilityInfo compatibilityInfo = mCompatibilityInfo.get();
Rect frame = mWinFrame;
mTraversalScheduled = false;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean fullRedrawNeeded = mFullRedrawNeeded;
boolean newSurface = false;
boolean surfaceChanged = false;
2.次に、初回実行であればhostを呼び出す重要な判断である.dispatchAttachedToWindow(attachInfo, 0); if (mFirst) {
//
mLastConfiguration.setTo(host.getResources().getConfiguration());
host.dispatchAttachedToWindow(attachInfo, 0);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
host.fitSystemWindows(mAttachInfo.mContentInsets);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
はdispatchAttachedToWindow()で3つのことを重点的に処理した.
2.1. onAttachedToWindow();
2.2. listener.onViewAttachedToWindow(this);
2.3 onWindowVisibilityChanged(vis); void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
mWindowAttachCount++;
onAttachedToWindow();
final CopyOnWriteArrayList listeners =
mOnAttachStateChangeListeners;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
}
}
ここでonAttachedToWindow()のコメントに問題があります.
onMeasureの前または後にonAttachedToWindowが呼び出されることを保証するのではなく、onDrawの前に呼び出されます. /**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
* guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
* however it may be called any time before the first onDraw -- including
* before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
protected void onAttachedToWindow() {
protected void onAttachedToWindow() {
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
resolveLayoutDirectionIfNeeded();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
}
}
3 fitSystemWindows()は、システムwindowであればpaddingの値を使用してinsetsを計算し、androidを開始する.view.View.requestLayout() protected boolean fitSystemWindows(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
mPaddingLeft = insets.left;
mPaddingTop = insets.top;
mPaddingRight = insets.right;
mPaddingBottom = insets.bottom;
requestLayout();
return true;
}
return false;
}
ここにいるよview.View.requestLayout()では、parentを呼び出すrequestLayout()が存在する場合、単純な1層1層上にparentが存在するかどうかをチェックします. /**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree.
*/
public void requestLayout() {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
}
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
if (mParent != null) {
if (mLayoutParams != null) {
mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
}
if (!mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
}
しかし、各層の呼び出しが完了し、layout操作を直ちに実行するのではなく、フラグビットmPrivateFlags|=FORCE_を付与することによってLAYOUT;,表示しに来ただけです.本当のlayoutプロセスは後ろにあります.
4.コードがあります。 if (mLayoutRequested && !mStopped) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
RunQueueは、handlerが初期化されていないときにイベントを処理するためのメッセージキューです.ViewRootImpl postに与えるイベントタイプをrunnableとし,handlerが構築されてからhandler処理に送る.本文のポイントではないので、簡単に言ってください.
5次はmeasureの部分です boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(TAG, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
のこのコードは、基本的には、場合によっては特定のパラメータでmeasureを使用する、場合によっては別の値でmeasureを使用する.
すべて呼び出しのhostです.measure(childWidthMeasureSpec, childHeightMeasureSpec); ただし状況が異なり、使用するパラメータが異なります.
計算値のこのセクションは、前述の「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」を参照してください.
次にmeasure()メソッドを呼び出す public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
は、いくつかの判定およびフラグビットmPrivateFlags付与後にonMeasure()メソッドを呼び出す.onMeasure()の議論も前の文書「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」に進んでください.
メソッドの最後にフラグ位置ビットmPrivateFlags|=LAYOUT_REQUIRED;
6それから複雑な論理と賦値が山積みになった後、relayoutWindow()を呼び出した。これはまだ何が起こっているのか分からない//TODO relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
7次に論理と付与値の山であり、場合によってはhostも呼び出される.measure()。measureが完成しました
8.そしてlayoutの部分です。 final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
layout()では、まずFrameをセットし、その後ステータスフラグビットを判断し、さらにonLayout()をコールバックする.つまりカスタマイズされた部分です. public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
if (mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList) 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 &= ~FORCE_LAYOUT;
}
setFrame()では、新しい位置と古い位置が一致するかどうかを判断します. protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mPrivateFlags |= HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
一致しない場合はinvalidate()を呼び出す. void invalidate(boolean invalidateCache) {
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}
親コントロールのp.invalidateChild()を呼び出してdirty領域を計算し、識別します.領域範囲はサブviewが位置する領域です. public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
setframeの戻り値がtrueの場合、領域の内容が変化したことを示し、さらにonLayout()メソッドとmOnLayoutChangeListenersのonLayoutChange()をコールバックする. /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
onLayout()では、viewは、各サブviewのlayout()メソッドを呼び出すことによって、各サブviewのサイズと位置を指定しなければならない.
9次は分からない部分があるsetInsets // TODO if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
10,処理過程の一部を省略し,焦点のコールバックや処理,入力法の処理などがある.
11.drawの部分に入ります。 if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する. private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.
尻尾
しっぽ、次は事件の流れを渡りましょう.
if (mFirst) {
//
mLastConfiguration.setTo(host.getResources().getConfiguration());
host.dispatchAttachedToWindow(attachInfo, 0);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
host.fitSystemWindows(mAttachInfo.mContentInsets);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
mWindowAttachCount++;
onAttachedToWindow();
final CopyOnWriteArrayList listeners =
mOnAttachStateChangeListeners;
if (listeners != null && listeners.size() > 0) {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
// the array from being modified while we iterate it.
for (OnAttachStateChangeListener listener : listeners) {
listener.onViewAttachedToWindow(this);
}
}
int vis = info.mWindowVisibility;
if (vis != GONE) {
onWindowVisibilityChanged(vis);
}
}
/**
* This is called when the view is attached to a window. At this point it
* has a Surface and will start drawing. Note that this function is
* guaranteed to be called before {@link #onDraw(android.graphics.Canvas)},
* however it may be called any time before the first onDraw -- including
* before or after {@link #onMeasure(int, int)}.
*
* @see #onDetachedFromWindow()
*/
protected void onAttachedToWindow() {
protected void onAttachedToWindow() {
// Order is important here: LayoutDirection MUST be resolved before Padding
// and TextDirection
resolveLayoutDirectionIfNeeded();
resolvePadding();
resolveTextDirection();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
}
}
protected boolean fitSystemWindows(Rect insets) {
if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
mPaddingLeft = insets.left;
mPaddingTop = insets.top;
mPaddingRight = insets.right;
mPaddingBottom = insets.bottom;
requestLayout();
return true;
}
return false;
}
ここにいるよview.View.requestLayout()では、parentを呼び出すrequestLayout()が存在する場合、単純な1層1層上にparentが存在するかどうかをチェックします.
/**
* Call this when something has changed which has invalidated the
* layout of this view. This will schedule a layout pass of the view
* tree.
*/
public void requestLayout() {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
}
mPrivateFlags |= FORCE_LAYOUT;
mPrivateFlags |= INVALIDATED;
if (mParent != null) {
if (mLayoutParams != null) {
mLayoutParams.resolveWithDirection(getResolvedLayoutDirection());
}
if (!mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
}
しかし、各層の呼び出しが完了し、layout操作を直ちに実行するのではなく、フラグビットmPrivateFlags|=FORCE_を付与することによってLAYOUT;,表示しに来ただけです.本当のlayoutプロセスは後ろにあります.4.コードがあります。 if (mLayoutRequested && !mStopped) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
RunQueueは、handlerが初期化されていないときにイベントを処理するためのメッセージキューです.ViewRootImpl postに与えるイベントタイプをrunnableとし,handlerが構築されてからhandler処理に送る.本文のポイントではないので、簡単に言ってください.
5次はmeasureの部分です boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(TAG, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
のこのコードは、基本的には、場合によっては特定のパラメータでmeasureを使用する、場合によっては別の値でmeasureを使用する.
すべて呼び出しのhostです.measure(childWidthMeasureSpec, childHeightMeasureSpec); ただし状況が異なり、使用するパラメータが異なります.
計算値のこのセクションは、前述の「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」を参照してください.
次にmeasure()メソッドを呼び出す public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
は、いくつかの判定およびフラグビットmPrivateFlags付与後にonMeasure()メソッドを呼び出す.onMeasure()の議論も前の文書「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」に進んでください.
メソッドの最後にフラグ位置ビットmPrivateFlags|=LAYOUT_REQUIRED;
6それから複雑な論理と賦値が山積みになった後、relayoutWindow()を呼び出した。これはまだ何が起こっているのか分からない//TODO relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
7次に論理と付与値の山であり、場合によってはhostも呼び出される.measure()。measureが完成しました
8.そしてlayoutの部分です。 final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
layout()では、まずFrameをセットし、その後ステータスフラグビットを判断し、さらにonLayout()をコールバックする.つまりカスタマイズされた部分です. public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
if (mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList) 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 &= ~FORCE_LAYOUT;
}
setFrame()では、新しい位置と古い位置が一致するかどうかを判断します. protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mPrivateFlags |= HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
一致しない場合はinvalidate()を呼び出す. void invalidate(boolean invalidateCache) {
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}
親コントロールのp.invalidateChild()を呼び出してdirty領域を計算し、識別します.領域範囲はサブviewが位置する領域です. public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
setframeの戻り値がtrueの場合、領域の内容が変化したことを示し、さらにonLayout()メソッドとmOnLayoutChangeListenersのonLayoutChange()をコールバックする. /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
onLayout()では、viewは、各サブviewのlayout()メソッドを呼び出すことによって、各サブviewのサイズと位置を指定しなければならない.
9次は分からない部分があるsetInsets // TODO if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
10,処理過程の一部を省略し,焦点のコールバックや処理,入力法の処理などがある.
11.drawの部分に入ります。 if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する. private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.
尻尾
しっぽ、次は事件の流れを渡りましょう.
if (mLayoutRequested && !mStopped) {
// Execute enqueued actions on every layout in case a view that was detached
// enqueued an action after being detached
getRunQueue().executeActions(attachInfo.mHandler);
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
// On large screens, we don't want to allow dialogs to just
// stretch to fill the entire width of the screen to display
// one line of text. First try doing the layout at a smaller
// size to see if it will fit.
final DisplayMetrics packageMetrics = res.getDisplayMetrics();
res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
int baseSize = 0;
if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
baseSize = (int)mTmpValue.getDimension(packageMetrics);
}
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
if (baseSize != 0 && desiredWindowWidth > baseSize) {
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
goodMeasure = true;
} else {
// Didn't fit in that size... try expanding a bit.
baseSize = (baseSize+desiredWindowWidth)/2;
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+ baseSize);
childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+ host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");
if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
if (DEBUG_DIALOG) Log.v(TAG, "Good!");
goodMeasure = true;
}
}
}
}
if (!goodMeasure) {
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
のこのコードは、基本的には、場合によっては特定のパラメータでmeasureを使用する、場合によっては別の値でmeasureを使用する.すべて呼び出しのhostです.measure(childWidthMeasureSpec, childHeightMeasureSpec); ただし状況が異なり、使用するパラメータが異なります.
計算値のこのセクションは、前述の「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」を参照してください.
次にmeasure()メソッドを呼び出す
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
は、いくつかの判定およびフラグビットmPrivateFlags付与後にonMeasure()メソッドを呼び出す.onMeasure()の議論も前の文書「ソース分析:LayoutParamsのwrap_content,match_parent,および具体的な値」に進んでください.メソッドの最後にフラグ位置ビットmPrivateFlags|=LAYOUT_REQUIRED;
6それから複雑な論理と賦値が山積みになった後、relayoutWindow()を呼び出した。これはまだ何が起こっているのか分からない//TODO relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
7次に論理と付与値の山であり、場合によってはhostも呼び出される.measure()。measureが完成しました
8.そしてlayoutの部分です。 final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
layout()では、まずFrameをセットし、その後ステータスフラグビットを判断し、さらにonLayout()をコールバックする.つまりカスタマイズされた部分です. public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
if (mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList) 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 &= ~FORCE_LAYOUT;
}
setFrame()では、新しい位置と古い位置が一致するかどうかを判断します. protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mPrivateFlags |= HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
一致しない場合はinvalidate()を呼び出す. void invalidate(boolean invalidateCache) {
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}
親コントロールのp.invalidateChild()を呼び出してdirty領域を計算し、識別します.領域範囲はサブviewが位置する領域です. public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
setframeの戻り値がtrueの場合、領域の内容が変化したことを示し、さらにonLayout()メソッドとmOnLayoutChangeListenersのonLayoutChange()をコールバックする. /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
onLayout()では、viewは、各サブviewのlayout()メソッドを呼び出すことによって、各サブviewのサイズと位置を指定しなければならない.
9次は分からない部分があるsetInsets // TODO if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
10,処理過程の一部を省略し,焦点のコールバックや処理,入力法の処理などがある.
11.drawの部分に入ります。 if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する. private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.
尻尾
しっぽ、次は事件の流れを渡りましょう.
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
8.そしてlayoutの部分です。 final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
layout()では、まずFrameをセットし、その後ステータスフラグビットを判断し、さらにonLayout()をコールバックする.つまりカスタマイズされた部分です. public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
if (mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList) 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 &= ~FORCE_LAYOUT;
}
setFrame()では、新しい位置と古い位置が一致するかどうかを判断します. protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mPrivateFlags |= HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
一致しない場合はinvalidate()を呼び出す. void invalidate(boolean invalidateCache) {
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}
親コントロールのp.invalidateChild()を呼び出してdirty領域を計算し、識別します.領域範囲はサブviewが位置する領域です. public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
setframeの戻り値がtrueの場合、領域の内容が変化したことを示し、さらにonLayout()メソッドとmOnLayoutChangeListenersのonLayoutChange()をコールバックする. /**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
onLayout()では、viewは、各サブviewのlayout()メソッドを呼び出すことによって、各サブviewのサイズと位置を指定しなければならない.
9次は分からない部分があるsetInsets // TODO if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
10,処理過程の一部を省略し,焦点のコールバックや処理,入力法の処理などがある.
11.drawの部分に入ります。 if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する. private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.
尻尾
しっぽ、次は事件の流れを渡りましょう.
final boolean didLayout = mLayoutRequested && !mStopped;
boolean triggerGlobalLayoutListener = didLayout
|| attachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
mLayoutRequested = false;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = setFrame(l, t, r, b);
if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
}
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED;
if (mOnLayoutChangeListeners != null) {
ArrayList listenersCopy =
(ArrayList) 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 &= ~FORCE_LAYOUT;
}
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mPrivateFlags |= HAS_BOUNDS;
if (sizeChanged) {
if ((mPrivateFlags & PIVOT_EXPLICITLY_SET) == 0) {
// A change in dimension means an auto-centered pivot point changes, too
if (mTransformationInfo != null) {
mTransformationInfo.mMatrixDirty = true;
}
}
onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
}
if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
// If we are visible, force the DRAWN bit to on so that
// this invalidate will go through (at least to our parent).
// This is because someone may have invalidated this view
// before this call to setFrame came in, thereby clearing
// the DRAWN bit.
mPrivateFlags |= DRAWN;
invalidate(sizeChanged);
// parent display list may need to be recreated based on a change in the bounds
// of any child
invalidateParentCaches();
}
// Reset drawn bit to original value (invalidate turns it off)
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
}
return changed;
}
void invalidate(boolean invalidateCache) {
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
r.set(0, 0, mRight - mLeft, mBottom - mTop);
// Don't call invalidate -- we don't want to internally scroll
// our own bounds
p.invalidateChild(this, r);
}
}
public void invalidateChild(View child, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
// Fast invalidation for GL-enabled applications; GL must redraw everything
invalidate();
return;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
if (!mDirty.isEmpty() && !mDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
mDirty.union(dirty);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
/**
* Called from layout when this view should
* assign a size and position to each of its children.
*
* Derived classes with children should override
* this method and call layout on each of
* their children.
* @param changed This is a new size or position for this view
* @param left Left position, relative to parent
* @param top Top position, relative to parent
* @param right Right position, relative to parent
* @param bottom Bottom position, relative to parent
*/
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
if (computesInternalInsets) {
// Clear the original insets.
final ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
insets.reset();
// Compute new insets in place.
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
10,処理過程の一部を省略し,焦点のコールバックや処理,入力法の処理などがある.
11.drawの部分に入ります。 if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する. private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.
尻尾
しっぽ、次は事件の流れを渡りましょう.
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
mFullRedrawNeeded = false;
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
}
draw(fullRedrawNeeded);
if (ViewDebug.DEBUG_LATENCY) {
mLastDrawDurationNanos = System.nanoTime() - drawStartTime;
}
if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
|| mReportNextDraw) {
if (LOCAL_LOGV) {
Log.v(TAG, "FINISHED DRAWING: " + mWindowAttributes.getTitle());
}
mReportNextDraw = false;
if (mSurfaceHolder != null && mSurface.isValid()) {
mSurfaceHolderCallback.surfaceRedrawNeeded(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
if (c instanceof SurfaceHolder.Callback2) {
((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
mSurfaceHolder);
}
}
}
}
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
}
draw()メソッドではdirtyの領域を計算し,ハードウェア加速を用いた場合,対応する処理を行い,そうでなければcanvasを用いてviewを描画する.
private void draw(boolean fullRedrawNeeded) {
mView.draw(canvas);
はandroidです.view.View.draw(Canvas)では、すべてのサブクラスを描画します. public void draw(Canvas canvas) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
}
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~DIRTY_MASK) | 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 = mBGDrawable;
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);
// we're done...
return;
}
注釈はよく書かれていて、step 1で背景を描き、step 3で自分のondraw()を呼び出します.step 4でdispatchDraw()ドラフトサブviewを呼び出します.
最後にsWindowSessionを呼び出します.finishDrawing()は下層部の完成を知らせるものと思われますが、よくわかりません.//TODO
try {
sWindowSession.finishDrawing(mWindow);
} catch (RemoteException e) {
}
drawの部分はこれで完成
http://u.cncn.com/space-323-do-blog-id-377295.html http://u.cncn.com/space-323-do-blog-id-377142.html http://u.cncn.com/space-mtag-tagid-782.html http://u.cncn.com/space-mtag-tagid-783.html
これで完全な描画プロセスが完了し、残りはこのプロセスを繰り返します.コールバックプロセスは、前述の「UI上位イベント処理コアメカニズムChoreographerの詳細な分析」を参照してください.