You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ac...@apache.org on 2017/11/28 03:53:51 UTC
incubator-weex git commit: * [android] support multi box-shadow close
#915
Repository: incubator-weex
Updated Branches:
refs/heads/master 76c9140c4 -> 7f79916c8
* [android] support multi box-shadow
close #915
* [android] support multi box-shadow
update android border image
Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/7f79916c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/7f79916c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/7f79916c
Branch: refs/heads/master
Commit: 7f79916c8d7541a5df2c509acf494027f14f7f66
Parents: 76c9140
Author: misakuo <mi...@apache.org>
Authored: Mon Nov 13 11:37:08 2017 +0800
Committer: acton393 <zh...@gmail.com>
Committed: Tue Nov 28 11:52:44 2017 +0800
----------------------------------------------------------------------
.../com/taobao/weex/utils/BoxShadowUtil.java | 252 +++++++++++++------
test/screenshot/border-android.png | Bin 160024 -> 159136 bytes
2 files changed, 172 insertions(+), 80 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/7f79916c/android/sdk/src/main/java/com/taobao/weex/utils/BoxShadowUtil.java
----------------------------------------------------------------------
diff --git a/android/sdk/src/main/java/com/taobao/weex/utils/BoxShadowUtil.java b/android/sdk/src/main/java/com/taobao/weex/utils/BoxShadowUtil.java
index 229e30d..bc7036d 100644
--- a/android/sdk/src/main/java/com/taobao/weex/utils/BoxShadowUtil.java
+++ b/android/sdk/src/main/java/com/taobao/weex/utils/BoxShadowUtil.java
@@ -28,6 +28,7 @@ import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -35,6 +36,7 @@ import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
import android.os.Build;
import android.support.annotation.IntRange;
import android.support.annotation.Nullable;
@@ -49,6 +51,8 @@ import com.taobao.weex.WXEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Created by moxun on 2017/9/4.
@@ -62,6 +66,8 @@ public class BoxShadowUtil {
private static final String TAG = "BoxShadowUtil";
private static boolean sBoxShadowEnabled = true;
+ private static Pattern sColorPattern;
+
public static void setBoxShadowEnabled(boolean enabled) {
sBoxShadowEnabled = enabled;
WXLogUtils.w(TAG, "Switch box-shadow status: " + enabled);
@@ -71,29 +77,40 @@ public class BoxShadowUtil {
return sBoxShadowEnabled;
}
- public static void setBoxShadow(final View target, String style, float[] radii, int viewPort, final float quality) {
+ public static void setBoxShadow(final View target, String style, final float[] radii, int viewPort, final float quality) {
if (!sBoxShadowEnabled) {
WXLogUtils.w(TAG, "box-shadow was disabled by config");
return;
}
- final BoxShadowOptions options = parseBoxShadow(style, viewPort);
- if (options == null) {
- WXLogUtils.w(TAG, "Failed to parse box-shadow: " + style);
- return;
- }
-
if (target == null) {
WXLogUtils.w(TAG, "Target view is null!");
return;
}
- if (options.isClear && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ if (TextUtils.isEmpty(style) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
target.getOverlay().clear();
- WXLogUtils.d(TAG, "Remove box-shadow");
+ WXLogUtils.d(TAG, "Remove all box-shadow");
+ return;
+ }
+
+ final BoxShadowOptions[] shadows = parseBoxShadows(style, viewPort);
+ if (shadows == null || shadows.length == 0) {
+ WXLogUtils.w(TAG, "Failed to parse box-shadow: " + style);
return;
}
+ final List<BoxShadowOptions> normalShadows = new ArrayList<>(), insetShadows = new ArrayList<>();
+ for (BoxShadowOptions shadow : shadows) {
+ if (shadow != null) {
+ if (shadow.isInset) {
+ insetShadows.add(0, shadow);
+ } else {
+ normalShadows.add(0, shadow);
+ }
+ }
+ }
+
if (radii != null) {
if (radii.length != 8) {
WXLogUtils.w(TAG, "Length of radii must be 8");
@@ -102,86 +119,78 @@ public class BoxShadowUtil {
float realRadius = WXViewUtils.getRealSubPxByWidth(radii[i], viewPort);
radii[i] = realRadius;
}
- options.radii = radii;
}
}
- WXLogUtils.d(TAG, "Set box-shadow: " + options.toString());
-
target.post(new Runnable() {
@Override
public void run() {
- if (options.isInset) {
- setInsetBoxShadow(target, options, quality);
- } else {
- setNormalBoxShadow(target, options, quality);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ target.getOverlay().clear();
+ if (normalShadows.size() > 0) {
+ setNormalBoxShadow(target, normalShadows, quality, radii);
+ }
+
+ if (insetShadows.size() > 0) {
+ setInsetBoxShadow(target, insetShadows, quality, radii);
+ }
}
}
});
}
- private static Bitmap createShadowBitmap(int viewWidth, int viewHeight,
- float[] radii, float shadowRadius,
- float shadowSpread,
- float dx, float dy, int shadowColor) {
- int canvasWidth = viewWidth + 2 * (int) (shadowRadius + shadowSpread + Math.abs(dx));
- int canvasHeight = viewHeight + 2 * (int) (shadowRadius + shadowSpread + Math.abs(dy));
-
- Bitmap output = Bitmap.createBitmap(canvasWidth, canvasHeight, Bitmap.Config.ARGB_4444);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- WXLogUtils.d(TAG, "Allocation memory for box-shadow: " + (output.getAllocationByteCount() / 1024) + " KB");
- }
- Canvas canvas = new Canvas(output);
-
- if (false && WXEnvironment.isApkDebugable()) {
- // Using for debug
- Paint strokePaint = new Paint();
- strokePaint.setColor(Color.BLACK);
- strokePaint.setStrokeWidth(2);
- strokePaint.setStyle(Paint.Style.STROKE);
- canvas.drawRect(canvas.getClipBounds(), strokePaint);
- }
-
+ private static void drawShadow(Canvas canvas, BoxShadowOptions options) {
RectF shadowRect = new RectF(
0f, 0f,
- viewWidth + 2f * shadowSpread, viewHeight + 2f * shadowSpread
+ options.viewWidth + 2f * options.spread, options.viewHeight + 2f * options.spread
);
- float shadowDx = shadowRadius;
- float shadowDy = shadowRadius;
- if (dx > 0) {
- shadowDx = shadowDx + 2f * dx;
+ if (options.topLeft != null) {
+ shadowRect.offset(options.topLeft.x, options.topLeft.y);
+ }
+
+ float shadowDx = options.blur;
+ float shadowDy = options.blur;
+ if (options.hShadow > 0) {
+ shadowDx = shadowDx + 2f * options.hShadow;
}
- if (dy > 0) {
- shadowDy = shadowDy + 2f * dy;
+ if (options.vShadow > 0) {
+ shadowDy = shadowDy + 2f * options.vShadow;
}
shadowRect.offset(shadowDx, shadowDy);
Paint shadowPaint = new Paint();
shadowPaint.setAntiAlias(true);
- shadowPaint.setColor(shadowColor);
+ shadowPaint.setColor(options.color);
shadowPaint.setStyle(Paint.Style.FILL);
- if (shadowRadius > 0) {
- shadowPaint.setMaskFilter(new BlurMaskFilter(shadowRadius, BlurMaskFilter.Blur.NORMAL));
+ if (options.blur > 0) {
+ shadowPaint.setMaskFilter(new BlurMaskFilter(options.blur, BlurMaskFilter.Blur.NORMAL));
}
Path shadowPath = new Path();
float[] shadowRadii = new float[8];
- for (int i = 0; i < radii.length; i++) {
- float contentRadius = radii[i];
+ for (int i = 0; i < options.radii.length; i++) {
+ float contentRadius = options.radii[i];
if (contentRadius == 0f) {
shadowRadii[i] = 0f;
} else {
- shadowRadii[i] = radii[i] + shadowSpread;
+ shadowRadii[i] = options.radii[i] + options.spread;
}
}
shadowPath.addRoundRect(shadowRect, shadowRadii, Path.Direction.CCW);
canvas.drawPath(shadowPath, shadowPaint);
- return output;
+
+ if (false && WXEnvironment.isApkDebugable()) {
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ paint.setColor(Color.BLACK);
+ paint.setStyle(Paint.Style.STROKE);
+ canvas.drawRect(shadowRect, paint);
+ }
}
- private static void setNormalBoxShadow(View target, BoxShadowOptions options, float quality) {
+ private static void setNormalBoxShadow(View target, List<BoxShadowOptions> options, float quality, float[] radii) {
int h = target.getHeight();
int w = target.getWidth();
@@ -191,16 +200,55 @@ public class BoxShadowUtil {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- options.viewWidth = w;
- options.viewHeight = h;
+ int maxWidth = 0, maxHeight = 0;
+ for (BoxShadowOptions option : options) {
+ option.viewWidth = w;
+ option.viewHeight = h;
+ option.radii = radii;
+
+ Rect rect = option.getTargetCanvasRect();
+ if (maxWidth < rect.width()) {
+ maxWidth = rect.width();
+ }
+
+ if (maxHeight < rect.height()) {
+ maxHeight = rect.height();
+ }
+ }
+
+ int canvasWidth = (int) (maxWidth * quality);
+ int canvasHeight = (int) (maxHeight * quality);
+ Bitmap output = Bitmap.createBitmap(canvasWidth, canvasHeight, Bitmap.Config.ARGB_4444);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ WXLogUtils.d(TAG, "Allocation memory for box-shadow: " + (output.getAllocationByteCount() / 1024) + " KB");
+ }
+ Canvas canvas = new Canvas(output);
+
+ if (false && WXEnvironment.isApkDebugable()) {
+ // Using for debug
+ Paint strokePaint = new Paint();
+ strokePaint.setColor(Color.BLACK);
+ strokePaint.setStrokeWidth(2);
+ strokePaint.setStyle(Paint.Style.STROKE);
+ canvas.drawRect(canvas.getClipBounds(), strokePaint);
+ }
+
+ for (BoxShadowOptions option : options) {
+ Rect rect = option.getTargetCanvasRect();
+ float left = (maxWidth - rect.width()) / 2f;
+ float top = (maxHeight - rect.height()) / 2f;
+ option.topLeft = new PointF(left, top);
- BoxShadowOptions scaleOptions = options.scale(quality);
- Bitmap shadowBitmap = createShadowBitmap(scaleOptions.viewWidth, scaleOptions.viewHeight, scaleOptions.radii, scaleOptions.blur, scaleOptions.spread, scaleOptions.hShadow, scaleOptions.vShadow, scaleOptions.color);
+ BoxShadowOptions scaledOption = option.scale(quality);
+ drawShadow(canvas, scaledOption);
+ }
//Drawable's bounds must match the bitmap size, otherwise the shadows will be scaled
- OverflowBitmapDrawable shadowDrawable = new OverflowBitmapDrawable(target.getResources(), shadowBitmap, options);
+ int paddingX = (maxWidth - w) / 2;
+ int paddingY = (maxHeight - h) / 2;
+ OverflowBitmapDrawable shadowDrawable = new OverflowBitmapDrawable(target.getResources(),
+ output, new Point(paddingX, paddingY), new Rect(0, 0, w, h), radii);
- target.getOverlay().clear();
target.getOverlay().add(shadowDrawable);
//Relayout to ensure the shadows are fully drawn
ViewParent parent = target.getParent();
@@ -216,7 +264,7 @@ public class BoxShadowUtil {
}
}
- private static void setInsetBoxShadow(View target, BoxShadowOptions options, float quality) {
+ private static void setInsetBoxShadow(View target, List<BoxShadowOptions> options, float quality, float[] radii) {
if (target == null || options == null) {
WXLogUtils.w(TAG, "Illegal arguments");
return;
@@ -228,23 +276,53 @@ public class BoxShadowUtil {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
- Drawable shadow = new InsetShadowDrawable(target.getWidth(), target.getHeight(),
- options.hShadow, options.vShadow,
- options.blur, options.spread,
- options.color, options.radii);
- target.getOverlay().clear();
- target.getOverlay().add(shadow);
+ Drawable[] drawables = new Drawable[options.size()];
+ for (int i = 0; i < options.size(); i++) {
+ BoxShadowOptions option = options.get(i);
+ Drawable shadow = new InsetShadowDrawable(target.getWidth(), target.getHeight(),
+ option.hShadow, option.vShadow,
+ option.blur, option.spread,
+ option.color, radii);
+ drawables[i] = shadow;
+ }
+
+ LayerDrawable layerDrawable = new LayerDrawable(drawables);
+ target.getOverlay().add(layerDrawable);
target.invalidate();
} else {
Log.w(TAG, "Call setInsetBoxShadow() requires API level 18 or higher.");
}
}
- public static BoxShadowOptions parseBoxShadow(String boxShadow, int viewport) {
+ public static BoxShadowOptions[] parseBoxShadows(String boxShadowStyle, int viewport) {
+ // normalization color expression to #AARRGGBB
+ if (sColorPattern == null) {
+ sColorPattern = Pattern.compile("([rR][gG][bB][aA]?)\\((\\d+\\s*),\\s*(\\d+\\s*),\\s*(\\d+\\s*)(?:,\\s*(\\d+(?:\\.\\d+)?))?\\)");
+ }
+
+ Matcher matcher = sColorPattern.matcher(boxShadowStyle);
+
+ String processedStyle = boxShadowStyle;
+ while (matcher.find()) {
+ String color = matcher.group();
+ processedStyle = processedStyle.replace(color, "#" + Integer.toHexString(WXResourceUtils.getColor(color, Color.BLACK)));
+ }
+
+ String[] styles = processedStyle.split(",");
+ if (styles != null && styles.length > 0) {
+ BoxShadowOptions[] result = new BoxShadowOptions[styles.length];
+ for (int i = 0; i < styles.length; i++) {
+ result[i] = parseBoxShadow(styles[i], viewport);
+ }
+ return result;
+ }
+ return null;
+ }
+
+ private static BoxShadowOptions parseBoxShadow(String boxShadow, int viewport) {
BoxShadowOptions result = new BoxShadowOptions(viewport);
if (TextUtils.isEmpty(boxShadow)) {
- result.isClear = true;
- return result;
+ return null;
}
String boxShadowCopy = boxShadow;
@@ -255,9 +333,10 @@ public class BoxShadowUtil {
// match inset first
if (boxShadowCopy.contains("inset")) {
result.isInset = true;
- boxShadowCopy = boxShadowCopy.replace("inset", "").trim();
+ boxShadowCopy = boxShadowCopy.replace("inset", "");
}
+ boxShadowCopy = boxShadowCopy.trim();
List<String> params = new ArrayList<>(Arrays.asList(boxShadowCopy.split("\\s+")));
// match color
@@ -299,15 +378,17 @@ public class BoxShadowUtil {
private static class OverflowBitmapDrawable extends BitmapDrawable {
private int paddingX;
private int paddingY;
- private BoxShadowOptions options;
+ private Rect viewRect;
+ private float[] radii;
- private OverflowBitmapDrawable(Resources resources, Bitmap bitmap, BoxShadowOptions options) {
+ private OverflowBitmapDrawable(Resources resources, Bitmap bitmap, Point topLeft, Rect viewRect, float[] radii) {
super(resources, bitmap);
- this.paddingX = (int) (options.blur + Math.abs(options.hShadow) + options.spread);
- this.paddingY = (int) (options.blur + Math.abs(options.vShadow) + options.spread);
- this.options = options;
+ this.paddingX = topLeft.x;
+ this.paddingY = topLeft.y;
+ this.viewRect = viewRect;
+ this.radii = radii;
- setBounds(-paddingX, -paddingY, options.viewWidth + paddingX, options.viewHeight + paddingY);
+ setBounds(-paddingX, -paddingY, viewRect.width() + paddingX, viewRect.height() + paddingY);
}
@Override
@@ -318,8 +399,8 @@ public class BoxShadowUtil {
canvas.clipRect(newRect, Region.Op.REPLACE);
Path contentPath = new Path();
- RectF rectF = new RectF(0f, 0f, options.viewWidth, options.viewHeight);
- contentPath.addRoundRect(rectF, options.radii, Path.Direction.CCW);
+ RectF rectF = new RectF(0f, 0f, viewRect.width(), viewRect.height());
+ contentPath.addRoundRect(rectF, radii, Path.Direction.CCW);
// can not antialias
canvas.clipPath(contentPath, Region.Op.DIFFERENCE);
@@ -477,9 +558,9 @@ public class BoxShadowUtil {
public int color = Color.BLACK;
public boolean isInset = false;
- public boolean isClear = false;
public int viewWidth = 0;
public int viewHeight = 0;
+ public PointF topLeft = null;
private BoxShadowOptions(int vp) {
if (viewport != 0) {
@@ -525,15 +606,26 @@ public class BoxShadowUtil {
scaledOptions.viewHeight = (int) (viewHeight * scale);
scaledOptions.viewWidth = (int) (viewWidth * scale);
+ if (topLeft != null) {
+ scaledOptions.topLeft = new PointF();
+ scaledOptions.topLeft.x = topLeft.x * scale;
+ scaledOptions.topLeft.y = topLeft.y * scale;
+ }
+
scaledOptions.color = color;
scaledOptions.isInset = isInset;
- scaledOptions.isClear = isClear;
WXLogUtils.d(TAG, "Scaled BoxShadowOptions: [" + scale + "] " + scaledOptions);
return scaledOptions;
}
return null;
}
+ public Rect getTargetCanvasRect() {
+ int canvasWidth = viewWidth + 2 * (int) (blur + spread + Math.abs(hShadow));
+ int canvasHeight = viewHeight + 2 * (int) (blur + spread + Math.abs(vShadow));
+ return new Rect(0, 0, canvasWidth, canvasHeight);
+ }
+
@Override
public String toString() {
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/7f79916c/test/screenshot/border-android.png
----------------------------------------------------------------------
diff --git a/test/screenshot/border-android.png b/test/screenshot/border-android.png
index b2bcd5d..4f41f3a 100644
Binary files a/test/screenshot/border-android.png and b/test/screenshot/border-android.png differ