mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
feat(android): support clipToBounds (#9508)
* performance improvements around border handling BREAKING CHANGE: * if you have broder-radius or clip-path, it will clip by default
This commit is contained in:

committed by
Nathan Walker

parent
4f5f0aae77
commit
1ffc1628d0
@ -9,18 +9,8 @@ export class LayoutBase extends LayoutBaseCommon {
|
||||
return true;
|
||||
}
|
||||
[clipToBoundsProperty.setNative](value: boolean) {
|
||||
// TODO: Use ClipRectangle if API > 16!
|
||||
(<any>this.nativeViewProtected).setClipToBounds(value);
|
||||
|
||||
// We can't implement this without calling setClipChildren(false) on every ancestor up in the visual tree,
|
||||
// which will kill performance. It will also lead to unwanted side effects such as other totally unrelated
|
||||
// views being affected by setting the parents' setClipChildren to false.
|
||||
// The problem in Android is that a ViewGroup either clips ALL of its children or it does not. Unlike iOS, the clipping
|
||||
// cannot be controlled on a per view basis. So clipToBounds=false will have to be somehow achieved with stacking different
|
||||
// views on top of one another in an AbsoluteLayout or GridLayout. There is always a workaround when playing with layouts.
|
||||
//
|
||||
// The following article explains this in detail:
|
||||
// http://stackoverflow.com/questions/25044085/when-drawing-outside-the-view-clip-bounds-with-android-how-do-i-prevent-underli
|
||||
console.warn(`clipToBounds with value false is not supported on Android. You can use this.android.getParent().setClipChildren(false) as an alternative`);
|
||||
}
|
||||
|
||||
[isPassThroughParentEnabledProperty.setNative](value: boolean) {
|
||||
|
@ -51,7 +51,12 @@ def computeTargetSdkVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
compileSdkVersion computeCompileSdkVersion()
|
||||
buildToolsVersion computeBuildToolsVersion()
|
||||
|
||||
|
@ -18,13 +18,20 @@ import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Shader;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.nativescript.widgets.image.BitmapOwner;
|
||||
import org.nativescript.widgets.image.Fetcher;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Created by hristov on 6/15/2016.
|
||||
*/
|
||||
@ -49,6 +56,20 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
|
||||
private String clipPath;
|
||||
|
||||
private Path clipPathPath = null;
|
||||
private Path backgroundPath = null;
|
||||
private Path backgroundOutlinePath = null;
|
||||
private Path unifiedColorBorderPath = null;
|
||||
private Path innerBorderPath = null;
|
||||
private Path topBorderPath = null;
|
||||
private Path rightBorderPath = null;
|
||||
private Path bottomBorderPath = null;
|
||||
private Path leftBorderPath = null;
|
||||
private Path clippingPath = null;
|
||||
private Rect lastBounds = null;
|
||||
|
||||
Paint borderPaint = null;
|
||||
|
||||
private int backgroundColor;
|
||||
private String backgroundImage;
|
||||
private Bitmap backgroundBitmap;
|
||||
@ -141,6 +162,65 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
return clipPath;
|
||||
}
|
||||
|
||||
public Path getClipPathPath() {
|
||||
return clipPathPath;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
public Path getClippingPath() {
|
||||
Path toClip = null;
|
||||
if (this.clipPath != null) {
|
||||
generateClipPath(this.clipPath, new RectF(getBounds()), density);
|
||||
toClip = this.clipPathPath;
|
||||
} else if (hasBorderWidth()) {
|
||||
generateInnerBorderPath(getBounds());
|
||||
toClip = this.innerBorderPath;
|
||||
} else if (android.os.Build.VERSION.SDK_INT < 21 || !this.hasUniformBorder()) {
|
||||
generateBackgroundOutlinePath(getBounds());
|
||||
toClip = this.backgroundOutlinePath;
|
||||
}
|
||||
if (toClip != null) {
|
||||
if (clippingPath != null && hasCache("clippingPath")) {
|
||||
return clippingPath;
|
||||
}
|
||||
setHasCache("clippingPath");
|
||||
|
||||
if (clippingPath == null) {
|
||||
clippingPath = new Path();
|
||||
} else {
|
||||
clippingPath.reset();
|
||||
}
|
||||
clippingPath.addRect(new RectF(lastBounds), Path.Direction.CW);
|
||||
clippingPath.op(toClip, Path.Op.DIFFERENCE);
|
||||
return clippingPath;
|
||||
}
|
||||
// if uniform borders and no border width and >= 21 then the oultine can clip
|
||||
// no need to return a clippingPath
|
||||
return null;
|
||||
}
|
||||
|
||||
HashSet<String> cacheKeys = new HashSet<String>();
|
||||
|
||||
private boolean hasCache(String key) {
|
||||
return (cacheKeys.contains(key));
|
||||
}
|
||||
|
||||
private void setHasCache(String key) {
|
||||
cacheKeys.add(key);
|
||||
}
|
||||
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
if (lastBounds != null && lastBounds.equals(bounds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastBounds == null) {
|
||||
lastBounds = new Rect();
|
||||
}
|
||||
lastBounds.set(bounds);
|
||||
cacheKeys.clear();
|
||||
}
|
||||
|
||||
public int getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
@ -153,7 +233,9 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
return backgroundBitmap;
|
||||
}
|
||||
|
||||
public LinearGradientDefinition getBackgroundGradient() { return backgroundGradient; }
|
||||
public LinearGradientDefinition getBackgroundGradient() {
|
||||
return backgroundGradient;
|
||||
}
|
||||
|
||||
public String getBackgroundRepeat() {
|
||||
return backgroundRepeat;
|
||||
@ -168,34 +250,33 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
}
|
||||
|
||||
public boolean hasBorderWidth() {
|
||||
return this.borderTopWidth != 0
|
||||
|| this.borderRightWidth != 0
|
||||
|| this.borderBottomWidth != 0
|
||||
return this.borderTopWidth != 0 || this.borderRightWidth != 0 || this.borderBottomWidth != 0
|
||||
|| this.borderLeftWidth != 0;
|
||||
}
|
||||
|
||||
public boolean hasUniformBorderColor() {
|
||||
return this.borderTopColor == this.borderRightColor &&
|
||||
this.borderTopColor == this.borderBottomColor &&
|
||||
this.borderTopColor == this.borderLeftColor;
|
||||
return this.borderTopColor == this.borderRightColor && this.borderTopColor == this.borderBottomColor
|
||||
&& this.borderTopColor == this.borderLeftColor;
|
||||
}
|
||||
|
||||
public boolean hasUniformBorderWidth() {
|
||||
return this.borderTopWidth == this.borderRightWidth &&
|
||||
this.borderTopWidth == this.borderBottomWidth &&
|
||||
this.borderTopWidth == this.borderLeftWidth;
|
||||
return this.borderTopWidth == this.borderRightWidth && this.borderTopWidth == this.borderBottomWidth
|
||||
&& this.borderTopWidth == this.borderLeftWidth;
|
||||
}
|
||||
|
||||
public boolean hasUniformBorderRadius() {
|
||||
return this.borderTopLeftRadius == this.borderTopRightRadius &&
|
||||
this.borderTopLeftRadius == this.borderBottomRightRadius &&
|
||||
this.borderTopLeftRadius == this.borderBottomLeftRadius;
|
||||
return this.borderTopLeftRadius == this.borderTopRightRadius
|
||||
&& this.borderTopLeftRadius == this.borderBottomRightRadius
|
||||
&& this.borderTopLeftRadius == this.borderBottomLeftRadius;
|
||||
}
|
||||
|
||||
public boolean hasBorderRadius() {
|
||||
return borderBottomLeftRadius > 0 || borderTopLeftRadius > 0 || borderBottomRightRadius > 0
|
||||
|| borderTopRightRadius > 0;
|
||||
}
|
||||
|
||||
public boolean hasUniformBorder() {
|
||||
return this.hasUniformBorderColor() &&
|
||||
this.hasUniformBorderWidth() &&
|
||||
this.hasUniformBorderRadius();
|
||||
return this.hasUniformBorderColor() && this.hasUniformBorderWidth() && this.hasUniformBorderRadius();
|
||||
}
|
||||
|
||||
public BorderDrawable(float density) {
|
||||
@ -209,32 +290,18 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void refresh(int borderTopColor,
|
||||
int borderRightColor,
|
||||
int borderBottomColor,
|
||||
int borderLeftColor,
|
||||
public void refresh(int borderTopColor, int borderRightColor, int borderBottomColor, int borderLeftColor,
|
||||
|
||||
float borderTopWidth,
|
||||
float borderRightWidth,
|
||||
float borderBottomWidth,
|
||||
float borderLeftWidth,
|
||||
float borderTopWidth, float borderRightWidth, float borderBottomWidth, float borderLeftWidth,
|
||||
|
||||
float borderTopLeftRadius,
|
||||
float borderTopRightRadius,
|
||||
float borderBottomRightRadius,
|
||||
float borderTopLeftRadius, float borderTopRightRadius, float borderBottomRightRadius,
|
||||
float borderBottomLeftRadius,
|
||||
|
||||
String clipPath,
|
||||
|
||||
int backgroundColor,
|
||||
String backgroundImageUri,
|
||||
Bitmap backgroundBitmap,
|
||||
LinearGradientDefinition backgroundGradient,
|
||||
Context context,
|
||||
String backgroundRepeat,
|
||||
String backgroundPosition,
|
||||
CSSValue[] backgroundPositionParsedCSSValues,
|
||||
String backgroundSize,
|
||||
int backgroundColor, String backgroundImageUri, Bitmap backgroundBitmap,
|
||||
LinearGradientDefinition backgroundGradient, Context context, String backgroundRepeat,
|
||||
String backgroundPosition, CSSValue[] backgroundPositionParsedCSSValues, String backgroundSize,
|
||||
CSSValue[] backgroundSizeParsedCSSValues) {
|
||||
|
||||
this.borderTopColor = borderTopColor;
|
||||
@ -254,6 +321,9 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
|
||||
this.clipPath = clipPath;
|
||||
|
||||
// clear all cached paths
|
||||
cacheKeys.clear();
|
||||
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.backgroundImage = backgroundImageUri;
|
||||
this.backgroundBitmap = backgroundBitmap;
|
||||
@ -273,6 +343,54 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
}
|
||||
}
|
||||
|
||||
private void generateBackgroundPath(Rect bounds) {
|
||||
|
||||
if (backgroundPath != null && hasCache("backgroundPath")) {
|
||||
return;
|
||||
}
|
||||
setHasCache("backgroundPath");
|
||||
|
||||
if (backgroundPath == null) {
|
||||
backgroundPath = new Path();
|
||||
} else {
|
||||
backgroundPath.reset();
|
||||
}
|
||||
float[] backgroundRadii = { Math.max(0, borderTopLeftRadius), Math.max(0, borderTopLeftRadius),
|
||||
Math.max(0, borderTopRightRadius), Math.max(0, borderTopRightRadius),
|
||||
Math.max(0, borderBottomRightRadius), Math.max(0, borderBottomRightRadius),
|
||||
Math.max(0, borderBottomLeftRadius), Math.max(0, borderBottomLeftRadius) };
|
||||
float topBackoffAntialias = calculateBackoffAntialias(this.borderTopColor, this.borderTopWidth);
|
||||
float rightBackoffAntialias = calculateBackoffAntialias(this.borderRightColor, this.borderRightWidth);
|
||||
float bottomBackoffAntialias = calculateBackoffAntialias(this.borderBottomColor, this.borderBottomWidth);
|
||||
float leftBackoffAntialias = calculateBackoffAntialias(this.borderLeftColor, this.borderLeftWidth);
|
||||
|
||||
float width = (float) bounds.width();
|
||||
float height = (float) bounds.height();
|
||||
|
||||
RectF backgroundRect = new RectF(leftBackoffAntialias, topBackoffAntialias, width - rightBackoffAntialias,
|
||||
height - bottomBackoffAntialias);
|
||||
backgroundPath.addRoundRect(backgroundRect, backgroundRadii, Path.Direction.CW);
|
||||
}
|
||||
|
||||
private void generateUniformedColorBorderPath(Rect bounds) {
|
||||
if (unifiedColorBorderPath != null && hasCache("unifiedColorBorderPath")) {
|
||||
return;
|
||||
}
|
||||
setHasCache("unifiedColorBorderPath");
|
||||
|
||||
if (unifiedColorBorderPath == null) {
|
||||
unifiedColorBorderPath = new Path();
|
||||
} else {
|
||||
unifiedColorBorderPath.reset();
|
||||
}
|
||||
// the path used for outer border is the same as outline
|
||||
generateBackgroundOutlinePath(bounds);
|
||||
unifiedColorBorderPath.addPath(backgroundOutlinePath);
|
||||
|
||||
generateInnerBorderPath(bounds);
|
||||
unifiedColorBorderPath.addPath(innerBorderPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
Rect bounds = this.getBounds();
|
||||
@ -280,32 +398,12 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
float height = (float) bounds.height();
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
// When the view is off-screen the bounds might be empty and we don't have anything to draw.
|
||||
// When the view is off-screen the bounds might be empty and we don't have
|
||||
// anything to draw.
|
||||
return;
|
||||
}
|
||||
|
||||
RectF backgroundBoundsF = new RectF(bounds.left, bounds.top, bounds.right, bounds.bottom);
|
||||
|
||||
float topBackoffAntialias = calculateBackoffAntialias(this.borderTopColor, this.borderTopWidth);
|
||||
float rightBackoffAntialias = calculateBackoffAntialias(this.borderRightColor, this.borderRightWidth);
|
||||
float bottomBackoffAntialias = calculateBackoffAntialias(this.borderBottomColor, this.borderBottomWidth);
|
||||
float leftBackoffAntialias = calculateBackoffAntialias(this.borderLeftColor, this.borderLeftWidth);
|
||||
|
||||
float[] backgroundRadii = {
|
||||
Math.max(0, borderTopLeftRadius + leftBackoffAntialias), Math.max(0, borderTopLeftRadius + topBackoffAntialias),
|
||||
Math.max(0, borderTopRightRadius + rightBackoffAntialias), Math.max(0, borderTopRightRadius + topBackoffAntialias),
|
||||
Math.max(0, borderBottomRightRadius + rightBackoffAntialias), Math.max(0, borderBottomRightRadius + bottomBackoffAntialias),
|
||||
Math.max(0, borderBottomLeftRadius + leftBackoffAntialias), Math.max(0, borderBottomLeftRadius + bottomBackoffAntialias)
|
||||
};
|
||||
|
||||
Path backgroundPath = new Path();
|
||||
RectF backgroundRect = new RectF(
|
||||
leftBackoffAntialias,
|
||||
topBackoffAntialias,
|
||||
width - rightBackoffAntialias,
|
||||
height - bottomBackoffAntialias
|
||||
);
|
||||
backgroundPath.addRoundRect(backgroundRect, backgroundRadii, Path.Direction.CW);
|
||||
RectF backgroundBoundsF = new RectF(lastBounds);
|
||||
|
||||
// draw background
|
||||
if (this.backgroundColor != 0) {
|
||||
@ -317,6 +415,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
if (this.clipPath != null && !this.clipPath.isEmpty()) {
|
||||
drawClipPath(this.clipPath, canvas, backgroundColorPaint, backgroundBoundsF, this.density);
|
||||
} else {
|
||||
generateBackgroundPath(bounds);
|
||||
canvas.drawPath(backgroundPath, backgroundColorPaint);
|
||||
}
|
||||
}
|
||||
@ -335,11 +434,9 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
transform.postTranslate(params.posX, params.posY);
|
||||
|
||||
Paint backgroundImagePaint = new Paint();
|
||||
BitmapShader shader = new BitmapShader(
|
||||
this.backgroundBitmap,
|
||||
BitmapShader shader = new BitmapShader(this.backgroundBitmap,
|
||||
params.repeatX ? Shader.TileMode.REPEAT : Shader.TileMode.CLAMP,
|
||||
params.repeatY ? Shader.TileMode.REPEAT : Shader.TileMode.CLAMP
|
||||
);
|
||||
params.repeatY ? Shader.TileMode.REPEAT : Shader.TileMode.CLAMP);
|
||||
shader.setLocalMatrix(transform);
|
||||
backgroundImagePaint.setAntiAlias(true);
|
||||
backgroundImagePaint.setFilterBitmap(true);
|
||||
@ -354,13 +451,16 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
drawClipPath(this.clipPath, canvas, backgroundImagePaint, backgroundBoundsF, this.density);
|
||||
} else {
|
||||
boolean supportsPathOp = android.os.Build.VERSION.SDK_INT >= 19;
|
||||
generateBackgroundPath(bounds);
|
||||
if (supportsPathOp) {
|
||||
Path backgroundNoRepeatPath = new Path();
|
||||
backgroundNoRepeatPath.addRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight, Path.Direction.CCW);
|
||||
backgroundNoRepeatPath.addRect(params.posX, params.posY, params.posX + imageWidth,
|
||||
params.posY + imageHeight, Path.Direction.CCW);
|
||||
intersect(backgroundNoRepeatPath, backgroundPath);
|
||||
canvas.drawPath(backgroundNoRepeatPath, backgroundImagePaint);
|
||||
} else {
|
||||
// Clipping here will not be anti-aliased but at least it won't shine through the rounded corners.
|
||||
// Clipping here will not be anti-aliased but at least it won't shine through
|
||||
// the rounded corners.
|
||||
canvas.save();
|
||||
canvas.clipRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight);
|
||||
canvas.drawPath(backgroundPath, backgroundImagePaint);
|
||||
@ -372,10 +472,9 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
if (this.backgroundGradient != null) {
|
||||
LinearGradientDefinition def = this.backgroundGradient;
|
||||
Paint backgroundGradientPaint = new Paint();
|
||||
LinearGradient shader = new LinearGradient(
|
||||
def.getStartX() * width, def.getStartY() * height,
|
||||
def.getEndX() * width, def.getEndY() * height,
|
||||
def.getColors(), def.getStops(), Shader.TileMode.MIRROR);
|
||||
LinearGradient shader = new LinearGradient(def.getStartX() * width, def.getStartY() * height,
|
||||
def.getEndX() * width, def.getEndY() * height, def.getColors(), def.getStops(),
|
||||
Shader.TileMode.MIRROR);
|
||||
backgroundGradientPaint.setAntiAlias(true);
|
||||
backgroundGradientPaint.setFilterBitmap(true);
|
||||
backgroundGradientPaint.setShader(shader);
|
||||
@ -391,7 +490,10 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
if (this.clipPath != null && !this.clipPath.isEmpty()) {
|
||||
float borderWidth = this.getUniformBorderWidth();
|
||||
if (borderWidth > 0) {
|
||||
Paint borderPaint = new Paint();
|
||||
if (borderPaint == null) {
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
}
|
||||
borderPaint.setColor(this.getUniformBorderColor());
|
||||
borderPaint.setStyle(Paint.Style.STROKE);
|
||||
borderPaint.setStrokeWidth(borderWidth);
|
||||
@ -402,36 +504,15 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
} else if (this.hasUniformBorderColor()) {
|
||||
// iOS and browsers use black when no color is specified.
|
||||
if (borderLeftWidth > 0 || borderTopWidth > 0 || borderRightWidth > 0 || borderBottomWidth > 0) {
|
||||
Paint borderPaint = new Paint();
|
||||
borderPaint.setColor(this.getUniformBorderColor());
|
||||
borderPaint.setStyle(Paint.Style.FILL);
|
||||
if (borderPaint == null) {
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
Path borderPath = new Path();
|
||||
}
|
||||
borderPaint.setStyle(Paint.Style.FILL);
|
||||
borderPaint.setColor(this.getUniformBorderColor());
|
||||
|
||||
RectF borderOuterRect = new RectF(0, 0, width, height);
|
||||
float[] borderOuterRadii = {
|
||||
borderTopLeftRadius, borderTopLeftRadius,
|
||||
borderTopRightRadius, borderTopRightRadius,
|
||||
borderBottomRightRadius, borderBottomRightRadius,
|
||||
borderBottomLeftRadius, borderBottomLeftRadius
|
||||
};
|
||||
borderPath.addRoundRect(borderOuterRect, borderOuterRadii, Path.Direction.CW);
|
||||
|
||||
RectF borderInnerRect = new RectF(
|
||||
borderLeftWidth,
|
||||
borderTopWidth,
|
||||
width - borderRightWidth,
|
||||
height - borderBottomWidth
|
||||
);
|
||||
float[] borderInnerRadii = {
|
||||
Math.max(0, borderTopLeftRadius - borderLeftWidth), Math.max(0, borderTopLeftRadius - borderTopWidth),
|
||||
Math.max(0, borderTopRightRadius - borderRightWidth), Math.max(0, borderTopRightRadius - borderTopWidth),
|
||||
Math.max(0, borderBottomRightRadius - borderRightWidth), Math.max(0, borderBottomRightRadius - borderBottomWidth),
|
||||
Math.max(0, borderBottomLeftRadius - borderLeftWidth), Math.max(0, borderBottomLeftRadius - borderBottomWidth)
|
||||
};
|
||||
borderPath.addRoundRect(borderInnerRect, borderInnerRadii, Path.Direction.CCW);
|
||||
|
||||
canvas.drawPath(borderPath, borderPaint);
|
||||
generateUniformedColorBorderPath(bounds);
|
||||
canvas.drawPath(unifiedColorBorderPath, borderPaint);
|
||||
}
|
||||
} else {
|
||||
float top = this.borderTopWidth;
|
||||
@ -463,66 +544,109 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
PointF lbi = new PointF(left, bounds.bottom - bottom); // left-bottom-inside
|
||||
|
||||
if (this.borderTopWidth > 0) {
|
||||
Paint topBorderPaint = new Paint();
|
||||
topBorderPaint.setColor(this.borderTopColor);
|
||||
topBorderPaint.setAntiAlias(true);
|
||||
Path topBorderPath = new Path();
|
||||
|
||||
if (topBorderPath == null || !hasCache("topBorderPath")) {
|
||||
setHasCache("topBorderPath");
|
||||
if (topBorderPath == null) {
|
||||
topBorderPath = new Path();
|
||||
} else {
|
||||
topBorderPath.reset();
|
||||
}
|
||||
topBorderPath.setFillType(Path.FillType.EVEN_ODD);
|
||||
topBorderPath.moveTo(lto.x, lto.y);
|
||||
topBorderPath.lineTo(rto.x, rto.y);
|
||||
topBorderPath.lineTo(rti.x, rti.y);
|
||||
topBorderPath.lineTo(lti.x, lti.y);
|
||||
topBorderPath.close();
|
||||
canvas.drawPath(topBorderPath, topBorderPaint);
|
||||
}
|
||||
if (borderPaint == null) {
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
}
|
||||
borderPaint.setColor(this.borderTopColor);
|
||||
canvas.drawPath(topBorderPath, borderPaint);
|
||||
}
|
||||
|
||||
if (this.borderRightWidth > 0) {
|
||||
Paint rightBorderPaint = new Paint();
|
||||
rightBorderPaint.setColor(this.borderRightColor);
|
||||
rightBorderPaint.setAntiAlias(true);
|
||||
Path rightBorderPath = new Path();
|
||||
if (rightBorderPath == null || !hasCache("rightBorderPath")) {
|
||||
setHasCache("rightBorderPath");
|
||||
if (rightBorderPath == null) {
|
||||
rightBorderPath = new Path();
|
||||
} else {
|
||||
rightBorderPath.reset();
|
||||
}
|
||||
rightBorderPath.setFillType(Path.FillType.EVEN_ODD);
|
||||
rightBorderPath.moveTo(rto.x, rto.y);
|
||||
rightBorderPath.lineTo(rbo.x, rbo.y);
|
||||
rightBorderPath.lineTo(rbi.x, rbi.y);
|
||||
rightBorderPath.lineTo(rti.x, rti.y);
|
||||
rightBorderPath.close();
|
||||
canvas.drawPath(rightBorderPath, rightBorderPaint);
|
||||
}
|
||||
if (borderPaint == null) {
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
borderPaint.setColor(this.borderRightColor);
|
||||
canvas.drawPath(rightBorderPath, borderPaint);
|
||||
}
|
||||
|
||||
if (this.borderBottomWidth > 0) {
|
||||
Paint bottomBorderPaint = new Paint();
|
||||
bottomBorderPaint.setColor(this.borderBottomColor);
|
||||
bottomBorderPaint.setAntiAlias(true);
|
||||
Path bottomBorderPath = new Path();
|
||||
if (bottomBorderPath == null || !hasCache("bottomBorderPath")) {
|
||||
setHasCache("bottomBorderPath");
|
||||
if (bottomBorderPath == null) {
|
||||
bottomBorderPath = new Path();
|
||||
} else {
|
||||
bottomBorderPath.reset();
|
||||
}
|
||||
bottomBorderPath.setFillType(Path.FillType.EVEN_ODD);
|
||||
bottomBorderPath.moveTo(rbo.x, rbo.y);
|
||||
bottomBorderPath.lineTo(lbo.x, lbo.y);
|
||||
bottomBorderPath.lineTo(lbi.x, lbi.y);
|
||||
bottomBorderPath.lineTo(rbi.x, rbi.y);
|
||||
bottomBorderPath.close();
|
||||
canvas.drawPath(bottomBorderPath, bottomBorderPaint);
|
||||
}
|
||||
if (borderPaint == null) {
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
borderPaint.setColor(this.borderBottomColor);
|
||||
canvas.drawPath(bottomBorderPath, borderPaint);
|
||||
}
|
||||
|
||||
if (this.borderLeftWidth > 0) {
|
||||
Paint leftBorderPaint = new Paint();
|
||||
leftBorderPaint.setColor(this.borderLeftColor);
|
||||
leftBorderPaint.setAntiAlias(true);
|
||||
Path leftBorderPath = new Path();
|
||||
|
||||
if (leftBorderPath == null || !hasCache("leftBorderPath")) {
|
||||
setHasCache("leftBorderPath");
|
||||
if (leftBorderPath == null) {
|
||||
leftBorderPath = new Path();
|
||||
} else {
|
||||
leftBorderPath.reset();
|
||||
}
|
||||
leftBorderPath.setFillType(Path.FillType.EVEN_ODD);
|
||||
leftBorderPath.moveTo(lbo.x, lbo.y);
|
||||
leftBorderPath.lineTo(lto.x, lto.y);
|
||||
leftBorderPath.lineTo(lti.x, lti.y);
|
||||
leftBorderPath.lineTo(lbi.x, lbi.y);
|
||||
leftBorderPath.close();
|
||||
canvas.drawPath(leftBorderPath, leftBorderPaint);
|
||||
}
|
||||
if (borderPaint == null) {
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
borderPaint.setColor(this.borderLeftColor);
|
||||
canvas.drawPath(leftBorderPath, borderPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static float calculateBackoffAntialias(int borderColor, float borderWidth) {
|
||||
// We will inset background colors and images so antialiasing will not color pixels outside the border.
|
||||
// If the border is transparent we will backoff less, and we will not backoff more than half a pixel or half the border width.
|
||||
// We will inset background colors and images so antialiasing will not color
|
||||
// pixels outside the border.
|
||||
// If the border is transparent we will backoff less, and we will not backoff
|
||||
// more than half a pixel or half the border width.
|
||||
float halfBorderWidth = borderWidth / 2.0f;
|
||||
float normalizedBorderAlpha = ((float) Color.alpha(borderColor)) / 255.0f;
|
||||
return Math.min(1f, halfBorderWidth) * normalizedBorderAlpha;
|
||||
@ -536,11 +660,22 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
private static Pattern spaceAndComma = Pattern.compile("[\\s,]+");
|
||||
private static Pattern space = Pattern.compile("\\s+");
|
||||
|
||||
private static void drawClipPath(String clipPath, Canvas canvas, Paint paint, RectF bounds, float density) {
|
||||
// Sample string is polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
|
||||
private void generateClipPath(String clipPath, RectF bounds, float density) {
|
||||
if (clipPathPath != null && hasCache("clipPathPath")) {
|
||||
return;
|
||||
}
|
||||
setHasCache("clipPathPath");
|
||||
|
||||
// Sample string is polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%,
|
||||
// 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
|
||||
String functionName = clipPath.substring(0, clipPath.indexOf("("));
|
||||
String value = clipPath.substring(clipPath.indexOf("(") + 1, clipPath.indexOf(")"));
|
||||
|
||||
if (clipPathPath == null) {
|
||||
clipPathPath = new Path();
|
||||
} else {
|
||||
clipPathPath.reset();
|
||||
}
|
||||
String[] arr;
|
||||
float top;
|
||||
float right;
|
||||
@ -554,8 +689,7 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
right = cssValueToDevicePixels(arr[1], bounds.right, density);
|
||||
bottom = cssValueToDevicePixels(arr[2], bounds.bottom, density);
|
||||
left = cssValueToDevicePixels(arr[3], bounds.right, density);
|
||||
|
||||
canvas.drawRect(left, top, right, bottom, paint);
|
||||
clipPathPath.addRect(new RectF(left, top, right, bottom), Path.Direction.CW);
|
||||
break;
|
||||
case "inset":
|
||||
arr = spaceAndComma.split(value);
|
||||
@ -580,18 +714,21 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
}
|
||||
|
||||
top = cssValueToDevicePixels(topString, bounds.bottom, density);
|
||||
right = cssValueToDevicePixels("100%", bounds.right, density) - cssValueToDevicePixels(rightString, bounds.right, density);
|
||||
bottom = cssValueToDevicePixels("100%", bounds.bottom, density) - cssValueToDevicePixels(bottomString, bounds.bottom, density);
|
||||
right = cssValueToDevicePixels("100%", bounds.right, density)
|
||||
- cssValueToDevicePixels(rightString, bounds.right, density);
|
||||
bottom = cssValueToDevicePixels("100%", bounds.bottom, density)
|
||||
- cssValueToDevicePixels(bottomString, bounds.bottom, density);
|
||||
left = cssValueToDevicePixels(leftString, bounds.right, density);
|
||||
|
||||
canvas.drawRect(left, top, right, bottom, paint);
|
||||
clipPathPath.addRect(new RectF(left, top, right, bottom), Path.Direction.CW);
|
||||
break;
|
||||
case "circle":
|
||||
arr = space.split(value);
|
||||
float radius = cssValueToDevicePixels(arr[0], (bounds.width() > bounds.height() ? bounds.height() : bounds.width()) / 2, density);
|
||||
float radius = cssValueToDevicePixels(arr[0],
|
||||
(bounds.width() > bounds.height() ? bounds.height() : bounds.width()) / 2, density);
|
||||
float y = cssValueToDevicePixels(arr[2], bounds.height(), density);
|
||||
float x = cssValueToDevicePixels(arr[3], bounds.width(), density);
|
||||
canvas.drawCircle(x, y, radius, paint);
|
||||
clipPathPath.addCircle(x, y, radius, Path.Direction.CW);
|
||||
break;
|
||||
case "ellipse":
|
||||
arr = space.split(value);
|
||||
@ -603,31 +740,35 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
top = cY - rY;
|
||||
right = (rX * 2) + left;
|
||||
bottom = (rY * 2) + top;
|
||||
canvas.drawOval(new RectF(left, top, right, bottom), paint);
|
||||
clipPathPath.addOval(new RectF(left, top, right, bottom), Path.Direction.CW);
|
||||
break;
|
||||
case "polygon":
|
||||
Path path = new Path();
|
||||
PointF firstPoint = null;
|
||||
arr = value.split(",");
|
||||
for (String s : arr) {
|
||||
String[] xy = space.split(s.trim());
|
||||
PointF point = new PointF(cssValueToDevicePixels(xy[0], bounds.width(), density), cssValueToDevicePixels(xy[1], bounds.height(), density));
|
||||
PointF point = new PointF(cssValueToDevicePixels(xy[0], bounds.width(), density),
|
||||
cssValueToDevicePixels(xy[1], bounds.height(), density));
|
||||
|
||||
if (firstPoint == null) {
|
||||
firstPoint = point;
|
||||
path.moveTo(point.x, point.y);
|
||||
clipPathPath.moveTo(point.x, point.y);
|
||||
}
|
||||
|
||||
path.lineTo(point.x, point.y);
|
||||
clipPathPath.lineTo(point.x, point.y);
|
||||
}
|
||||
if (firstPoint != null) {
|
||||
path.lineTo(firstPoint.x, firstPoint.y);
|
||||
clipPathPath.lineTo(firstPoint.x, firstPoint.y);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void drawClipPath(String clipPath, Canvas canvas, Paint paint, RectF bounds, float density) {
|
||||
generateClipPath(clipPath, bounds, density);
|
||||
canvas.drawPath(clipPathPath, paint);
|
||||
}
|
||||
|
||||
private BackgroundDrawParams getDrawParams(float width, float height) {
|
||||
BackgroundDrawParams res = new BackgroundDrawParams();
|
||||
|
||||
@ -663,15 +804,18 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
|
||||
res.sizeX = imageWidth;
|
||||
res.sizeY = imageHeight;
|
||||
} else if ("number".equals(vx.getType()) && "number".equals(vy.getType()) &&
|
||||
(("px".equals(vx.getUnit()) && "px".equals(vy.getUnit())) || ((vx.getUnit() == null || vx.getUnit().isEmpty()) && (vy.getUnit() == null || vy.getUnit().isEmpty())))) {
|
||||
} else if ("number".equals(vx.getType()) && "number".equals(vy.getType())
|
||||
&& (("px".equals(vx.getUnit()) && "px".equals(vy.getUnit()))
|
||||
|| ((vx.getUnit() == null || vx.getUnit().isEmpty())
|
||||
&& (vy.getUnit() == null || vy.getUnit().isEmpty())))) {
|
||||
imageWidth = vx.getValue();
|
||||
imageHeight = vy.getValue();
|
||||
|
||||
res.sizeX = imageWidth;
|
||||
res.sizeY = imageHeight;
|
||||
}
|
||||
} else if (this.backgroundSizeParsedCSSValues.length == 1 && "ident".equals(this.backgroundSizeParsedCSSValues[0].getType())) {
|
||||
} else if (this.backgroundSizeParsedCSSValues.length == 1
|
||||
&& "ident".equals(this.backgroundSizeParsedCSSValues[0].getType())) {
|
||||
float scale = 0;
|
||||
|
||||
if ("cover".equals(this.backgroundSizeParsedCSSValues[0].getString())) {
|
||||
@ -702,8 +846,10 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
if ("%".equals(vx.getUnit()) && "%".equals(vy.getUnit())) {
|
||||
res.posX = spaceX * vx.getValue() / 100;
|
||||
res.posY = spaceY * vy.getValue() / 100;
|
||||
} else if ("number".equals(vx.getType()) && "number".equals(vy.getType()) &&
|
||||
(("px".equals(vx.getUnit()) && "px".equals(vy.getUnit())) || ((vx.getUnit() == null || vx.getUnit().isEmpty()) && (vy.getUnit() == null || vy.getUnit().isEmpty())))) {
|
||||
} else if ("number".equals(vx.getType()) && "number".equals(vy.getType())
|
||||
&& (("px".equals(vx.getUnit()) && "px".equals(vy.getUnit()))
|
||||
|| ((vx.getUnit() == null || vx.getUnit().isEmpty())
|
||||
&& (vy.getUnit() == null || vy.getUnit().isEmpty())))) {
|
||||
res.posX = vx.getValue();
|
||||
res.posY = vy.getValue();
|
||||
} else if ("ident".equals(vx.getType()) && "ident".equals(vy.getType())) {
|
||||
@ -781,29 +927,22 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
|
||||
"id: " + this.id + "; " +
|
||||
|
||||
"borderTopColor: " + this.borderTopColor + "; " +
|
||||
"borderRightColor: " + this.borderRightColor + "; " +
|
||||
"borderBottomColor: " + this.borderBottomColor + "; " +
|
||||
"borderLeftColor: " + this.borderLeftColor + "; " +
|
||||
"borderTopColor: " + this.borderTopColor + "; " + "borderRightColor: " + this.borderRightColor + "; "
|
||||
+ "borderBottomColor: " + this.borderBottomColor + "; " + "borderLeftColor: " + this.borderLeftColor
|
||||
+ "; " +
|
||||
|
||||
"borderTopWidth: " + this.borderTopWidth + "; " +
|
||||
"borderRightWidth: " + this.borderRightWidth + "; " +
|
||||
"borderBottomWidth: " + this.borderBottomWidth + "; " +
|
||||
"borderLeftWidth: " + this.borderLeftWidth + "; " +
|
||||
"borderTopWidth: " + this.borderTopWidth + "; " + "borderRightWidth: " + this.borderRightWidth + "; "
|
||||
+ "borderBottomWidth: " + this.borderBottomWidth + "; " + "borderLeftWidth: " + this.borderLeftWidth
|
||||
+ "; " +
|
||||
|
||||
"borderTopLeftRadius: " + this.borderTopLeftRadius + "; " +
|
||||
"borderTopRightRadius: " + this.borderTopRightRadius + "; " +
|
||||
"borderBottomRightRadius: " + this.borderBottomRightRadius + "; " +
|
||||
"borderBottomLeftRadius: " + this.borderBottomLeftRadius + "; " +
|
||||
"borderTopLeftRadius: " + this.borderTopLeftRadius + "; " + "borderTopRightRadius: "
|
||||
+ this.borderTopRightRadius + "; " + "borderBottomRightRadius: " + this.borderBottomRightRadius + "; "
|
||||
+ "borderBottomLeftRadius: " + this.borderBottomLeftRadius + "; " +
|
||||
|
||||
"clipPath: " + this.clipPath + "; " +
|
||||
"backgroundColor: " + this.backgroundColor + "; " +
|
||||
"backgroundImage: " + this.backgroundImage + "; " +
|
||||
"backgroundBitmap: " + this.backgroundBitmap + "; " +
|
||||
"backgroundRepeat: " + this.backgroundRepeat + "; " +
|
||||
"backgroundPosition: " + this.backgroundPosition + "; " +
|
||||
"backgroundSize: " + this.backgroundSize + "; "
|
||||
;
|
||||
"clipPath: " + this.clipPath + "; " + "backgroundColor: " + this.backgroundColor + "; "
|
||||
+ "backgroundImage: " + this.backgroundImage + "; " + "backgroundBitmap: " + this.backgroundBitmap
|
||||
+ "; " + "backgroundRepeat: " + this.backgroundRepeat + "; " + "backgroundPosition: "
|
||||
+ this.backgroundPosition + "; " + "backgroundSize: " + this.backgroundSize + "; ";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -823,18 +962,72 @@ public class BorderDrawable extends ColorDrawable implements BitmapOwner {
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private void generateBackgroundOutlinePath(Rect bounds) {
|
||||
if (backgroundOutlinePath != null && hasCache("backgroundOutlinePath")) {
|
||||
return;
|
||||
}
|
||||
setHasCache("backgroundOutlinePath");
|
||||
|
||||
if (backgroundOutlinePath == null) {
|
||||
backgroundOutlinePath = new Path();
|
||||
} else {
|
||||
backgroundOutlinePath.reset();
|
||||
}
|
||||
float[] backgroundRadii = { Math.max(0, borderTopLeftRadius), Math.max(0, borderTopLeftRadius),
|
||||
Math.max(0, borderTopRightRadius), Math.max(0, borderTopRightRadius),
|
||||
Math.max(0, borderBottomRightRadius), Math.max(0, borderBottomRightRadius),
|
||||
Math.max(0, borderBottomLeftRadius), Math.max(0, borderBottomLeftRadius) };
|
||||
backgroundOutlinePath.addRoundRect(new RectF(bounds), backgroundRadii, Path.Direction.CW);
|
||||
}
|
||||
|
||||
private void generateInnerBorderPath(Rect bounds) {
|
||||
if (innerBorderPath != null && hasCache("innerBorderPath")) {
|
||||
return;
|
||||
}
|
||||
setHasCache("innerBorderPath");
|
||||
|
||||
if (innerBorderPath == null) {
|
||||
innerBorderPath = new Path();
|
||||
} else {
|
||||
innerBorderPath.reset();
|
||||
}
|
||||
|
||||
float width = (float) bounds.width();
|
||||
float height = (float) bounds.height();
|
||||
|
||||
RectF borderInnerRect = new RectF(borderLeftWidth, borderTopWidth, width - borderRightWidth,
|
||||
height - borderBottomWidth);
|
||||
float[] borderInnerRadii = { Math.max(0, borderTopLeftRadius - borderLeftWidth),
|
||||
Math.max(0, borderTopLeftRadius - borderTopWidth), Math.max(0, borderTopRightRadius - borderRightWidth),
|
||||
Math.max(0, borderTopRightRadius - borderTopWidth),
|
||||
Math.max(0, borderBottomRightRadius - borderRightWidth),
|
||||
Math.max(0, borderBottomRightRadius - borderBottomWidth),
|
||||
Math.max(0, borderBottomLeftRadius - borderLeftWidth),
|
||||
Math.max(0, borderBottomLeftRadius - borderBottomWidth) };
|
||||
innerBorderPath.addRoundRect(borderInnerRect, borderInnerRadii, Path.Direction.CCW);
|
||||
}
|
||||
|
||||
public boolean shouldOutline() {
|
||||
return (android.os.Build.VERSION.SDK_INT >= 21 && getUniformBorderRadius() > 0 && !hasBorderWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getOutline(@NonNull Outline outline) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
Path backgroundPath = new Path();
|
||||
float[] backgroundRadii = {
|
||||
Math.max(0, borderTopLeftRadius), Math.max(0, borderTopLeftRadius),
|
||||
Math.max(0, borderTopRightRadius), Math.max(0, borderTopRightRadius),
|
||||
Math.max(0, borderBottomRightRadius), Math.max(0, borderBottomRightRadius),
|
||||
Math.max(0, borderBottomLeftRadius), Math.max(0, borderBottomLeftRadius)
|
||||
};
|
||||
backgroundPath.addRoundRect(new RectF(getBounds()), backgroundRadii, Path.Direction.CW);
|
||||
outline.setConvexPath(backgroundPath);
|
||||
if (this.clipPath != null) {
|
||||
// no clip!
|
||||
generateClipPath(this.clipPath, new RectF(getBounds()), density);
|
||||
outline.setConvexPath(this.clipPathPath);
|
||||
} else if (hasUniformBorder()) {
|
||||
// clip!
|
||||
outline.setRoundRect(getBounds(), Math.max(0, borderTopLeftRadius));
|
||||
} else {
|
||||
// no clip!
|
||||
generateBackgroundOutlinePath(getBounds());
|
||||
if (backgroundOutlinePath != null) {
|
||||
outline.setConvexPath(backgroundOutlinePath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Method supported on API 21 or higher");
|
||||
}
|
||||
|
@ -4,6 +4,14 @@
|
||||
package org.nativescript.widgets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
@ -11,19 +19,45 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
*
|
||||
*/
|
||||
public abstract class LayoutBase extends ViewGroup {
|
||||
private boolean passThroughParent;
|
||||
private boolean clipEnabled = true;
|
||||
private static Paint clipPaint;
|
||||
|
||||
public LayoutBase(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
setClipToBounds(clipEnabled);
|
||||
}
|
||||
|
||||
public LayoutBase(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setClipToBounds(boolean value) {
|
||||
clipEnabled = value;
|
||||
if (value) {
|
||||
// TODO: does it cost to enable it even if we actually
|
||||
// will still need to clip?
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
setClipToOutline(true);
|
||||
}
|
||||
} else if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
setClipToOutline(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,7 +107,8 @@ public abstract class LayoutBase extends ViewGroup {
|
||||
}
|
||||
|
||||
// LayoutBase.onTouchEvent(ev) execution means no interactive child view handled
|
||||
// the event so we let the event pass through to parent view of the layout container
|
||||
// the event so we let the event pass through to parent view of the layout
|
||||
// container
|
||||
// because passThroughParent is set to true
|
||||
return false;
|
||||
}
|
||||
@ -99,4 +134,45 @@ public abstract class LayoutBase extends ViewGroup {
|
||||
public void setPassThroughParent(boolean value) {
|
||||
this.passThroughParent = value;
|
||||
}
|
||||
|
||||
public boolean getClipEnabled() {
|
||||
return this.clipEnabled;
|
||||
}
|
||||
|
||||
public void setClipEnabled(boolean value) {
|
||||
this.clipEnabled = value;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
if (clipEnabled) {
|
||||
Drawable drawable = getBackground();
|
||||
if (drawable instanceof BorderDrawable) {
|
||||
Path clippingPath = ((BorderDrawable) drawable).getClippingPath();
|
||||
// if no clippingPath either it is unnecessary or handled by outline
|
||||
if (clippingPath != null) {
|
||||
if (LayoutBase.clipPaint == null) {
|
||||
LayoutBase.clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
// LayoutBase.clipPaint.setColor(Color.WHITE);
|
||||
LayoutBase.clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
}
|
||||
int saveCount;
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21) {
|
||||
saveCount = canvas.saveLayer(new android.graphics.RectF(0.0f, 0.0f, width, height), null);
|
||||
} else {
|
||||
saveCount = canvas.saveLayer(0.0f, 0.0f, width, height, null, Canvas.ALL_SAVE_FLAG);
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
// we dont use clipPath as it is not antialiased
|
||||
canvas.drawPath(clippingPath, LayoutBase.clipPaint);
|
||||
canvas.restoreToCount(saveCount);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user