diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index c8e3d03169e84..9e50ee8987695 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -24,6 +24,7 @@ import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result.Status; import com.android.ide.common.rendering.api.SessionParams; +import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.layoutlib.bridge.impl.RenderDrawable; import com.android.layoutlib.bridge.impl.RenderSessionImpl; import com.android.layoutlib.bridge.util.DynamicIdMap; @@ -408,7 +409,9 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { /** * Starts a layout session by inflating and rendering it. The method returns a * {@link RenderSession} on which further actions can be taken. - * + *

+ * If {@link SessionParams} includes the {@link RenderParamsFlags#FLAG_DO_NOT_RENDER_ON_CREATE}, + * this method will only inflate the layout but will NOT render it. * @param params the {@link SessionParams} object with all the information necessary to create * the scene. * @return a new {@link RenderSession} object that contains the result of the layout. @@ -424,7 +427,10 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { lastResult = scene.init(params.getTimeout()); if (lastResult.isSuccess()) { lastResult = scene.inflate(); - if (lastResult.isSuccess()) { + + boolean doNotRenderOnCreate = Boolean.TRUE.equals( + params.getFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE)); + if (lastResult.isSuccess() && !doNotRenderOnCreate) { lastResult = scene.render(true /*freshRender*/); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java index bd17a2fe6ca2d..051de9055042e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java @@ -53,6 +53,12 @@ public final class RenderParamsFlags { */ public static final Key FLAG_KEY_XML_FILE_PARSER_SUPPORT = new Key("xmlFileParser", Boolean.class); + /** + * To tell LayoutLib to not render when creating a new session. This allows controlling when the first + * layout rendering will happen. + */ + public static final Key FLAG_DO_NOT_RENDER_ON_CREATE = + new Key("doNotRenderOnCreate", Boolean.class); // Disallow instances. private RenderParamsFlags() {} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 016825ae66e21..ce7104ee6f9bf 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -266,6 +266,34 @@ public class RenderSessionImpl extends RenderAction { mElapsedFrameTimeNanos = nanos; } + /** + * Renders the given view hierarchy to the passed canvas and returns the result of the render + * operation. + * @param canvas an optional canvas to render the views to. If null, only the measure and + * layout steps will be executed. + */ + private static Result render(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot, + @Nullable Canvas canvas, int width, int height) { + // measure again with the size we need + // This must always be done before the call to layout + measureView(viewRoot, null /*measuredView*/, + width, MeasureSpec.EXACTLY, + height, MeasureSpec.EXACTLY); + + // now do the layout. + viewRoot.layout(0, 0, width, height); + handleScrolling(context, viewRoot); + + if (canvas == null) { + return SUCCESS.createResult(); + } + + AttachInfo_Accessor.dispatchOnPreDraw(viewRoot); + viewRoot.draw(canvas); + + return SUCCESS.createResult(); + } + /** * Renders the scene. *

@@ -367,24 +395,12 @@ public class RenderSessionImpl extends RenderAction { } } - // measure again with the size we need - // This must always be done before the call to layout - measureView(mViewRoot, null /*measuredView*/, - mMeasuredScreenWidth, MeasureSpec.EXACTLY, - mMeasuredScreenHeight, MeasureSpec.EXACTLY); - - // now do the layout. - mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight); - - handleScrolling(mViewRoot); - + Result renderResult = SUCCESS.createResult(); if (params.isLayoutOnly()) { // delete the canvas and image to reset them on the next full rendering mImage = null; mCanvas = null; } else { - AttachInfo_Accessor.dispatchOnPreDraw(mViewRoot); - // draw the views // create the BufferedImage into which the layout will be rendered. boolean newImage = false; @@ -446,6 +462,9 @@ public class RenderSessionImpl extends RenderAction { if (mElapsedFrameTimeNanos >= 0) { long initialTime = System_Delegate.nanoTime(); if (!mFirstFrameExecuted) { + // We need to run an initial draw call to initialize the animations + render(getContext(), mViewRoot, mCanvas, 0, 0); + // The first frame will initialize the animations Choreographer_Delegate.doFrame(initialTime); mFirstFrameExecuted = true; @@ -453,14 +472,15 @@ public class RenderSessionImpl extends RenderAction { // Second frame will move the animations Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos); } - mViewRoot.draw(mCanvas); + renderResult = render(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth, + mMeasuredScreenHeight); } mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(), false); // success! - return SUCCESS.createResult(); + return renderResult; } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; @@ -488,7 +508,7 @@ public class RenderSessionImpl extends RenderAction { * @return the measured width/height if measuredView is non-null, null otherwise. */ @SuppressWarnings("deprecation") // For the use of Pair - private Pair measureView(ViewGroup viewToMeasure, View measuredView, + private static Pair measureView(ViewGroup viewToMeasure, View measuredView, int width, int widthMode, int height, int heightMode) { int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode); int h_spec = MeasureSpec.makeMeasureSpec(height, heightMode); @@ -1061,8 +1081,7 @@ public class RenderSessionImpl extends RenderAction { * the component supports nested scrolling attempt that first, then use the unconsumed scroll * part to scroll the content in the component. */ - private void handleScrolling(View view) { - BridgeContext context = getContext(); + private static void handleScrolling(BridgeContext context, View view) { int scrollPosX = context.getScrollXPos(view); int scrollPosY = context.getScrollYPos(view); if (scrollPosX != 0 || scrollPosY != 0) { @@ -1080,7 +1099,7 @@ public class RenderSessionImpl extends RenderAction { } } if (scrollPosX != 0 || scrollPosY != 0) { - view.scrollBy(scrollPosX, scrollPosY); + view.scrollTo(scrollPosX, scrollPosY); } } @@ -1090,7 +1109,7 @@ public class RenderSessionImpl extends RenderAction { ViewGroup group = (ViewGroup) view; for (int i = 0; i < group.getChildCount(); i++) { View child = group.getChildAt(i); - handleScrolling(child); + handleScrolling(context, child); } } diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml index a5ebc2e20847c..a07498cd07b14 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/scrolled.xml @@ -2,8 +2,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:scrollX="10px" - android:scrollY="30px"> + android:scrollX="30px" + android:scrollY="90px"> + android:scrollX="-90px" + android:scrollY="450px">