| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.graphics; |
| |
| import android.annotation.BytesLong; |
| import android.annotation.ColorInt; |
| import android.annotation.FloatRange; |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.graphics.animation.RenderNodeAnimator; |
| import android.view.NativeVectorDrawableAnimator; |
| import android.view.Surface; |
| import android.view.View; |
| |
| import com.android.internal.util.ArrayUtils; |
| |
| import dalvik.annotation.optimization.CriticalNative; |
| |
| import libcore.util.NativeAllocationRegistry; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.ref.WeakReference; |
| |
| /** |
| * <p>RenderNode is used to build hardware accelerated rendering hierarchies. Each RenderNode |
| * contains both a display list as well as a set of properties that affect the rendering of the |
| * display list. RenderNodes are used internally for all Views by default and are not typically |
| * used directly.</p> |
| * |
| * <p>RenderNodes are used to divide up the rendering content of a complex scene into smaller |
| * pieces that can then be updated individually more cheaply. Updating part of the scene only needs |
| * to update the display list or properties of a small number of RenderNode instead of redrawing |
| * everything from scratch. A RenderNode only needs its display list re-recorded when its content |
| * alone should be changed. RenderNodes can also be transformed without re-recording the display |
| * list through the transform properties.</p> |
| * |
| * <p>A text editor might for instance store each paragraph into its own RenderNode. |
| * Thus when the user inserts or removes characters, only the display list of the |
| * affected paragraph needs to be recorded again.</p> |
| * |
| * <h3>Hardware acceleration</h3> |
| * <p>RenderNodes can be drawn using a {@link RecordingCanvas}. They are not |
| * supported in software. Always make sure that the {@link android.graphics.Canvas} |
| * you are using to render a display list is hardware accelerated using |
| * {@link android.graphics.Canvas#isHardwareAccelerated()}.</p> |
| * |
| * <h3>Creating a RenderNode</h3> |
| * <pre class="prettyprint"> |
| * RenderNode renderNode = new RenderNode("myRenderNode"); |
| * renderNode.setPosition(0, 0, 50, 50); // Set the size to 50x50 |
| * RecordingCanvas canvas = renderNode.beginRecording(); |
| * try { |
| * // Draw with the canvas |
| * canvas.drawRect(...); |
| * } finally { |
| * renderNode.endRecording(); |
| * }</pre> |
| * |
| * <h3>Drawing a RenderNode in a View</h3> |
| * <pre class="prettyprint"> |
| * protected void onDraw(Canvas canvas) { |
| * if (canvas.isHardwareAccelerated()) { |
| * // Check that the RenderNode has a display list, re-recording it if it does not. |
| * if (!myRenderNode.hasDisplayList()) { |
| * updateDisplayList(myRenderNode); |
| * } |
| * // Draw the RenderNode into this canvas. |
| * canvas.drawRenderNode(myRenderNode); |
| * } |
| * }</pre> |
| * |
| * <h3>Releasing resources</h3> |
| * <p>This step is not mandatory but recommended if you want to release resources |
| * held by a display list as soon as possible. Most significantly any bitmaps it may contain.</p> |
| * <pre class="prettyprint"> |
| * // Discards the display list content allowing for any held resources to be released. |
| * // After calling this |
| * renderNode.discardDisplayList();</pre> |
| * |
| * |
| * <h3>Properties</h3> |
| * <p>In addition, a RenderNode offers several properties, such as |
| * {@link #setScaleX(float)} or {@link #setTranslationX(float)}, that can be used to affect all |
| * the drawing commands recorded within. For instance, these properties can be used |
| * to move around a large number of images without re-issuing all the individual |
| * <code>canvas.drawBitmap()</code> calls.</p> |
| * |
| * <pre class="prettyprint"> |
| * private void createDisplayList() { |
| * mRenderNode = new RenderNode("MyRenderNode"); |
| * mRenderNode.setPosition(0, 0, width, height); |
| * RecordingCanvas canvas = mRenderNode.beginRecording(); |
| * try { |
| * for (Bitmap b : mBitmaps) { |
| * canvas.drawBitmap(b, 0.0f, 0.0f, null); |
| * canvas.translate(0.0f, b.getHeight()); |
| * } |
| * } finally { |
| * mRenderNode.endRecording(); |
| * } |
| * } |
| * |
| * protected void onDraw(Canvas canvas) { |
| * if (canvas.isHardwareAccelerated()) |
| * canvas.drawRenderNode(mRenderNode); |
| * } |
| * } |
| * |
| * private void moveContentBy(int x) { |
| * // This will move all the bitmaps recorded inside the display list |
| * // by x pixels to the right and redraw this view. All the commands |
| * // recorded in createDisplayList() won't be re-issued, only onDraw() |
| * // will be invoked and will execute very quickly |
| * mRenderNode.offsetLeftAndRight(x); |
| * invalidate(); |
| * }</pre> |
| * |
| * <p>A few of the properties may at first appear redundant, such as {@link #setElevation(float)} |
| * and {@link #setTranslationZ(float)}. The reason for these duplicates are to allow for a |
| * separation between static & transient usages. For example consider a button that raises from 2dp |
| * to 8dp when pressed. To achieve that an application may decide to setElevation(2dip), and then |
| * on press to animate setTranslationZ to 6dip. Combined this achieves the final desired 8dip |
| * value, but the animation need only concern itself with animating the lift from press without |
| * needing to know the initial starting value. {@link #setTranslationX(float)} and |
| * {@link #setTranslationY(float)} are similarly provided for animation uses despite the functional |
| * overlap with {@link #setPosition(Rect)}. |
| * |
| * <p>The RenderNode's transform matrix is computed at render time as follows: |
| * <pre class="prettyprint"> |
| * Matrix transform = new Matrix(); |
| * transform.setTranslate(renderNode.getTranslationX(), renderNode.getTranslationY()); |
| * transform.preRotate(renderNode.getRotationZ(), |
| * renderNode.getPivotX(), renderNode.getPivotY()); |
| * transform.preScale(renderNode.getScaleX(), renderNode.getScaleY(), |
| * renderNode.getPivotX(), renderNode.getPivotY());</pre> |
| * The current canvas transform matrix, which is translated to the RenderNode's position, |
| * is then multiplied by the RenderNode's transform matrix. Therefore the ordering of calling |
| * property setters does not affect the result. That is to say that: |
| * |
| * <pre class="prettyprint"> |
| * renderNode.setTranslationX(100); |
| * renderNode.setScaleX(100);</pre> |
| * |
| * is equivalent to: |
| * |
| * <pre class="prettyprint"> |
| * renderNode.setScaleX(100); |
| * renderNode.setTranslationX(100);</pre> |
| * |
| * <h3>Threading</h3> |
| * <p>RenderNode may be created and used on any thread but they are not thread-safe. Only |
| * a single thread may interact with a RenderNode at any given time. It is critical |
| * that the RenderNode is only used on the same thread it is drawn with. For example when using |
| * RenderNode with a custom View, then that RenderNode must only be used from the UI thread.</p> |
| * |
| * <h3>When to re-render</h3> |
| * <p>Many of the RenderNode mutation methods, such as {@link #setTranslationX(float)}, return |
| * a boolean indicating if the value actually changed or not. This is useful in detecting |
| * if a new frame should be rendered or not. A typical usage would look like: |
| * <pre class="prettyprint"> |
| * public void translateTo(int x, int y) { |
| * boolean needsUpdate = myRenderNode.setTranslationX(x); |
| * needsUpdate |= myRenderNode.setTranslationY(y); |
| * if (needsUpdate) { |
| * myOwningView.invalidate(); |
| * } |
| * }</pre> |
| * This is marginally faster than doing a more explicit up-front check if the value changed by |
| * comparing the desired value against {@link #getTranslationX()} as it minimizes JNI transitions. |
| * The actual mechanism of requesting a new frame to be rendered will depend on how this |
| * RenderNode is being drawn. If it's drawn to a containing View, as in the above snippet, |
| * then simply invalidating that View works. If instead the RenderNode is being drawn to a Canvas |
| * directly such as with {@link Surface#lockHardwareCanvas()} then a new frame needs to be drawn |
| * by calling {@link Surface#lockHardwareCanvas()}, re-drawing the root RenderNode or whatever |
| * top-level content is desired, and finally calling {@link Surface#unlockCanvasAndPost(Canvas)}. |
| * </p> |
| */ |
| public final class RenderNode { |
| |
| // Use a Holder to allow static initialization in the boot image. |
| private static class NoImagePreloadHolder { |
| public static final NativeAllocationRegistry sRegistry = |
| NativeAllocationRegistry.createMalloced( |
| RenderNode.class.getClassLoader(), nGetNativeFinalizer()); |
| } |
| |
| /** |
| * Not for general use; use only if you are ThreadedRenderer or RecordingCanvas. |
| * |
| * @hide |
| */ |
| public final long mNativeRenderNode; |
| private final AnimationHost mAnimationHost; |
| private RecordingCanvas mCurrentRecordingCanvas; |
| |
| // Will be null if not currently registered |
| @Nullable |
| private CompositePositionUpdateListener mCompositePositionUpdateListener; |
| |
| /** |
| * Creates a new RenderNode that can be used to record batches of |
| * drawing operations, and store / apply render properties when drawn. |
| * |
| * @param name The name of the RenderNode, used for debugging purpose. May be null. |
| */ |
| public RenderNode(@Nullable String name) { |
| this(name, null); |
| } |
| |
| private RenderNode(String name, AnimationHost animationHost) { |
| mNativeRenderNode = nCreate(name); |
| NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode); |
| mAnimationHost = animationHost; |
| } |
| |
| /** |
| * @see RenderNode#adopt(long) |
| */ |
| private RenderNode(long nativePtr) { |
| mNativeRenderNode = nativePtr; |
| NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode); |
| mAnimationHost = null; |
| } |
| |
| /** @hide */ |
| public static RenderNode create(String name, @Nullable AnimationHost animationHost) { |
| return new RenderNode(name, animationHost); |
| } |
| |
| /** |
| * Adopts an existing native render node. |
| * |
| * Note: This will *NOT* incRef() on the native object, however it will |
| * decRef() when it is destroyed. The caller should have already incRef'd it |
| * |
| * @hide |
| */ |
| public static RenderNode adopt(long nativePtr) { |
| return new RenderNode(nativePtr); |
| } |
| |
| /** |
| * Listens for RenderNode position updates for synchronous window movement. |
| * |
| * This is not suitable for generic position listening, it is only designed & intended |
| * for use by things which require external position events like SurfaceView, PopupWindow, etc.. |
| * |
| * @hide |
| */ |
| public interface PositionUpdateListener { |
| /** |
| * Called by native by a Rendering Worker thread to update window position |
| * |
| * @hide |
| */ |
| void positionChanged(long frameNumber, int left, int top, int right, int bottom); |
| |
| /** |
| * Called by native by a Rendering Worker thread to update window position; includes |
| * the local rect that represents the clipped area of the RenderNode's bounds. |
| * |
| * @hide |
| */ |
| default void positionChanged(long frameNumber, int left, int top, int right, int bottom, |
| int clipLeft, int clipTop, int clipRight, int clipBottom) { |
| positionChanged(frameNumber, left, top, right, bottom); |
| } |
| |
| /** |
| * Called by JNI |
| * |
| * @hide */ |
| static boolean callPositionChanged(WeakReference<PositionUpdateListener> weakListener, |
| long frameNumber, int left, int top, int right, int bottom) { |
| final PositionUpdateListener listener = weakListener.get(); |
| if (listener != null) { |
| listener.positionChanged(frameNumber, left, top, right, bottom); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Called by JNI |
| * |
| * @hide */ |
| static boolean callPositionChanged2(WeakReference<PositionUpdateListener> weakListener, |
| long frameNumber, int left, int top, int right, int bottom, |
| int clipLeft, int clipTop, int clipRight, int clipBottom) { |
| final PositionUpdateListener listener = weakListener.get(); |
| if (listener != null) { |
| listener.positionChanged(frameNumber, left, top, right, bottom, clipLeft, |
| clipTop, clipRight, clipBottom); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Call to apply a stretch effect to any child SurfaceControl layers |
| * |
| * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls? |
| * (njawad) update to consume different stretch parameters for horizontal/vertical stretch |
| * to ensure SkiaGLRenderEngine can also apply the same stretch to a surface |
| * |
| * @hide |
| */ |
| default void applyStretch(long frameNumber, float width, float height, |
| float vecX, float vecY, |
| float maxStretchX, float maxStretchY, float childRelativeLeft, |
| float childRelativeTop, float childRelativeRight, float childRelativeBottom) { } |
| |
| /** |
| * Called by JNI |
| * |
| * @hide */ |
| static boolean callApplyStretch(WeakReference<PositionUpdateListener> weakListener, |
| long frameNumber, float width, float height, |
| float vecX, float vecY, |
| float maxStretchX, float maxStretchY, float childRelativeLeft, |
| float childRelativeTop, float childRelativeRight, float childRelativeBottom) { |
| final PositionUpdateListener listener = weakListener.get(); |
| if (listener != null) { |
| listener.applyStretch(frameNumber, width, height, vecX, vecY, maxStretchX, |
| maxStretchY, childRelativeLeft, childRelativeTop, childRelativeRight, |
| childRelativeBottom); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * Called by native on RenderThread to notify that the view is no longer in the |
| * draw tree. UI thread is blocked at this point. |
| * |
| * @hide |
| */ |
| void positionLost(long frameNumber); |
| |
| /** |
| * Called by JNI |
| * |
| * @hide */ |
| static boolean callPositionLost(WeakReference<PositionUpdateListener> weakListener, |
| long frameNumber) { |
| final PositionUpdateListener listener = weakListener.get(); |
| if (listener != null) { |
| listener.positionLost(frameNumber); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| } |
| |
| private static final class CompositePositionUpdateListener implements PositionUpdateListener { |
| private final PositionUpdateListener[] mListeners; |
| private static final PositionUpdateListener[] sEmpty = new PositionUpdateListener[0]; |
| |
| CompositePositionUpdateListener(PositionUpdateListener... listeners) { |
| mListeners = listeners != null ? listeners : sEmpty; |
| } |
| |
| public CompositePositionUpdateListener with(PositionUpdateListener listener) { |
| return new CompositePositionUpdateListener( |
| ArrayUtils.appendElement(PositionUpdateListener.class, mListeners, listener)); |
| } |
| |
| public CompositePositionUpdateListener without(PositionUpdateListener listener) { |
| return new CompositePositionUpdateListener( |
| ArrayUtils.removeElement(PositionUpdateListener.class, mListeners, listener)); |
| } |
| |
| @Override |
| public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { |
| for (PositionUpdateListener pul : mListeners) { |
| pul.positionChanged(frameNumber, left, top, right, bottom); |
| } |
| } |
| |
| @Override |
| public void positionChanged(long frameNumber, int left, int top, int right, int bottom, |
| int clipLeft, int clipTop, int clipRight, int clipBottom) { |
| for (PositionUpdateListener pul : mListeners) { |
| pul.positionChanged(frameNumber, left, top, right, bottom, clipLeft, clipTop, |
| clipRight, clipBottom); |
| } |
| } |
| |
| @Override |
| public void positionLost(long frameNumber) { |
| for (PositionUpdateListener pul : mListeners) { |
| pul.positionLost(frameNumber); |
| } |
| } |
| |
| @Override |
| public void applyStretch(long frameNumber, float width, float height, |
| float vecX, float vecY, float maxStretchX, float maxStretchY, float childRelativeLeft, |
| float childRelativeTop, float childRelativeRight, float childRelativeBottom) { |
| for (PositionUpdateListener pul : mListeners) { |
| pul.applyStretch(frameNumber, width, height, vecX, vecY, maxStretchX, |
| maxStretchY, childRelativeLeft, childRelativeTop, childRelativeRight, |
| childRelativeBottom); |
| } |
| } |
| } |
| |
| /** |
| * Enable callbacks for position changes. Call only from the UI thread or with |
| * external synchronization. |
| * |
| * @hide |
| */ |
| public void addPositionUpdateListener(@NonNull PositionUpdateListener listener) { |
| CompositePositionUpdateListener comp = mCompositePositionUpdateListener; |
| if (comp == null) { |
| comp = new CompositePositionUpdateListener(listener); |
| } else { |
| comp = comp.with(listener); |
| } |
| mCompositePositionUpdateListener = comp; |
| nRequestPositionUpdates(mNativeRenderNode, new WeakReference<>(comp)); |
| } |
| |
| /** |
| * Disable a callback for position changes. Call only from the UI thread or with |
| * external synchronization. |
| * |
| * @param listener Callback to remove |
| * @hide |
| */ |
| public void removePositionUpdateListener(@NonNull PositionUpdateListener listener) { |
| CompositePositionUpdateListener comp = mCompositePositionUpdateListener; |
| if (comp != null) { |
| comp = comp.without(listener); |
| mCompositePositionUpdateListener = comp; |
| nRequestPositionUpdates(mNativeRenderNode, new WeakReference<>(comp)); |
| } |
| } |
| |
| /** |
| * Starts recording a display list for the render node. All |
| * operations performed on the returned canvas are recorded and |
| * stored in this display list. |
| * |
| * {@link #endRecording()} must be called when the recording is finished in order to apply |
| * the updated display list. Failing to call {@link #endRecording()} will result in an |
| * {@link IllegalStateException} if {@link #beginRecording(int, int)} is called again. |
| * |
| * @param width The width of the recording viewport. This will not alter the width of the |
| * RenderNode itself, that must be set with {@link #setPosition(Rect)}. |
| * @param height The height of the recording viewport. This will not alter the height of the |
| * RenderNode itself, that must be set with {@link #setPosition(Rect)}. |
| * @return A canvas to record drawing operations. |
| * @throws IllegalStateException If a recording is already in progress. That is, the previous |
| * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}. |
| * @see #endRecording() |
| * @see #hasDisplayList() |
| */ |
| public @NonNull RecordingCanvas beginRecording(int width, int height) { |
| if (mCurrentRecordingCanvas != null) { |
| throw new IllegalStateException( |
| "Recording currently in progress - missing #endRecording() call?"); |
| } |
| mCurrentRecordingCanvas = RecordingCanvas.obtain(this, width, height); |
| return mCurrentRecordingCanvas; |
| } |
| |
| /** |
| * Same as {@link #beginRecording(int, int)} with the width & height set |
| * to the RenderNode's own width & height. The RenderNode's width & height may be set |
| * with {@link #setPosition(int, int, int, int)}. |
| * |
| * @return A canvas to record drawing operations. |
| * @throws IllegalStateException If a recording is already in progress. That is, the previous |
| * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}. |
| * @see #endRecording() |
| * @see #hasDisplayList() |
| */ |
| public @NonNull RecordingCanvas beginRecording() { |
| return beginRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode)); |
| } |
| |
| /** |
| * ` |
| * Ends the recording for this display list. Calling this method marks |
| * the display list valid and {@link #hasDisplayList()} will return true. |
| * |
| * @see #beginRecording(int, int) |
| * @see #hasDisplayList() |
| */ |
| public void endRecording() { |
| if (mCurrentRecordingCanvas == null) { |
| throw new IllegalStateException( |
| "No recording in progress, forgot to call #beginRecording()?"); |
| } |
| RecordingCanvas canvas = mCurrentRecordingCanvas; |
| mCurrentRecordingCanvas = null; |
| canvas.finishRecording(this); |
| canvas.recycle(); |
| } |
| |
| /** |
| * @hide |
| * @deprecated use {@link #beginRecording(int, int)} instead |
| */ |
| @Deprecated |
| public RecordingCanvas start(int width, int height) { |
| return beginRecording(width, height); |
| } |
| |
| /** |
| * @hide |
| * @deprecated use {@link #endRecording()} instead |
| */ |
| @Deprecated |
| public void end(RecordingCanvas canvas) { |
| if (canvas != mCurrentRecordingCanvas) { |
| throw new IllegalArgumentException("Wrong canvas"); |
| } |
| endRecording(); |
| } |
| |
| /** |
| * Reset native resources. This is called when cleaning up the state of display lists |
| * during destruction of hardware resources, to ensure that we do not hold onto |
| * obsolete resources after related resources are gone. |
| */ |
| public void discardDisplayList() { |
| nDiscardDisplayList(mNativeRenderNode); |
| } |
| |
| /** |
| * Returns whether the RenderNode has a display list. If this returns false, the RenderNode |
| * should be re-recorded with {@link #beginRecording()} and {@link #endRecording()}. |
| * |
| * A RenderNode without a display list may still be drawn, however it will have no impact |
| * on the rendering content until its display list is updated. |
| * |
| * When a RenderNode is no longer drawn by anything the system may automatically |
| * invoke {@link #discardDisplayList()}. It is therefore important to ensure that |
| * {@link #hasDisplayList()} is true on a RenderNode prior to drawing it. |
| * |
| * See {@link #discardDisplayList()} |
| * |
| * @return boolean true if this RenderNode has a display list, false otherwise. |
| */ |
| public boolean hasDisplayList() { |
| return nIsValid(mNativeRenderNode); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Matrix manipulation |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Whether or not the RenderNode has an identity transform. This is a faster |
| * way to do the otherwise equivalent {@link #getMatrix(Matrix)} {@link Matrix#isIdentity()} |
| * as it doesn't require copying the Matrix first, thus minimizing overhead. |
| * |
| * @return true if the RenderNode has an identity transform, false otherwise |
| */ |
| public boolean hasIdentityMatrix() { |
| return nHasIdentityMatrix(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the current transform matrix |
| * |
| * @param outMatrix The matrix to store the transform of the RenderNode |
| */ |
| public void getMatrix(@NonNull Matrix outMatrix) { |
| nGetTransformMatrix(mNativeRenderNode, outMatrix.ni()); |
| } |
| |
| /** |
| * Gets the current transform inverted. This is a faster way to do the otherwise |
| * equivalent {@link #getMatrix(Matrix)} followed by {@link Matrix#invert(Matrix)} |
| * |
| * @param outMatrix The matrix to store the inverse transform of the RenderNode |
| */ |
| public void getInverseMatrix(@NonNull Matrix outMatrix) { |
| nGetInverseTransformMatrix(mNativeRenderNode, outMatrix.ni()); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // RenderProperty Setters |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * @hide |
| * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead |
| */ |
| @Deprecated |
| public boolean setLayerType(int layerType) { |
| return nSetLayerType(mNativeRenderNode, layerType); |
| } |
| |
| /** |
| * @hide |
| * @deprecated use {@link #setUseCompositingLayer(boolean, Paint)} instead |
| */ |
| @Deprecated |
| public boolean setLayerPaint(@Nullable Paint paint) { |
| return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0); |
| } |
| |
| /** |
| * Controls whether or not to force this RenderNode to render to an intermediate buffer. |
| * Internally RenderNode will already promote itself to a composition layer if it's useful |
| * for performance or required for the current combination of {@link #setAlpha(float)} and |
| * {@link #setHasOverlappingRendering(boolean)}. |
| * |
| * <p>The usage of this is instead to allow for either overriding of the internal behavior |
| * if it's measured to be necessary for the particular rendering content in question or, more |
| * usefully, to add a composition effect to the RenderNode via the optional paint parameter. |
| * |
| * <p>Note: When a RenderNode is using a compositing layer it will also result in |
| * clipToBounds=true behavior. |
| * |
| * @param forceToLayer if true this forces the RenderNode to use an intermediate buffer. |
| * Default & generally recommended value is false. |
| * @param paint The blend mode, alpha, and ColorFilter to apply to the compositing layer. |
| * Only applies if forceToLayer is true. The paint's alpha is multiplied |
| * with {@link #getAlpha()} to resolve the final alpha of the RenderNode. |
| * If null then no additional composition effects are applied on top of the |
| * composition layer. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) { |
| boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0); |
| didChange |= nSetLayerPaint(mNativeRenderNode, |
| paint != null ? paint.getNativeInstance() : 0); |
| return didChange; |
| } |
| |
| /** |
| * Gets whether or not a compositing layer is forced to be used. The default & recommended |
| * is false, as it is typically faster to avoid using compositing layers. |
| * See {@link #setUseCompositingLayer(boolean, Paint)}. |
| * |
| * @return true if a compositing layer is forced, false otherwise |
| */ |
| public boolean getUseCompositingLayer() { |
| return nGetLayerType(mNativeRenderNode) != 0; |
| } |
| |
| /** |
| * Sets an additional clip on the RenderNode. If null, the extra clip is removed from the |
| * RenderNode. If non-null, the RenderNode will be clipped to this rect. In addition if |
| * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the |
| * intersection of this rectangle and the bounds of the render node, which is set with |
| * {@link #setPosition(Rect)}. |
| * |
| * <p>This is equivalent to do a {@link Canvas#clipRect(Rect)} at the start of this |
| * RenderNode's display list. However, as this is a property of the RenderNode instead |
| * of part of the display list it can be more easily animated for transient additional |
| * clipping. An example usage of this would be the {@link android.transition.ChangeBounds} |
| * transition animation with the resizeClip=true option. |
| * |
| * @param rect the bounds to clip to. If null, the additional clip is removed. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setClipRect(@Nullable Rect rect) { |
| if (rect == null) { |
| return nSetClipBoundsEmpty(mNativeRenderNode); |
| } else { |
| return nSetClipBounds(mNativeRenderNode, rect.left, rect.top, rect.right, rect.bottom); |
| } |
| } |
| |
| /** |
| * Set whether the Render node should clip itself to its bounds. This defaults to true, |
| * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as |
| * better partial invalidation support. Clipping can be further restricted or controlled |
| * through the combination of this property as well as {@link #setClipRect(Rect)}, which |
| * allows for a different clipping rectangle to be used in addition to or instead of the |
| * {@link #setPosition(int, int, int, int)} or the RenderNode. |
| * |
| * @param clipToBounds true if the display list should clip to its bounds, false otherwise. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setClipToBounds(boolean clipToBounds) { |
| return nSetClipToBounds(mNativeRenderNode, clipToBounds); |
| } |
| |
| /** |
| * Returns whether or not the RenderNode is clipping to its bounds. See |
| * {@link #setClipToBounds(boolean)} and {@link #setPosition(int, int, int, int)} |
| * |
| * @return true if the render node clips to its bounds, false otherwise. |
| */ |
| public boolean getClipToBounds() { |
| return nGetClipToBounds(mNativeRenderNode); |
| } |
| |
| /** |
| * <p>Sets whether the RenderNode should be drawn immediately after the |
| * closest ancestor RenderNode containing a projection receiver. |
| * |
| * <p>The default is false, and the rendering of this node happens in the typical draw order. |
| * |
| * <p>If true, then at rendering time this rendernode will not be drawn in order with the |
| * {@link Canvas#drawRenderNode(RenderNode)} command that drew this RenderNode, but instead |
| * it will be re-positioned in the RenderNode tree to be drawn on the closet ancestor with a |
| * child rendernode that has {@link #setProjectionReceiver(boolean)} as true. |
| * |
| * <p>The typical usage of this is to allow a child RenderNode to draw on a parent's background, |
| * such as the platform's usage with {@link android.graphics.drawable.RippleDrawable}. Consider |
| * the following structure, built out of which RenderNode called drawRenderNode on a different |
| * RenderNode: |
| * |
| * <pre> |
| * +-------------+ |
| * |RenderNode: P| |
| * +-+----------++ |
| * | | |
| * v v |
| * +-------+-----+ +-+--------------+ |
| * |RenderNode: C| |RenderNode: P'BG| |
| * +-------+-----+ +----------------+ |
| * | |
| * | |
| * +--------+-------+ |
| * |RenderNode: C'BG| |
| * +----------------+ |
| * </pre> |
| * |
| * If P'BG is a projection receiver, and C'BG is set to project backwards then C'BG will |
| * behave as if it was drawn directly by P'BG instead of by C. This includes inheriting P'BG's |
| * clip instead of C's clip. |
| * |
| * @param shouldProject true if the display list should be projected onto a |
| * containing volume. Default is false. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setProjectBackwards(boolean shouldProject) { |
| return nSetProjectBackwards(mNativeRenderNode, shouldProject); |
| } |
| |
| /** |
| * Sets whether the RenderNode is a projection receiver. If true then this RenderNode's parent |
| * should draw any descendant RenderNodes with ProjectBackwards=true directly on top of it. |
| * Default value is false. See |
| * {@link #setProjectBackwards(boolean)} for a description of what this entails. |
| * |
| * @param shouldReceive True if this RenderNode is a projection receiver, false otherwise. |
| * Default is false. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setProjectionReceiver(boolean shouldReceive) { |
| return nSetProjectionReceiver(mNativeRenderNode, shouldReceive); |
| } |
| |
| /** |
| * Sets the outline, defining the shape that casts a shadow, and the path to |
| * be clipped if setClipToOutline is set. |
| * |
| * This will make a copy of the provided {@link Outline}, so any future modifications |
| * to the outline will need to call {@link #setOutline(Outline)} with the modified |
| * outline for those changes to be applied. |
| * |
| * @param outline The outline to use for this RenderNode. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setOutline(@Nullable Outline outline) { |
| if (outline == null) { |
| return nSetOutlineNone(mNativeRenderNode); |
| } |
| |
| switch (outline.mMode) { |
| case Outline.MODE_EMPTY: |
| return nSetOutlineEmpty(mNativeRenderNode); |
| case Outline.MODE_ROUND_RECT: |
| return nSetOutlineRoundRect(mNativeRenderNode, |
| outline.mRect.left, outline.mRect.top, |
| outline.mRect.right, outline.mRect.bottom, |
| outline.mRadius, outline.mAlpha); |
| case Outline.MODE_PATH: |
| return nSetOutlinePath(mNativeRenderNode, outline.mPath.mNativePath, |
| outline.mAlpha); |
| } |
| |
| throw new IllegalArgumentException("Unrecognized outline?"); |
| } |
| |
| /** @hide */ |
| public boolean clearStretch() { |
| return nClearStretch(mNativeRenderNode); |
| } |
| |
| /** @hide */ |
| public boolean stretch(float vecX, float vecY, |
| float maxStretchAmountX, float maxStretchAmountY) { |
| if (Float.isInfinite(vecX) || Float.isNaN(vecX)) { |
| throw new IllegalArgumentException("vecX must be a finite, non-NaN value " + vecX); |
| } |
| if (Float.isInfinite(vecY) || Float.isNaN(vecY)) { |
| throw new IllegalArgumentException("vecY must be a finite, non-NaN value " + vecY); |
| } |
| |
| if (maxStretchAmountX <= 0.0f) { |
| throw new IllegalArgumentException( |
| "The max horizontal stretch amount must be >0, got " + maxStretchAmountX); |
| } |
| if (maxStretchAmountY <= 0.0f) { |
| throw new IllegalArgumentException( |
| "The max vertical stretch amount must be >0, got " + maxStretchAmountY); |
| } |
| return nStretch( |
| mNativeRenderNode, |
| vecX, |
| vecY, |
| maxStretchAmountX, |
| maxStretchAmountY |
| ); |
| } |
| |
| /** |
| * Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()} |
| * and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with |
| * a valid shadow caster path, and the provided outline has a non-zero |
| * {@link Outline#getAlpha()}. |
| * |
| * @return True if this RenderNode has a shadow, false otherwise |
| */ |
| public boolean hasShadow() { |
| return nHasShadow(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the color of the spot shadow that is drawn when the RenderNode has a positive Z or |
| * elevation value and is drawn inside of a {@link Canvas#enableZ()} section. |
| * <p> |
| * By default the shadow color is black. Generally, this color will be opaque so the intensity |
| * of the shadow is consistent between different RenderNodes with different colors. |
| * <p> |
| * The opacity of the final spot shadow is a function of the shadow caster height, the |
| * alpha channel of the outlineSpotShadowColor (typically opaque), and the |
| * {@link android.R.attr#spotShadowAlpha} theme attribute |
| * |
| * @param color The color this RenderNode will cast for its elevation spot shadow. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setSpotShadowColor(@ColorInt int color) { |
| return nSetSpotShadowColor(mNativeRenderNode, color); |
| } |
| |
| /** |
| * @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing |
| * was set |
| */ |
| public @ColorInt int getSpotShadowColor() { |
| return nGetSpotShadowColor(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the color of the ambient shadow that is drawn when the RenderNode has a positive Z or |
| * elevation value and is drawn inside of a {@link Canvas#enableZ()} section. |
| * <p> |
| * By default the shadow color is black. Generally, this color will be opaque so the intensity |
| * of the shadow is consistent between different RenderNodes with different colors. |
| * <p> |
| * The opacity of the final ambient shadow is a function of the shadow caster height, the |
| * alpha channel of the outlineAmbientShadowColor (typically opaque), and the |
| * {@link android.R.attr#ambientShadowAlpha} theme attribute. |
| * |
| * @param color The color this RenderNode will cast for its elevation shadow. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setAmbientShadowColor(@ColorInt int color) { |
| return nSetAmbientShadowColor(mNativeRenderNode, color); |
| } |
| |
| /** |
| * @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if |
| * nothing was set |
| */ |
| public @ColorInt int getAmbientShadowColor() { |
| return nGetAmbientShadowColor(mNativeRenderNode); |
| } |
| |
| /** |
| * Enables or disables clipping to the outline. |
| * |
| * @param clipToOutline true if clipping to the outline. |
| * @return True if the clipToOutline value changed, false if previous value matched the new |
| * value. |
| */ |
| public boolean setClipToOutline(boolean clipToOutline) { |
| return nSetClipToOutline(mNativeRenderNode, clipToOutline); |
| } |
| |
| /** |
| * See {@link #setClipToOutline(boolean)} |
| * |
| * @return True if this RenderNode clips to its outline, false otherwise |
| */ |
| public boolean getClipToOutline() { |
| return nGetClipToOutline(mNativeRenderNode); |
| } |
| |
| /** |
| * Controls the RenderNode's circular reveal clip. |
| * |
| * @hide |
| */ |
| public boolean setRevealClip(boolean shouldClip, |
| float x, float y, float radius) { |
| return nSetRevealClip(mNativeRenderNode, shouldClip, x, y, radius); |
| } |
| |
| /** |
| * Set the static matrix on the display list. The specified matrix is combined with other |
| * transforms (such as {@link #setScaleX(float)}, {@link #setRotationZ(float)}, etc.) |
| * |
| * @param matrix A transform matrix to apply to this display list |
| * @hide TODO Do we want this? |
| */ |
| public boolean setStaticMatrix(Matrix matrix) { |
| return nSetStaticMatrix(mNativeRenderNode, matrix.ni()); |
| } |
| |
| /** |
| * Set the Animation matrix on the display list. This matrix exists if an Animation is |
| * currently playing on a View, and is set on the display list during at draw() time. When |
| * the Animation finishes, the matrix should be cleared by sending <code>null</code> |
| * for the matrix parameter. |
| * |
| * @param matrix The matrix, null indicates that the matrix should be cleared. |
| * @see #getAnimationMatrix() |
| * |
| * @hide TODO Do we want this? |
| */ |
| public boolean setAnimationMatrix(@Nullable Matrix matrix) { |
| return nSetAnimationMatrix(mNativeRenderNode, |
| (matrix != null) ? matrix.ni() : 0); |
| } |
| |
| /** |
| * Returns the previously set Animation matrix. This matrix exists if an Animation is |
| * currently playing on a View, and is set on the display list during at draw() time. |
| * Returns <code>null</code> when there is no transformation provided by |
| * {@link #setAnimationMatrix(Matrix)}. |
| * |
| * @return the current Animation matrix. |
| * @see #setAnimationMatrix(Matrix) |
| * |
| * @hide |
| */ |
| @Nullable |
| public Matrix getAnimationMatrix() { |
| Matrix output = new Matrix(); |
| if (nGetAnimationMatrix(mNativeRenderNode, output.ni())) { |
| return output; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Sets the translucency level for the display list. |
| * |
| * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f |
| * @see View#setAlpha(float) |
| * @see #getAlpha() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setAlpha(float alpha) { |
| return nSetAlpha(mNativeRenderNode, alpha); |
| } |
| |
| /** |
| * Configure the {@link android.graphics.RenderEffect} to apply to this RenderNode. This |
| * will apply a visual effect to the end result of the contents of this RenderNode before |
| * it is drawn into the destination. For example if |
| * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)} |
| * is provided, the contents will be drawn in a separate layer, then this layer will |
| * be blurred when this RenderNode is drawn into the destination. |
| * @param renderEffect to be applied to the RenderNode. Passing null clears all previously |
| * configured RenderEffects |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setRenderEffect(@Nullable RenderEffect renderEffect) { |
| return nSetRenderEffect(mNativeRenderNode, |
| renderEffect != null ? renderEffect.getNativeInstance() : 0); |
| } |
| |
| /** |
| * Configure the {@link android.graphics.RenderEffect} to apply to the backdrop contents of |
| * this RenderNode. This will apply a visual effect to the result of the backdrop contents |
| * of this RenderNode before the RenderNode is drawn into the destination. For example if |
| * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)} |
| * is provided, the previous content behind this RenderNode will be blurred before the |
| * RenderNode is drawn in to the destination. |
| * @param renderEffect to be applied to the backdrop contents of this RenderNode. Passing |
| * null clears all previously configured RenderEffects |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| * @hide |
| */ |
| public boolean setBackdropRenderEffect(@Nullable RenderEffect renderEffect) { |
| return nSetBackdropRenderEffect(mNativeRenderNode, |
| renderEffect != null ? renderEffect.getNativeInstance() : 0); |
| } |
| |
| /** |
| * Returns the translucency level of this display list. |
| * |
| * @return A value between 0.0f and 1.0f |
| * @see #setAlpha(float) |
| */ |
| public float getAlpha() { |
| return nGetAlpha(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets whether the display list renders content which overlaps. Non-overlapping rendering |
| * can use a fast path for alpha that avoids rendering to an offscreen buffer. By default |
| * display lists consider they do not have overlapping content. |
| * |
| * @param hasOverlappingRendering False if the content is guaranteed to be non-overlapping, |
| * true otherwise. |
| * @see android.view.View#hasOverlappingRendering() |
| * @see #hasOverlappingRendering() |
| */ |
| public boolean setHasOverlappingRendering(boolean hasOverlappingRendering) { |
| return nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering); |
| } |
| |
| /** @hide */ |
| @IntDef({USAGE_BACKGROUND}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface UsageHint { |
| } |
| |
| /** |
| * The default usage hint |
| * |
| * @hide |
| */ |
| public static final int USAGE_UNKNOWN = 0; |
| |
| /** |
| * Usage is background content |
| * |
| * @hide |
| */ |
| public static final int USAGE_BACKGROUND = 1; |
| |
| /** |
| * Provides a hint on what this RenderNode's display list content contains. This hint is used |
| * for automatic content transforms to improve accessibility or similar. |
| * |
| * @hide |
| */ |
| public void setUsageHint(@UsageHint int usageHint) { |
| nSetUsageHint(mNativeRenderNode, usageHint); |
| } |
| |
| /** |
| * Indicates whether the content of this display list overlaps. |
| * |
| * @return True if this display list renders content which overlaps, false otherwise. |
| * @see #setHasOverlappingRendering(boolean) |
| */ |
| public boolean hasOverlappingRendering() { |
| return nHasOverlappingRendering(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the base elevation of this RenderNode in pixels |
| * |
| * @param lift the elevation in pixels |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setElevation(float lift) { |
| return nSetElevation(mNativeRenderNode, lift); |
| } |
| |
| /** |
| * See {@link #setElevation(float)} |
| * |
| * @return The RenderNode's current elevation |
| */ |
| public float getElevation() { |
| return nGetElevation(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the translation value for the display list on the X axis. |
| * |
| * @param translationX The X axis translation value of the display list, in pixels |
| * @see View#setTranslationX(float) |
| * @see #getTranslationX() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setTranslationX(float translationX) { |
| return nSetTranslationX(mNativeRenderNode, translationX); |
| } |
| |
| /** |
| * Returns the translation value for this display list on the X axis, in pixels. |
| * |
| * @see #setTranslationX(float) |
| */ |
| public float getTranslationX() { |
| return nGetTranslationX(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the translation value for the display list on the Y axis. |
| * |
| * @param translationY The Y axis translation value of the display list, in pixels |
| * @see View#setTranslationY(float) |
| * @see #getTranslationY() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setTranslationY(float translationY) { |
| return nSetTranslationY(mNativeRenderNode, translationY); |
| } |
| |
| /** |
| * Returns the translation value for this display list on the Y axis, in pixels. |
| * |
| * @see #setTranslationY(float) |
| */ |
| public float getTranslationY() { |
| return nGetTranslationY(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the translation value for the display list on the Z axis. |
| * |
| * @see View#setTranslationZ(float) |
| * @see #getTranslationZ() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setTranslationZ(float translationZ) { |
| return nSetTranslationZ(mNativeRenderNode, translationZ); |
| } |
| |
| /** |
| * Returns the translation value for this display list on the Z axis. |
| * |
| * @see #setTranslationZ(float) |
| */ |
| public float getTranslationZ() { |
| return nGetTranslationZ(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the rotation value for the display list around the Z axis. |
| * |
| * @param rotation The rotation value of the display list, in degrees |
| * @see View#setRotation(float) |
| * @see #getRotationZ() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setRotationZ(float rotation) { |
| return nSetRotation(mNativeRenderNode, rotation); |
| } |
| |
| /** |
| * Returns the rotation value for this display list around the Z axis, in degrees. |
| * |
| * @see #setRotationZ(float) |
| */ |
| public float getRotationZ() { |
| return nGetRotation(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the rotation value for the display list around the X axis. |
| * |
| * @param rotationX The rotation value of the display list, in degrees |
| * @see View#setRotationX(float) |
| * @see #getRotationX() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setRotationX(float rotationX) { |
| return nSetRotationX(mNativeRenderNode, rotationX); |
| } |
| |
| /** |
| * Returns the rotation value for this display list around the X axis, in degrees. |
| * |
| * @see #setRotationX(float) |
| */ |
| public float getRotationX() { |
| return nGetRotationX(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the rotation value for the display list around the Y axis. |
| * |
| * @param rotationY The rotation value of the display list, in degrees |
| * @see View#setRotationY(float) |
| * @see #getRotationY() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setRotationY(float rotationY) { |
| return nSetRotationY(mNativeRenderNode, rotationY); |
| } |
| |
| /** |
| * Returns the rotation value for this display list around the Y axis, in degrees. |
| * |
| * @see #setRotationY(float) |
| */ |
| public float getRotationY() { |
| return nGetRotationY(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the scale value for the display list on the X axis. |
| * |
| * @param scaleX The scale value of the display list |
| * @see View#setScaleX(float) |
| * @see #getScaleX() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setScaleX(float scaleX) { |
| return nSetScaleX(mNativeRenderNode, scaleX); |
| } |
| |
| /** |
| * Returns the scale value for this display list on the X axis. |
| * |
| * @see #setScaleX(float) |
| */ |
| public float getScaleX() { |
| return nGetScaleX(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the scale value for the display list on the Y axis. |
| * |
| * @param scaleY The scale value of the display list |
| * @see View#setScaleY(float) |
| * @see #getScaleY() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setScaleY(float scaleY) { |
| return nSetScaleY(mNativeRenderNode, scaleY); |
| } |
| |
| /** |
| * Returns the scale value for this display list on the Y axis. |
| * |
| * @see #setScaleY(float) |
| */ |
| public float getScaleY() { |
| return nGetScaleY(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the pivot value for the display list on the X axis |
| * |
| * @param pivotX The pivot value of the display list on the X axis, in pixels |
| * @see View#setPivotX(float) |
| * @see #getPivotX() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setPivotX(float pivotX) { |
| return nSetPivotX(mNativeRenderNode, pivotX); |
| } |
| |
| /** |
| * Returns the pivot value for this display list on the X axis, in pixels. |
| * |
| * @see #setPivotX(float) |
| */ |
| public float getPivotX() { |
| return nGetPivotX(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the pivot value for the display list on the Y axis |
| * |
| * @param pivotY The pivot value of the display list on the Y axis, in pixels |
| * @see View#setPivotY(float) |
| * @see #getPivotY() |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setPivotY(float pivotY) { |
| return nSetPivotY(mNativeRenderNode, pivotY); |
| } |
| |
| /** |
| * Returns the pivot value for this display list on the Y axis, in pixels. |
| * |
| * @see #setPivotY(float) |
| */ |
| public float getPivotY() { |
| return nGetPivotY(mNativeRenderNode); |
| } |
| |
| /** |
| * @return Whether or not a pivot was explicitly set with {@link #setPivotX(float)} or |
| * {@link #setPivotY(float)}. If no pivot has been set then the pivot will be the center |
| * of the RenderNode. |
| */ |
| public boolean isPivotExplicitlySet() { |
| return nIsPivotExplicitlySet(mNativeRenderNode); |
| } |
| |
| /** |
| * Clears any pivot previously set by a call to {@link #setPivotX(float)} or |
| * {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false |
| * and the pivot used for rotation will return to default of being centered on the view. |
| * |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean resetPivot() { |
| return nResetPivot(mNativeRenderNode); |
| } |
| |
| /** |
| * <p>Sets the distance along the Z axis (orthogonal to the X/Y plane on which |
| * RenderNodes are drawn) from the camera to this RenderNode. The camera's distance |
| * affects 3D transformations, for instance rotations around the X and Y |
| * axis. If the rotationX or rotationY properties are changed and this view is |
| * large (more than half the size of the screen), it is recommended to always |
| * use a camera distance that's greater than the height (X axis rotation) or |
| * the width (Y axis rotation) of this view.</p> |
| * |
| * <p>The distance of the camera from the drawing plane can have an affect on the |
| * perspective distortion of the RenderNode when it is rotated around the x or y axis. |
| * For example, a large distance will result in a large viewing angle, and there |
| * will not be much perspective distortion of the view as it rotates. A short |
| * distance may cause much more perspective distortion upon rotation, and can |
| * also result in some drawing artifacts if the rotated view ends up partially |
| * behind the camera (which is why the recommendation is to use a distance at |
| * least as far as the size of the view, if the view is to be rotated.)</p> |
| * |
| * <p>The distance is expressed in pixels and must always be positive</p> |
| * |
| * @param distance The distance in pixels, must always be positive |
| * @see #setRotationX(float) |
| * @see #setRotationY(float) |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setCameraDistance( |
| @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float distance) { |
| if (!Float.isFinite(distance) || distance < 0.0f) { |
| throw new IllegalArgumentException("distance must be finite & positive, given=" |
| + distance); |
| } |
| // Native actually wants this to be negative not positive, so we flip it. |
| return nSetCameraDistance(mNativeRenderNode, -distance); |
| } |
| |
| /** |
| * Returns the distance in Z of the camera for this RenderNode |
| * |
| * @return the distance along the Z axis in pixels. |
| * @see #setCameraDistance(float) |
| */ |
| public @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float getCameraDistance() { |
| return -nGetCameraDistance(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the left position for the RenderNode. |
| * |
| * @param left The left position, in pixels, of the RenderNode |
| * @return true if the value changed, false otherwise |
| * @hide |
| */ |
| public boolean setLeft(int left) { |
| return nSetLeft(mNativeRenderNode, left); |
| } |
| |
| /** |
| * Sets the top position for the RenderNode. |
| * |
| * @param top The top position, in pixels, of the RenderNode |
| * @return true if the value changed, false otherwise. |
| * @hide |
| */ |
| public boolean setTop(int top) { |
| return nSetTop(mNativeRenderNode, top); |
| } |
| |
| /** |
| * Sets the right position for the RenderNode. |
| * |
| * @param right The right position, in pixels, of the RenderNode |
| * @return true if the value changed, false otherwise. |
| * @hide |
| */ |
| public boolean setRight(int right) { |
| return nSetRight(mNativeRenderNode, right); |
| } |
| |
| /** |
| * Sets the bottom position for the RenderNode. |
| * |
| * @param bottom The bottom position, in pixels, of the RenderNode |
| * @return true if the value changed, false otherwise. |
| * @hide |
| */ |
| public boolean setBottom(int bottom) { |
| return nSetBottom(mNativeRenderNode, bottom); |
| } |
| |
| /** |
| * Gets the left position for the RenderNode. |
| * |
| * @return the left position in pixels |
| */ |
| public int getLeft() { |
| return nGetLeft(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the top position for the RenderNode. |
| * |
| * @return the top position in pixels |
| */ |
| public int getTop() { |
| return nGetTop(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the right position for the RenderNode. |
| * |
| * @return the right position in pixels |
| */ |
| public int getRight() { |
| return nGetRight(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the bottom position for the RenderNode. |
| * |
| * @return the bottom position in pixels |
| */ |
| public int getBottom() { |
| return nGetBottom(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the width of the RenderNode, which is the right - left. |
| * |
| * @return the width of the RenderNode |
| */ |
| public int getWidth() { |
| return nGetWidth(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the height of the RenderNode, which is the bottom - top. |
| * |
| * @return the height of the RenderNode |
| */ |
| public int getHeight() { |
| return nGetHeight(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets the left, top, right, and bottom of the RenderNode. |
| * |
| * @param left The left position of the RenderNode, in pixels |
| * @param top The top position of the RenderNode, in pixels |
| * @param right The right position of the RenderNode, in pixels |
| * @param bottom The bottom position of the RenderNode, in pixels |
| * @return true if any values changed, false otherwise. |
| * @hide |
| */ |
| public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) { |
| return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom); |
| } |
| |
| /** |
| * Sets the position of the RenderNode. |
| * |
| * @param left The left position of the RenderNode, in pixels |
| * @param top The top position of the RenderNode, in pixels |
| * @param right The right position of the RenderNode, in pixels |
| * @param bottom The bottom position of the RenderNode, in pixels |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setPosition(int left, int top, int right, int bottom) { |
| return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom); |
| } |
| |
| /** |
| * Sets the position of the RenderNode. |
| * |
| * @param position The position rectangle in pixels |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setPosition(@NonNull Rect position) { |
| return nSetLeftTopRightBottom(mNativeRenderNode, |
| position.left, position.top, position.right, position.bottom); |
| } |
| |
| /** |
| * Offsets the left and right positions for the RenderNode |
| * |
| * @param offset The amount that the left and right positions are offset in pixels |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean offsetLeftAndRight(int offset) { |
| return nOffsetLeftAndRight(mNativeRenderNode, offset); |
| } |
| |
| /** |
| * Offsets the top and bottom values for the RenderNode |
| * |
| * @param offset The amount that the left and right positions are offset in pixels |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean offsetTopAndBottom(int offset) { |
| return nOffsetTopAndBottom(mNativeRenderNode, offset); |
| } |
| |
| /** |
| * Outputs the RenderNode to the log. This method exists for use by |
| * tools to output display lists for selected nodes to the log. |
| * |
| * @hide TODO: Expose? Should the shape of this be different than forced dump to logcat? |
| */ |
| public void output() { |
| nOutput(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the approximate memory usage of the RenderNode for debug purposes. Does not include |
| * the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of |
| * this RenderNode and any data it owns. |
| * |
| * @return Approximate memory usage in bytes. |
| */ |
| public @BytesLong long computeApproximateMemoryUsage() { |
| return nGetUsageSize(mNativeRenderNode); |
| } |
| |
| /** |
| * Gets the approximate amount of memory allocated for the RenderNode for debug purposes. |
| * Does not include the memory allocated by any child RenderNodes nor any bitmaps, only the |
| * memory allocated for this RenderNode and any data it owns. |
| * |
| * The difference between this and {@link #computeApproximateMemoryUsage()} is this includes |
| * memory allocated but not used. In particular structures such as DisplayLists are similar |
| * to things like ArrayLists - they need to resize as commands are added to them. As such, |
| * memory used can be less than memory allocated. |
| * |
| * @hide */ |
| public @BytesLong long computeApproximateMemoryAllocated() { |
| return nGetAllocatedSize(mNativeRenderNode); |
| } |
| |
| /** |
| * Sets whether or not to allow force dark to apply to this RenderNode. |
| * |
| * Setting this to false will disable the auto-dark feature on everything this RenderNode |
| * draws, including any descendants. |
| * |
| * Setting this to true will allow this RenderNode to be automatically made dark, however |
| * a value of 'true' will not override any 'false' value in its parent chain nor will |
| * it prevent any 'false' in any of its children. |
| * |
| * @param allow Whether or not to allow force dark. |
| * @return True if the value changed, false if the new value was the same as the previous value. |
| */ |
| public boolean setForceDarkAllowed(boolean allow) { |
| return nSetAllowForceDark(mNativeRenderNode, allow); |
| } |
| |
| /** |
| * See {@link #setForceDarkAllowed(boolean)} |
| * |
| * @return true if force dark is allowed (default), false if it is disabled |
| */ |
| public boolean isForceDarkAllowed() { |
| return nGetAllowForceDark(mNativeRenderNode); |
| } |
| |
| /** |
| * Returns the unique ID that identifies this RenderNode. This ID is unique for the |
| * lifetime of the process. IDs are reset on process death, and are unique only within |
| * the process. |
| * |
| * This ID is intended to be used with debugging tools to associate a particular |
| * RenderNode across different debug dumping & inspection tools. For example |
| * a View layout inspector should include the unique ID for any RenderNodes that it owns |
| * to associate the drawing content with the layout content. |
| * |
| * @return the unique ID for this RenderNode |
| */ |
| public long getUniqueId() { |
| return nGetUniqueId(mNativeRenderNode); |
| } |
| |
| /** |
| * Captures whether this RenderNote represents a TextureView |
| * TODO(b/281695725): Clean this up once TextureView use setFrameRate API |
| * |
| * @hide |
| */ |
| public void setIsTextureView() { |
| nSetIsTextureView(mNativeRenderNode); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Animations |
| /////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * TODO: Figure out if this can be eliminated/refactored away |
| * |
| * For now this interface exists to de-couple RenderNode from anything View-specific in a |
| * bit of a kludge. |
| * |
| * @hide |
| */ |
| public interface AnimationHost { |
| /** @hide */ |
| void registerAnimatingRenderNode(RenderNode animator); |
| |
| /** @hide */ |
| void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator); |
| |
| /** @hide */ |
| boolean isAttached(); |
| } |
| |
| /** @hide */ |
| public void addAnimator(RenderNodeAnimator animator) { |
| if (!isAttached()) { |
| throw new IllegalStateException("Cannot start this animator on a detached view!"); |
| } |
| nAddAnimator(mNativeRenderNode, animator.getNativeAnimator()); |
| mAnimationHost.registerAnimatingRenderNode(this); |
| } |
| |
| /** @hide */ |
| public boolean isAttached() { |
| return mAnimationHost != null && mAnimationHost.isAttached(); |
| } |
| |
| /** @hide */ |
| public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animatorSet) { |
| if (!isAttached()) { |
| throw new IllegalStateException("Cannot start this animator on a detached view!"); |
| } |
| mAnimationHost.registerVectorDrawableAnimator(animatorSet); |
| } |
| |
| /** @hide */ |
| public void endAllAnimators() { |
| nEndAllAnimators(mNativeRenderNode); |
| } |
| |
| /** @hide */ |
| public void forceEndAnimators() { |
| nForceEndAnimators(mNativeRenderNode); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // Regular JNI methods |
| /////////////////////////////////////////////////////////////////////////// |
| |
| private static native long nCreate(String name); |
| |
| private static native long nGetNativeFinalizer(); |
| |
| private static native void nOutput(long renderNode); |
| |
| private static native int nGetUsageSize(long renderNode); |
| private static native int nGetAllocatedSize(long renderNode); |
| |
| private static native void nRequestPositionUpdates(long renderNode, |
| WeakReference<PositionUpdateListener> callback); |
| |
| // Animations |
| |
| private static native void nAddAnimator(long renderNode, long animatorPtr); |
| |
| private static native void nEndAllAnimators(long renderNode); |
| |
| private static native void nForceEndAnimators(long renderNode); |
| |
| /////////////////////////////////////////////////////////////////////////// |
| // @CriticalNative methods |
| /////////////////////////////////////////////////////////////////////////// |
| |
| @CriticalNative |
| private static native void nDiscardDisplayList(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nIsValid(long renderNode); |
| |
| // Matrix |
| |
| @CriticalNative |
| private static native void nGetTransformMatrix(long renderNode, long nativeMatrix); |
| |
| @CriticalNative |
| private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix); |
| |
| @CriticalNative |
| private static native boolean nHasIdentityMatrix(long renderNode); |
| |
| // Properties |
| |
| @CriticalNative |
| private static native boolean nOffsetTopAndBottom(long renderNode, int offset); |
| |
| @CriticalNative |
| private static native boolean nOffsetLeftAndRight(long renderNode, int offset); |
| |
| @CriticalNative |
| private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top, |
| int right, int bottom); |
| |
| @CriticalNative |
| private static native boolean nSetLeft(long renderNode, int left); |
| |
| @CriticalNative |
| private static native boolean nSetTop(long renderNode, int top); |
| |
| @CriticalNative |
| private static native boolean nSetRight(long renderNode, int right); |
| |
| @CriticalNative |
| private static native boolean nSetBottom(long renderNode, int bottom); |
| |
| @CriticalNative |
| private static native int nGetLeft(long renderNode); |
| |
| @CriticalNative |
| private static native int nGetTop(long renderNode); |
| |
| @CriticalNative |
| private static native int nGetRight(long renderNode); |
| |
| @CriticalNative |
| private static native int nGetBottom(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetCameraDistance(long renderNode, float distance); |
| |
| @CriticalNative |
| private static native boolean nSetPivotY(long renderNode, float pivotY); |
| |
| @CriticalNative |
| private static native boolean nSetPivotX(long renderNode, float pivotX); |
| |
| @CriticalNative |
| private static native boolean nResetPivot(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetLayerType(long renderNode, int layerType); |
| |
| @CriticalNative |
| private static native int nGetLayerType(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetLayerPaint(long renderNode, long paint); |
| |
| @CriticalNative |
| private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds); |
| |
| @CriticalNative |
| private static native boolean nGetClipToBounds(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetClipBounds(long renderNode, int left, int top, |
| int right, int bottom); |
| |
| @CriticalNative |
| private static native boolean nSetClipBoundsEmpty(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject); |
| |
| @CriticalNative |
| private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldReceive); |
| |
| @CriticalNative |
| private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top, |
| int right, int bottom, float radius, float alpha); |
| |
| @CriticalNative |
| private static native boolean nSetOutlinePath(long renderNode, long nativePath, |
| float alpha); |
| |
| @CriticalNative |
| private static native boolean nSetOutlineEmpty(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetOutlineNone(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nClearStretch(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nStretch(long renderNode, float vecX, float vecY, |
| float maxStretchX, float maxStretchY); |
| |
| @CriticalNative |
| private static native boolean nHasShadow(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetSpotShadowColor(long renderNode, int color); |
| |
| @CriticalNative |
| private static native boolean nSetAmbientShadowColor(long renderNode, int color); |
| |
| @CriticalNative |
| private static native int nGetSpotShadowColor(long renderNode); |
| |
| @CriticalNative |
| private static native int nGetAmbientShadowColor(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline); |
| |
| @CriticalNative |
| private static native boolean nSetRevealClip(long renderNode, |
| boolean shouldClip, float x, float y, float radius); |
| |
| @CriticalNative |
| private static native boolean nSetAlpha(long renderNode, float alpha); |
| |
| @CriticalNative |
| private static native boolean nSetRenderEffect(long renderNode, long renderEffect); |
| |
| @CriticalNative |
| private static native boolean nSetBackdropRenderEffect(long renderNode, long renderEffect); |
| |
| @CriticalNative |
| private static native boolean nSetHasOverlappingRendering(long renderNode, |
| boolean hasOverlappingRendering); |
| |
| @CriticalNative |
| private static native void nSetUsageHint(long renderNode, int usageHint); |
| |
| @CriticalNative |
| private static native boolean nSetElevation(long renderNode, float lift); |
| |
| @CriticalNative |
| private static native boolean nSetTranslationX(long renderNode, float translationX); |
| |
| @CriticalNative |
| private static native boolean nSetTranslationY(long renderNode, float translationY); |
| |
| @CriticalNative |
| private static native boolean nSetTranslationZ(long renderNode, float translationZ); |
| |
| @CriticalNative |
| private static native boolean nSetRotation(long renderNode, float rotation); |
| |
| @CriticalNative |
| private static native boolean nSetRotationX(long renderNode, float rotationX); |
| |
| @CriticalNative |
| private static native boolean nSetRotationY(long renderNode, float rotationY); |
| |
| @CriticalNative |
| private static native boolean nSetScaleX(long renderNode, float scaleX); |
| |
| @CriticalNative |
| private static native boolean nSetScaleY(long renderNode, float scaleY); |
| |
| @CriticalNative |
| private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix); |
| |
| @CriticalNative |
| private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix); |
| |
| @CriticalNative |
| private static native boolean nHasOverlappingRendering(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nGetAnimationMatrix(long renderNode, long animationMatrix); |
| |
| @CriticalNative |
| private static native boolean nGetClipToOutline(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetAlpha(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetCameraDistance(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetScaleX(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetScaleY(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetElevation(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetTranslationX(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetTranslationY(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetTranslationZ(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetRotation(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetRotationX(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetRotationY(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nIsPivotExplicitlySet(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetPivotX(long renderNode); |
| |
| @CriticalNative |
| private static native float nGetPivotY(long renderNode); |
| |
| @CriticalNative |
| private static native int nGetWidth(long renderNode); |
| |
| @CriticalNative |
| private static native int nGetHeight(long renderNode); |
| |
| @CriticalNative |
| private static native boolean nSetAllowForceDark(long renderNode, boolean allowForceDark); |
| |
| @CriticalNative |
| private static native boolean nGetAllowForceDark(long renderNode); |
| |
| @CriticalNative |
| private static native long nGetUniqueId(long renderNode); |
| |
| @CriticalNative |
| private static native void nSetIsTextureView(long renderNode); |
| } |