Merge "Add primitive shadows support to LayoutLib" into lmp-dev
BIN
tools/layoutlib/bridge/resources/icons/shadow-b.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow-bl.png
Normal file
After Width: | Height: | Size: 397 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow-br.png
Normal file
After Width: | Height: | Size: 406 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow-l.png
Normal file
After Width: | Height: | Size: 120 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow-r.png
Normal file
After Width: | Height: | Size: 207 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow-tl.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow-tr.png
Normal file
After Width: | Height: | Size: 397 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-b.png
Normal file
After Width: | Height: | Size: 195 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-bl.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-br.png
Normal file
After Width: | Height: | Size: 282 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-l.png
Normal file
After Width: | Height: | Size: 108 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-r.png
Normal file
After Width: | Height: | Size: 192 B |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-tl.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
tools/layoutlib/bridge/resources/icons/shadow2-tr.png
Normal file
After Width: | Height: | Size: 286 B |
@ -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;
|
||||
}
|
||||
}
|
415
tools/layoutlib/bridge/src/android/view/ShadowPainter.java
Normal file
@ -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");
|
||||
}
|
205
tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|