編輯:關於Android編程
上一篇博客我們講到了ViewRoot中與UI相關的三個重要步驟:performMeasure(測量)、performLayout(布局)和performDraw(繪制),這次我們就來重點研究一下這三個方法。先上圖說明三個方法的關系:
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams(); private void performTraversals() { ... WindowManager.LayoutParams lp = mWindowAttributes; ... int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ... // Ask host how big it wants to be performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... }
/** * Figures out the measure spec for the root view in a window based on it's * layout params. * * @param windowSize * The available width or height of the window * * @param rootDimension * The layout params for one dimension (width or height) of the * window. * * @return The measure spec to use to measure the root view. */ private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
/** * A MeasureSpec encapsulates the layout requirements passed from parent to child. * Each MeasureSpec represents a requirement for either the width or the height. * A MeasureSpec is comprised of a size and a mode. There are three possible * modes: * UNSPECIFIED * The parent has not imposed any constraint on the child. It can be whatever size * it wants. * EXACTLY * The parent has determined an exact size for the child. The child is going to be * given those bounds regardless of how big it wants to be. * AT_MOST * The child can be as large as it wants up to the specified size. * * MeasureSpecs are implemented as ints to reduce object allocation. This class * is provided to pack and unpack thesize, mode tuple into the int. */ public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; /** * Measure specification mode: The parent has not imposed any constraint * on the child. It can be whatever size it wants. */ public static final int UNSPECIFIED = 0 << MODE_SHIFT; /** * Measure specification mode: The parent has determined an exact size * for the child. The child is going to be given those bounds regardless * of how big it wants to be. */ public static final int EXACTLY = 1 << MODE_SHIFT; /** * Measure specification mode: The child can be as large as it wants up * to the specified size. */ public static final int AT_MOST = 2 << MODE_SHIFT; public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } ... }
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
int mPrivateFlags; public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... // 大致是強制需要測量的意思 if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); // 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 & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; }
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
private void performTraversals() { ... performLayout(lp, desiredWindowWidth, desiredWindowHeight); ... }
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) { ... final View host = mView; ... try { host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight()); ... }
@Override public final void layout(int l, int t, int r, int b) { if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) { if (mTransition != null) { mTransition.layoutChange(this); } super.layout(l, t, r, b); } else { // record the fact that we noop'd it; request layout when transition finishes mLayoutCalledWhileSuppressed = true; } }
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 = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ... } mPrivateFlags &= ~PFLAG_FORCE_LAYOUT; }
protected boolean setFrame(int left, int top, int right, int bottom) { boolean changed = false; ... if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) { changed = true; // Remember our drawn bit int drawn = mPrivateFlags & PFLAG_DRAWN; int oldWidth = mRight - mLeft; int oldHeight = mBottom - mTop; int newWidth = right - left; int newHeight = bottom - top; boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight); ... if (sizeChanged) { if ((mPrivateFlags & PFLAG_PIVOT_EXPLICITLY_SET) == 0) { // A change in dimension means an auto-centered pivot point changes, too if (mTransformationInfo != null) { mTransformationInfo.mMatrixDirty = true; } } sizeChange(newWidth, newHeight, oldWidth, oldHeight); } ... } return changed; }
private void sizeChange(int newWidth, int newHeight, int oldWidth, int oldHeight) { onSizeChanged(newWidth, newHeight, oldWidth, oldHeight); if (mOverlay != null) { mOverlay.getOverlayView().setRight(newWidth); mOverlay.getOverlayView().setBottom(newHeight); } }
@Override protected abstract void onLayout(boolean changed, int l, int t, int r, int b);
private void performDraw() { ... final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); try { draw(fullRedrawNeeded); } finally { mIsDrawing = false; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } ... }
private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; ... final Rect dirty = mDirty; ... if (fullRedrawNeeded) { attachInfo.mIgnoreDirtyState = true; dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); } ... // 使用硬件渲染繪制 if (!dirty.isEmpty() || mIsAnimating) { if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) { ... attachInfo.mHardwareRenderer.draw(mView, attachInfo, this, animating ? null : mCurrentDirty); } else { ... // 使用軟件渲染繪制 if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { return; } } } ... }
/** * @return true if drawing was succesfull, false if an error occurred */ private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty) { // Draw with software renderer. Canvas canvas; try { int left = dirty.left; int top = dirty.top; int right = dirty.right; int bottom = dirty.bottom; canvas = mSurface.lockCanvas(dirty); ... try { ... mView.draw(canvas); ... } finally { try { surface.unlockCanvasAndPost(canvas); } ... } return true; }
public void draw(Canvas canvas) { ... /* * 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) { ... background.draw(canvas); ... } // Step 2, save the canvas' layers ... // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers ... // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } }
最近根據項目需要,整理了一個相對比較全面的 WheelView 使用控件,借用之前看到的一句話來說,就是站在巨人肩膀上,進行了一些小調整。 這裡先貼上效果圖一般常用的時間
一 相關下載(1) java JDK下載:進入該網頁: http://java.sun.com/javase/downloads/index.jsp (或者
一、網絡爬蟲的基本知識網絡爬蟲通過遍歷互聯網絡,把網絡中的相關網頁全部抓取過來,這體現了爬的概念。爬蟲如何遍歷網絡呢,互聯網可以看做是一張大圖,每個頁面看做其中的一個節點
Android 實現記住用戶名和密碼的功能是通過SharedPreference 存儲來實現的。創建一個復選按鈕,通過按鈕的否選取來進行事件處理。若按鈕選中存儲賬號和密碼