summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDeepanshu Gupta <deepanshu@google.com>2014-11-04 18:19:55 -0800
committerDeepanshu Gupta <deepanshu@google.com>2014-11-07 22:26:11 +0000
commit796e9b7f9910f2ba8133cdb6f137107585b2e5fa (patch)
treedb85df3bd9830e22f22ce69e6ea6ec774f1b511d /tools
parent9173c8a2e90a48959dedc9d4aa415482f5014844 (diff)
downloadframeworks_base-796e9b7f9910f2ba8133cdb6f137107585b2e5fa.zip
frameworks_base-796e9b7f9910f2ba8133cdb6f137107585b2e5fa.tar.gz
frameworks_base-796e9b7f9910f2ba8133cdb6f137107585b2e5fa.tar.bz2
Add primitive shadows support to LayoutLib
The shadows rendered are not the same as rendered by the framework. The main purpose of the shadows here is let the user know when shadows will be cast. Currently rectangular shadows are available in only two intensities. Any elevation below 10dp uses one shadow and any elevation over 10 dp uses the other. Also, even though the code for arbitrary shapes exist, it doesn't quite work yet. No shadows are visible when that code is run. Change-Id: Ie8235ffccf1d3809713f5d8f82afde434817d6b2
Diffstat (limited to 'tools')
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-b.pngbin0 -> 215 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-bl.pngbin0 -> 397 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-br.pngbin0 -> 406 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-l.pngbin0 -> 120 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-r.pngbin0 -> 207 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-tl.pngbin0 -> 277 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow-tr.pngbin0 -> 397 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-b.pngbin0 -> 195 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-bl.pngbin0 -> 277 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-br.pngbin0 -> 282 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-l.pngbin0 -> 108 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-r.pngbin0 -> 192 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-tl.pngbin0 -> 2855 bytes
-rw-r--r--tools/layoutlib/bridge/resources/icons/shadow2-tr.pngbin0 -> 286 bytes
-rw-r--r--tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java72
-rw-r--r--tools/layoutlib/bridge/src/android/view/ShadowPainter.java415
-rw-r--r--tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java205
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java113
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java47
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java5
20 files changed, 798 insertions, 59 deletions
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-b.png b/tools/layoutlib/bridge/resources/icons/shadow-b.png
new file mode 100644
index 0000000..68f4f4b
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-b.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-bl.png b/tools/layoutlib/bridge/resources/icons/shadow-bl.png
new file mode 100644
index 0000000..ee7dbe8
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-bl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-br.png b/tools/layoutlib/bridge/resources/icons/shadow-br.png
new file mode 100644
index 0000000..c45ad77
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-br.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-l.png b/tools/layoutlib/bridge/resources/icons/shadow-l.png
new file mode 100644
index 0000000..77d0bd0
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-l.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-r.png b/tools/layoutlib/bridge/resources/icons/shadow-r.png
new file mode 100644
index 0000000..4af7a33
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-r.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-tl.png b/tools/layoutlib/bridge/resources/icons/shadow-tl.png
new file mode 100644
index 0000000..424fb36
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-tl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-tr.png b/tools/layoutlib/bridge/resources/icons/shadow-tr.png
new file mode 100644
index 0000000..1fd0c77
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-tr.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-b.png b/tools/layoutlib/bridge/resources/icons/shadow2-b.png
new file mode 100644
index 0000000..963973e
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-b.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-bl.png b/tools/layoutlib/bridge/resources/icons/shadow2-bl.png
new file mode 100644
index 0000000..7612487
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-bl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-br.png b/tools/layoutlib/bridge/resources/icons/shadow2-br.png
new file mode 100644
index 0000000..8e20252
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-br.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-l.png b/tools/layoutlib/bridge/resources/icons/shadow2-l.png
new file mode 100644
index 0000000..2db18a0
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-l.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-r.png b/tools/layoutlib/bridge/resources/icons/shadow2-r.png
new file mode 100644
index 0000000..8e026f1
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-r.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-tl.png b/tools/layoutlib/bridge/resources/icons/shadow2-tl.png
new file mode 100644
index 0000000..a8045ed
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-tl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-tr.png b/tools/layoutlib/bridge/resources/icons/shadow2-tr.png
new file mode 100644
index 0000000..590373c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-tr.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
new file mode 100644
index 0000000..6c949d9
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 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.view;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of {@link RenderNode}
+ * <p/>
+ * Through the layoutlib_create tool, some native methods of RenderNode have been replaced by calls
+ * to methods of the same name in this delegate class.
+ *
+ * @see DelegateManager
+ */
+public class RenderNode_Delegate {
+
+
+ // ---- delegate manager ----
+ private static final DelegateManager<RenderNode_Delegate> sManager =
+ new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
+
+
+ private float mLift;
+ @SuppressWarnings("UnusedDeclaration")
+ private String mName;
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreate(String name) {
+ RenderNode_Delegate renderNodeDelegate = new RenderNode_Delegate();
+ renderNodeDelegate.mName = name;
+ return sManager.addNewDelegate(renderNodeDelegate);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDestroyRenderNode(long renderNode) {
+ sManager.removeJavaReferenceFor(renderNode);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nSetElevation(long renderNode, float lift) {
+ RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
+ if (delegate != null && delegate.mLift != lift) {
+ delegate.mLift = lift;
+ return true;
+ }
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static float nGetElevation(long renderNode) {
+ RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
+ if (delegate != null) {
+ return delegate.mLift;
+ }
+ return 0f;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/ShadowPainter.java b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java
new file mode 100644
index 0000000..38846bd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014 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.view;
+
+import com.android.annotations.NonNull;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.imageio.ImageIO;
+
+public class ShadowPainter {
+
+ /**
+ * Adds a drop shadow to a semi-transparent image (of an arbitrary shape) and returns it as a
+ * new image. This method attempts to mimic the same visual characteristics as the rectangular
+ * shadow painting methods in this class, {@link #createRectangularDropShadow(java.awt.image.BufferedImage)}
+ * and {@link #createSmallRectangularDropShadow(java.awt.image.BufferedImage)}.
+ *
+ * @param source the source image
+ * @param shadowSize the size of the shadow, normally {@link #SHADOW_SIZE or {@link
+ * #SMALL_SHADOW_SIZE}}
+ *
+ * @return a new image with the shadow painted in
+ */
+ @NonNull
+ public static BufferedImage createDropShadow(BufferedImage source, int shadowSize) {
+ shadowSize /= 2; // make shadow size have the same meaning as in the other shadow paint methods in this class
+
+ return createDropShadow(source, shadowSize, 0.7f, 0);
+ }
+
+ /**
+ * Creates a drop shadow of a given image and returns a new image which shows the input image on
+ * top of its drop shadow.
+ * <p/>
+ * <b>NOTE: If the shape is rectangular and opaque, consider using {@link
+ * #drawRectangleShadow(Graphics2D, int, int, int, int)} instead.</b>
+ *
+ * @param source the source image to be shadowed
+ * @param shadowSize the size of the shadow in pixels
+ * @param shadowOpacity the opacity of the shadow, with 0=transparent and 1=opaque
+ * @param shadowRgb the RGB int to use for the shadow color
+ *
+ * @return a new image with the source image on top of its shadow
+ */
+ @SuppressWarnings({"SuspiciousNameCombination", "UnnecessaryLocalVariable"}) // Imported code
+ public static BufferedImage createDropShadow(BufferedImage source, int shadowSize,
+ float shadowOpacity, int shadowRgb) {
+
+ // This code is based on
+ // http://www.jroller.com/gfx/entry/non_rectangular_shadow
+
+ BufferedImage image;
+ int width = source.getWidth();
+ int height = source.getHeight();
+ image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE,
+ BufferedImage.TYPE_INT_ARGB);
+
+ Graphics2D g2 = image.createGraphics();
+ g2.drawImage(image, shadowSize, shadowSize, null);
+
+ int dstWidth = image.getWidth();
+ int dstHeight = image.getHeight();
+
+ int left = (shadowSize - 1) >> 1;
+ int right = shadowSize - left;
+ int xStart = left;
+ int xStop = dstWidth - right;
+ int yStart = left;
+ int yStop = dstHeight - right;
+
+ shadowRgb &= 0x00FFFFFF;
+
+ int[] aHistory = new int[shadowSize];
+ int historyIdx;
+
+ int aSum;
+
+ int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ int lastPixelOffset = right * dstWidth;
+ float sumDivider = shadowOpacity / shadowSize;
+
+ // horizontal pass
+ for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
+ aSum = 0;
+ historyIdx = 0;
+ for (int x = 0; x < shadowSize; x++, bufferOffset++) {
+ int a = dataBuffer[bufferOffset] >>> 24;
+ aHistory[x] = a;
+ aSum += a;
+ }
+
+ bufferOffset -= right;
+
+ for (int x = xStart; x < xStop; x++, bufferOffset++) {
+ int a = (int) (aSum * sumDivider);
+ dataBuffer[bufferOffset] = a << 24 | shadowRgb;
+
+ // subtract the oldest pixel from the sum
+ aSum -= aHistory[historyIdx];
+
+ // get the latest pixel
+ a = dataBuffer[bufferOffset + right] >>> 24;
+ aHistory[historyIdx] = a;
+ aSum += a;
+
+ if (++historyIdx >= shadowSize) {
+ historyIdx -= shadowSize;
+ }
+ }
+ }
+ // vertical pass
+ for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
+ aSum = 0;
+ historyIdx = 0;
+ for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
+ int a = dataBuffer[bufferOffset] >>> 24;
+ aHistory[y] = a;
+ aSum += a;
+ }
+
+ bufferOffset -= lastPixelOffset;
+
+ for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
+ int a = (int) (aSum * sumDivider);
+ dataBuffer[bufferOffset] = a << 24 | shadowRgb;
+
+ // subtract the oldest pixel from the sum
+ aSum -= aHistory[historyIdx];
+
+ // get the latest pixel
+ a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
+ aHistory[historyIdx] = a;
+ aSum += a;
+
+ if (++historyIdx >= shadowSize) {
+ historyIdx -= shadowSize;
+ }
+ }
+ }
+
+ g2.drawImage(source, null, 0, 0);
+ g2.dispose();
+
+ return image;
+ }
+
+ /**
+ * Draws a rectangular drop shadow (of size {@link #SHADOW_SIZE} by {@link #SHADOW_SIZE} around
+ * the given source and returns a new image with both combined
+ *
+ * @param source the source image
+ *
+ * @return the source image with a drop shadow on the bottom and right
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static BufferedImage createRectangularDropShadow(BufferedImage source) {
+ int type = source.getType();
+ if (type == BufferedImage.TYPE_CUSTOM) {
+ type = BufferedImage.TYPE_INT_ARGB;
+ }
+
+ int width = source.getWidth();
+ int height = source.getHeight();
+ BufferedImage image;
+ image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE, type);
+ Graphics2D g = image.createGraphics();
+ g.drawImage(source, 0, 0, null);
+ drawRectangleShadow(image, 0, 0, width, height);
+ g.dispose();
+
+ return image;
+ }
+
+ /**
+ * Draws a small rectangular drop shadow (of size {@link #SMALL_SHADOW_SIZE} by {@link
+ * #SMALL_SHADOW_SIZE} around the given source and returns a new image with both combined
+ *
+ * @param source the source image
+ *
+ * @return the source image with a drop shadow on the bottom and right
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static BufferedImage createSmallRectangularDropShadow(BufferedImage source) {
+ int type = source.getType();
+ if (type == BufferedImage.TYPE_CUSTOM) {
+ type = BufferedImage.TYPE_INT_ARGB;
+ }
+
+ int width = source.getWidth();
+ int height = source.getHeight();
+
+ BufferedImage image;
+ image = new BufferedImage(width + SMALL_SHADOW_SIZE, height + SMALL_SHADOW_SIZE, type);
+
+ Graphics2D g = image.createGraphics();
+ g.drawImage(source, 0, 0, null);
+ drawSmallRectangleShadow(image, 0, 0, width, height);
+ g.dispose();
+
+ return image;
+ }
+
+ /**
+ * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
+ * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
+ * graphics. The size of the shadow is {@link #SHADOW_SIZE}.
+ *
+ * @param image the image to draw the shadow into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawRectangleShadow(BufferedImage image,
+ int x, int y, int width, int height) {
+ Graphics2D gc = image.createGraphics();
+ try {
+ drawRectangleShadow(gc, x, y, width, height);
+ } finally {
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Draws a small drop shadow for the given rectangle into the given context. It will not draw
+ * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
+ * shadow graphics. The size of the shadow is {@link #SMALL_SHADOW_SIZE}.
+ *
+ * @param image the image to draw the shadow into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawSmallRectangleShadow(BufferedImage image,
+ int x, int y, int width, int height) {
+ Graphics2D gc = image.createGraphics();
+ try {
+ drawSmallRectangleShadow(gc, x, y, width, height);
+ } finally {
+ gc.dispose();
+ }
+ }
+
+ /**
+ * The width and height of the drop shadow painted by
+ * {@link #drawRectangleShadow(Graphics2D, int, int, int, int)}
+ */
+ public static final int SHADOW_SIZE = 20; // DO NOT EDIT. This corresponds to bitmap graphics
+
+ /**
+ * The width and height of the drop shadow painted by
+ * {@link #drawSmallRectangleShadow(Graphics2D, int, int, int, int)}
+ */
+ public static final int SMALL_SHADOW_SIZE = 10; // DO NOT EDIT. Corresponds to bitmap graphics
+
+ /**
+ * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
+ * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
+ * graphics.
+ *
+ * @param gc the graphics context to draw into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawRectangleShadow(Graphics2D gc, int x, int y, int width, int height) {
+ assert ShadowBottomLeft != null;
+ assert ShadowBottomRight.getWidth(null) == SHADOW_SIZE;
+ assert ShadowBottomRight.getHeight(null) == SHADOW_SIZE;
+
+ int blWidth = ShadowBottomLeft.getWidth(null);
+ int trHeight = ShadowTopRight.getHeight(null);
+ if (width < blWidth) {
+ return;
+ }
+ if (height < trHeight) {
+ return;
+ }
+
+ gc.drawImage(ShadowBottomLeft, x - ShadowBottomLeft.getWidth(null), y + height, null);
+ gc.drawImage(ShadowBottomRight, x + width, y + height, null);
+ gc.drawImage(ShadowTopRight, x + width, y, null);
+ gc.drawImage(ShadowTopLeft, x - ShadowTopLeft.getWidth(null), y, null);
+ gc.drawImage(ShadowBottom,
+ x, y + height, x + width, y + height + ShadowBottom.getHeight(null),
+ 0, 0, ShadowBottom.getWidth(null), ShadowBottom.getHeight(null), null);
+ gc.drawImage(ShadowRight,
+ x + width, y + ShadowTopRight.getHeight(null), x + width + ShadowRight.getWidth(null), y + height,
+ 0, 0, ShadowRight.getWidth(null), ShadowRight.getHeight(null), null);
+ gc.drawImage(ShadowLeft,
+ x - ShadowLeft.getWidth(null), y + ShadowTopLeft.getHeight(null), x, y + height,
+ 0, 0, ShadowLeft.getWidth(null), ShadowLeft.getHeight(null), null);
+ }
+
+ /**
+ * Draws a small drop shadow for the given rectangle into the given context. It will not draw
+ * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
+ * shadow graphics.
+ * <p/>
+ *
+ * @param gc the graphics context to draw into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawSmallRectangleShadow(Graphics2D gc, int x, int y, int width,
+ int height) {
+ assert Shadow2BottomLeft != null;
+ assert Shadow2TopRight != null;
+ assert Shadow2BottomRight.getWidth(null) == SMALL_SHADOW_SIZE;
+ assert Shadow2BottomRight.getHeight(null) == SMALL_SHADOW_SIZE;
+
+ int blWidth = Shadow2BottomLeft.getWidth(null);
+ int trHeight = Shadow2TopRight.getHeight(null);
+ if (width < blWidth) {
+ return;
+ }
+ if (height < trHeight) {
+ return;
+ }
+
+ gc.drawImage(Shadow2BottomLeft, x - Shadow2BottomLeft.getWidth(null), y + height, null);
+ gc.drawImage(Shadow2BottomRight, x + width, y + height, null);
+ gc.drawImage(Shadow2TopRight, x + width, y, null);
+ gc.drawImage(Shadow2TopLeft, x - Shadow2TopLeft.getWidth(null), y, null);
+ gc.drawImage(Shadow2Bottom,
+ x, y + height, x + width, y + height + Shadow2Bottom.getHeight(null),
+ 0, 0, Shadow2Bottom.getWidth(null), Shadow2Bottom.getHeight(null), null);
+ gc.drawImage(Shadow2Right,
+ x + width, y + Shadow2TopRight.getHeight(null), x + width + Shadow2Right.getWidth(null), y + height,
+ 0, 0, Shadow2Right.getWidth(null), Shadow2Right.getHeight(null), null);
+ gc.drawImage(Shadow2Left,
+ x - Shadow2Left.getWidth(null), y + Shadow2TopLeft.getHeight(null), x, y + height,
+ 0, 0, Shadow2Left.getWidth(null), Shadow2Left.getHeight(null), null);
+ }
+
+ private static Image loadIcon(String name) {
+ InputStream inputStream = ShadowPainter.class.getResourceAsStream(name);
+ if (inputStream == null) {
+ throw new RuntimeException("Unable to load image for shadow: " + name);
+ }
+ try {
+ return ImageIO.read(inputStream);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to load image for shadow:" + name, e);
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ // ignore.
+ }
+ }
+ }
+
+ // Shadow graphics. This was generated by creating a drop shadow in
+ // Gimp, using the parameters x offset=10, y offset=10, blur radius=10,
+ // (for the small drop shadows x offset=10, y offset=10, blur radius=10)
+ // color=black, and opacity=51. These values attempt to make a shadow
+ // that is legible both for dark and light themes, on top of the
+ // canvas background (rgb(150,150,150). Darker shadows would tend to
+ // blend into the foreground for a dark holo screen, and lighter shadows
+ // would be hard to spot on the canvas background. If you make adjustments,
+ // make sure to check the shadow with both dark and light themes.
+ //
+ // After making the graphics, I cut out the top right, bottom left
+ // and bottom right corners as 20x20 images, and these are reproduced by
+ // painting them in the corresponding places in the target graphics context.
+ // I then grabbed a single horizontal gradient line from the middle of the
+ // right edge,and a single vertical gradient line from the bottom. These
+ // are then painted scaled/stretched in the target to fill the gaps between
+ // the three corner images.
+ //
+ // Filenames: bl=bottom left, b=bottom, br=bottom right, r=right, tr=top right
+
+ // Normal Drop Shadow
+ private static final Image ShadowBottom = loadIcon("/icons/shadow-b.png");
+ private static final Image ShadowBottomLeft = loadIcon("/icons/shadow-bl.png");
+ private static final Image ShadowBottomRight = loadIcon("/icons/shadow-br.png");
+ private static final Image ShadowRight = loadIcon("/icons/shadow-r.png");
+ private static final Image ShadowTopRight = loadIcon("/icons/shadow-tr.png");
+ private static final Image ShadowTopLeft = loadIcon("/icons/shadow-tl.png");
+ private static final Image ShadowLeft = loadIcon("/icons/shadow-l.png");
+
+ // Small Drop Shadow
+ private static final Image Shadow2Bottom = loadIcon("/icons/shadow2-b.png");
+ private static final Image Shadow2BottomLeft = loadIcon("/icons/shadow2-bl.png");
+ private static final Image Shadow2BottomRight = loadIcon("/icons/shadow2-br.png");
+ private static final Image Shadow2Right = loadIcon("/icons/shadow2-r.png");
+ private static final Image Shadow2TopRight = loadIcon("/icons/shadow2-tr.png");
+ private static final Image Shadow2TopLeft = loadIcon("/icons/shadow2-tl.png");
+ private static final Image Shadow2Left = loadIcon("/icons/shadow2-l.png");
+}
diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
new file mode 100644
index 0000000..a6c00f7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 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.view;
+
+import com.android.annotations.NonNull;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.resources.Density;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Path_Delegate;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.animation.Transformation;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link ViewGroup}
+ * <p/>
+ * Through the layoutlib_create tool, the original methods of ViewGroup have been replaced by calls
+ * to methods of the same name in this delegate class.
+ */
+public class ViewGroup_Delegate {
+
+ /**
+ * Overrides the original drawChild call in ViewGroup to draw the shadow.
+ */
+ @LayoutlibDelegate
+ /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
+ long drawingTime) {
+ boolean retVal = thisVG.drawChild_Original(canvas, child, drawingTime);
+ if (child.getZ() > thisVG.getZ()) {
+ ViewOutlineProvider outlineProvider = child.getOutlineProvider();
+ Outline outline = new Outline();
+ outlineProvider.getOutline(child, outline);
+
+ if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
+ int restoreTo = transformCanvas(thisVG, canvas, child);
+ drawShadow(thisVG, canvas, child, outline);
+ canvas.restoreToCount(restoreTo);
+ }
+ }
+ return retVal;
+ }
+
+ private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
+ Outline outline) {
+ BufferedImage shadow = null;
+ int x = 0;
+ if (outline.mRect != null) {
+ Shadow s = getRectShadow(parent, canvas, child, outline);
+ shadow = s.mShadow;
+ x = -s.mShadowWidth;
+ } else if (outline.mPath != null) {
+ shadow = getPathShadow(child, outline, canvas);
+ }
+ if (shadow == null) {
+ return;
+ }
+ Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
+ Density.getEnum(canvas.getDensity()));
+ Rect clipBounds = canvas.getClipBounds();
+ Rect newBounds = new Rect(clipBounds);
+ newBounds.left = newBounds.left + x;
+ canvas.clipRect(newBounds, Op.REPLACE);
+ canvas.drawBitmap(bitmap, x, 0, null);
+ canvas.clipRect(clipBounds, Op.REPLACE);
+ }
+
+ private static Shadow getRectShadow(ViewGroup parent, Canvas canvas, View child,
+ Outline outline) {
+ BufferedImage shadow;
+ Rect clipBounds = canvas.getClipBounds();
+ if (clipBounds.isEmpty()) {
+ return null;
+ }
+ float height = child.getZ() - parent.getZ();
+ // Draw large shadow if difference in z index is more than 10dp
+ float largeShadowThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
+ getMetrics(child));
+ boolean largeShadow = height > largeShadowThreshold;
+ int shadowSize = largeShadow ? ShadowPainter.SHADOW_SIZE : ShadowPainter.SMALL_SHADOW_SIZE;
+ shadow = new BufferedImage(clipBounds.width() + shadowSize, clipBounds.height(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = shadow.createGraphics();
+ Rect rect = outline.mRect;
+ if (largeShadow) {
+ ShadowPainter.drawRectangleShadow(graphics,
+ rect.left + shadowSize, rect.top, rect.width(), rect.height());
+ } else {
+ ShadowPainter.drawSmallRectangleShadow(graphics,
+ rect.left + shadowSize, rect.top, rect.width(), rect.height());
+ }
+ graphics.dispose();
+ return new Shadow(shadow, shadowSize);
+ }
+
+ @NonNull
+ private static DisplayMetrics getMetrics(View view) {
+ Context context = view.getContext();
+ while (context instanceof ContextThemeWrapper) {
+ context = ((ContextThemeWrapper) context).getBaseContext();
+ }
+ if (context instanceof BridgeContext) {
+ return ((BridgeContext) context).getMetrics();
+ }
+ throw new RuntimeException("View " + view.getClass().getName() + " not created with the " +
+ "right context");
+ }
+
+ private static BufferedImage getPathShadow(View child, Outline outline, Canvas canvas) {
+ Rect clipBounds = canvas.getClipBounds();
+ BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = image.createGraphics();
+ graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
+ graphics.dispose();
+ return ShadowPainter.createDropShadow(image, ((int) child.getZ()));
+ }
+
+ // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
+ // which were never taken. Ideally, we should hook up the shadow code in the same method so
+ // that we don't have to transform the canvas twice.
+ private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) {
+ final int restoreTo = canvas.save();
+ final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
+ int flags = thisVG.mGroupFlags;
+ Transformation transformToApply = null;
+ boolean concatMatrix = false;
+ if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+ final Transformation t = thisVG.getChildTransformation();
+ final boolean hasTransform = thisVG.getChildStaticTransformation(child, t);
+ if (hasTransform) {
+ final int transformType = t.getTransformationType();
+ transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
+ concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
+ }
+ }
+ concatMatrix |= childHasIdentityMatrix;
+
+ child.computeScroll();
+ int sx = child.mScrollX;
+ int sy = child.mScrollY;
+
+ canvas.translate(child.mLeft - sx, child.mTop - sy);
+ float alpha = child.getAlpha() * child.getTransitionAlpha();
+
+ if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
+ if (transformToApply != null || !childHasIdentityMatrix) {
+ int transX = -sx;
+ int transY = -sy;
+
+ if (transformToApply != null) {
+ if (concatMatrix) {
+ // Undo the scroll translation, apply the transformation matrix,
+ // then redo the scroll translate to get the correct result.
+ canvas.translate(-transX, -transY);
+ canvas.concat(transformToApply.getMatrix());
+ canvas.translate(transX, transY);
+ }
+ if (!childHasIdentityMatrix) {
+ canvas.translate(-transX, -transY);
+ canvas.concat(child.getMatrix());
+ canvas.translate(transX, transY);
+ }
+ }
+
+ }
+ }
+ return restoreTo;
+ }
+
+ private static class Shadow {
+ public BufferedImage mShadow;
+ public int mShadowWidth;
+
+ public Shadow(BufferedImage shadow, int shadowWidth) {
+ mShadow = shadow;
+ mShadowWidth = shadowWidth;
+ }
+
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index 57fd68e..2ff8d37 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.bars;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.internal.R;
@@ -37,7 +38,6 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.ActionMenuPresenter;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
@@ -49,15 +49,23 @@ public class ActionBarLayout {
private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
// The Action Bar
- @NonNull private CustomActionBarWrapper mActionBar;
+ @NonNull
+ private CustomActionBarWrapper mActionBar;
// Store another reference to the context so that we don't have to cast it repeatedly.
- @NonNull private final BridgeContext mBridgeContext;
+ @NonNull
+ private final BridgeContext mBridgeContext;
- @NonNull private FrameLayout mContentRoot;
+ @NonNull
+ private FrameLayout mContentRoot;
// A fake parent for measuring views.
- @Nullable private ViewGroup mMeasureParent;
+ @Nullable
+ private ViewGroup mMeasureParent;
+
+ // A Layout that contains the inflated action bar. The menu popup is added to this layout.
+ @NonNull
+ private final RelativeLayout mEnclosingLayout;
/**
* Inflate the action bar and attach it to {@code parentView}
@@ -90,20 +98,25 @@ public class ActionBarLayout {
if (layoutId == 0) {
throw new RuntimeException(error);
}
+ // Create a RelativeLayout to hold the action bar. The layout is needed so that we may
+ // add the menu popup to it.
+ mEnclosingLayout = new RelativeLayout(mBridgeContext);
+ setMatchParent(mEnclosingLayout);
+ parentView.addView(mEnclosingLayout);
+
// Inflate action bar layout.
- View decorContent = LayoutInflater.from(context).inflate(layoutId, parentView, true);
+ View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
- FrameLayout contentRoot = (FrameLayout) parentView.findViewById(android.R.id.content);
+ FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
// If something went wrong and we were not able to initialize the content root,
// just add a frame layout inside this and return.
if (contentRoot == null) {
contentRoot = new FrameLayout(context);
- contentRoot.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- parentView.addView(contentRoot);
+ setMatchParent(contentRoot);
+ mEnclosingLayout.addView(contentRoot);
mContentRoot = contentRoot;
} else {
mContentRoot = contentRoot;
@@ -112,70 +125,49 @@ public class ActionBarLayout {
}
}
+ private void setMatchParent(View view) {
+ view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ }
+
/**
* Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
* the content frame which shall serve as the new content root.
*/
public void createMenuPopup() {
- assert mContentRoot.getId() == android.R.id.content
+ assert mEnclosingLayout.getChildCount() == 1
: "Action Bar Menus have already been created.";
if (!isOverflowPopupNeeded()) {
return;
}
- // Create a layout to hold the menus and the user's content.
- RelativeLayout layout = new RelativeLayout(mActionBar.getPopupContext());
- layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- mContentRoot.addView(layout);
- // Create a layout for the user's content.
- FrameLayout contentRoot = new FrameLayout(mBridgeContext);
- contentRoot.setLayoutParams(new LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- // Add contentRoot and menus to the layout.
- layout.addView(contentRoot);
- layout.addView(createMenuView());
- // ContentRoot is now the view we just created.
- mContentRoot = contentRoot;
- }
-
- /**
- * Returns a {@link LinearLayout} containing the menu list view to be embedded in a
- * {@link RelativeLayout}
- */
- @NonNull
- private View createMenuView() {
DisplayMetrics metrics = mBridgeContext.getMetrics();
MenuBuilder menu = mActionBar.getMenuBuilder();
OverflowMenuAdapter adapter = new OverflowMenuAdapter(menu, mActionBar.getPopupContext());
- LinearLayout layout = new LinearLayout(mActionBar.getPopupContext());
+ ListView listView = new ListView(mActionBar.getPopupContext(), null,
+ R.attr.dropDownListViewStyle);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
measureContentWidth(adapter), LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
if (mActionBar.isSplit()) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
- // TODO: Find correct value instead of hardcoded 10dp.
- layoutParams.bottomMargin = getPixelValue("-10dp", metrics);
+ layoutParams.bottomMargin = getActionBarHeight() + mActionBar.getMenuPopupMargin();
} else {
- layoutParams.topMargin = getPixelValue("-10dp", metrics);
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ layoutParams.topMargin = getActionBarHeight() + mActionBar.getMenuPopupMargin();
}
- layout.setLayoutParams(layoutParams);
+ layoutParams.setMarginEnd(getPixelValue("5dp", metrics));
+ listView.setLayoutParams(layoutParams);
+ listView.setAdapter(adapter);
final TypedArray a = mActionBar.getPopupContext().obtainStyledAttributes(null,
R.styleable.PopupWindow, R.attr.popupMenuStyle, 0);
- layout.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground));
- layout.setDividerDrawable(a.getDrawable(R.attr.actionBarDivider));
+ listView.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground));
+ listView.setDivider(a.getDrawable(R.attr.actionBarDivider));
a.recycle();
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.setDividerPadding(getPixelValue("12dp", metrics));
- layout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
-
- ListView listView = new ListView(mActionBar.getPopupContext(), null,
- R.attr.dropDownListViewStyle);
- listView.setAdapter(adapter);
- layout.addView(listView);
- return layout;
+ listView.setElevation(mActionBar.getMenuPopupElevation());
+ mEnclosingLayout.addView(listView);
}
private boolean isOverflowPopupNeeded() {
@@ -244,9 +236,30 @@ public class ActionBarLayout {
return maxWidth;
}
- private int getPixelValue(@NonNull String value, @NonNull DisplayMetrics metrics) {
+ static int getPixelValue(@NonNull String value, @NonNull DisplayMetrics metrics) {
TypedValue typedValue = ResourceHelper.getValue(null, value, false /*requireUnit*/);
return (int) typedValue.getDimension(metrics);
}
+ // TODO: This is duplicated from RenderSessionImpl.
+ private int getActionBarHeight() {
+ RenderResources resources = mBridgeContext.getRenderResources();
+ DisplayMetrics metrics = mBridgeContext.getMetrics();
+ ResourceValue value = resources.findItemInTheme("actionBarSize", true);
+
+ // resolve it
+ value = resources.resolveResValue(value);
+
+ if (value != null) {
+ // get the numerical value, if available
+ TypedValue typedValue = ResourceHelper.getValue("actionBarSize", value.getValue(),
+ true);
+ if (typedValue != null) {
+ // compute the pixel value based on the display metrics
+ return (int) typedValue.getDimension(metrics);
+
+ }
+ }
+ return 0;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
index 70b9cc3..6db722e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
@@ -65,18 +65,17 @@ public abstract class CustomActionBarWrapper {
* Returns a wrapper around different implementations of the Action Bar to provide a common API.
*
* @param decorContent the top level view returned by inflating
- * ?attr/windowActionBarFullscreenDecorLayout
+ * ?attr/windowActionBarFullscreenDecorLayout
*/
@NonNull
public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
@NonNull SessionParams params, @NonNull View decorContent) {
View view = decorContent.findViewById(R.id.action_bar);
if (view instanceof Toolbar) {
- return new ToolbarWrapper(context, params, ((Toolbar) view)
- );
+ return new ToolbarWrapper(context, params, ((Toolbar) view));
} else if (view instanceof ActionBarView) {
- return new WindowActionBarWrapper(context, params, decorContent, ((ActionBarView) view)
- );
+ return new WindowActionBarWrapper(context, params, decorContent,
+ ((ActionBarView) view));
} else {
throw new IllegalStateException("Can't make an action bar out of " +
view.getClass().getSimpleName());
@@ -174,6 +173,13 @@ public abstract class CustomActionBarWrapper {
@NonNull
abstract DecorToolbar getDecorToolbar();
+ abstract int getMenuPopupElevation();
+
+ /**
+ * Margin between the menu popup and the action bar.
+ */
+ abstract int getMenuPopupMargin();
+
// ---- The implementations ----
/**
@@ -226,25 +232,38 @@ public abstract class CustomActionBarWrapper {
DecorToolbar getDecorToolbar() {
return mToolbar.getWrapper();
}
+
+ @Override
+ int getMenuPopupElevation() {
+ return 10;
+ }
+
+ @Override
+ int getMenuPopupMargin() {
+ return 0;
+ }
}
/**
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
* access to it using a common API.
*/
- private static class WindowActionBarWrapper extends CustomActionBarWrapper{
+ private static class WindowActionBarWrapper extends CustomActionBarWrapper {
@NonNull
private final WindowDecorActionBar mActionBar;
+ @NonNull
private final ActionBarView mActionBarView;
+ @NonNull
+ private final View mDecorContentRoot;
private MenuBuilder mMenuBuilder;
public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
@NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) {
- super(context, params, new WindowDecorActionBar(decorContentRoot)
- );
+ super(context, params, new WindowDecorActionBar(decorContentRoot));
mActionBarView = actionBarView;
mActionBar = ((WindowDecorActionBar) super.mActionBar);
+ mDecorContentRoot = decorContentRoot;
}
@Override
@@ -270,7 +289,7 @@ public abstract class CustomActionBarWrapper {
}
// Set action bar to be split, if needed.
- ViewGroup splitView = (ViewGroup) mActionBarView.findViewById(R.id.split_action_bar);
+ ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
if (splitView != null) {
mActionBarView.setSplitView(splitView);
Resources res = mContext.getResources();
@@ -314,6 +333,16 @@ public abstract class CustomActionBarWrapper {
return mActionBarView;
}
+ @Override
+ int getMenuPopupElevation() {
+ return 0;
+ }
+
+ @Override
+ int getMenuPopupMargin() {
+ return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics());
+ }
+
// TODO: Use an adapter, like List View to set up tabs.
@SuppressWarnings("deprecation") // For Tab
private void setupTabs(int num) {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 2fcdf34..4e6f456 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -162,6 +162,11 @@ public final class CreateInfo implements ICreateInfo {
"android.view.WindowManagerGlobal#getWindowManagerService",
"android.view.inputmethod.InputMethodManager#getInstance",
"android.view.MenuInflater#registerMenu",
+ "android.view.RenderNode#nCreate",
+ "android.view.RenderNode#nDestroyRenderNode",
+ "android.view.RenderNode#nSetElevation",
+ "android.view.RenderNode#nGetElevation",
+ "android.view.ViewGroup#drawChild",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.textservice.ITextServicesManager$Stub#asInterface",