fix text node shadow & gradient conflict
This commit is contained in:
parent
0a305b3104
commit
5782a0d161
@ -0,0 +1,174 @@
|
|||||||
|
package pub.doric.shader;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Shader;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
@SuppressLint("AppCompatCustomView")
|
||||||
|
public class DoricTextView extends TextView {
|
||||||
|
|
||||||
|
private boolean strikethrough = false;
|
||||||
|
private boolean underline = false;
|
||||||
|
|
||||||
|
private float shadowAlpha = 0;
|
||||||
|
private float shadowRadius = 0;
|
||||||
|
private float shadowDx = 0;
|
||||||
|
private float shadowDy = 0;
|
||||||
|
private int shadowColor = Color.TRANSPARENT;
|
||||||
|
|
||||||
|
private float gradientAngle = 0;
|
||||||
|
private int[] gradientColors = null;
|
||||||
|
private float[] gradientPositions = null;
|
||||||
|
|
||||||
|
public DoricTextView(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoricTextView(Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoricTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setShadow(float alpha, float radius, float dx, float dy, int color) {
|
||||||
|
this.shadowAlpha = alpha;
|
||||||
|
this.shadowRadius = radius;
|
||||||
|
this.shadowDx = dx;
|
||||||
|
this.shadowDy = dy;
|
||||||
|
this.shadowColor = color;
|
||||||
|
|
||||||
|
getPaint().setAlpha((int) (255 * alpha));
|
||||||
|
getPaint().setShadowLayer(radius, dx, dy, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasShadow() {
|
||||||
|
return this.shadowAlpha > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGradient(float angle, int[] colors, float[] positions) {
|
||||||
|
this.gradientAngle = angle;
|
||||||
|
this.gradientColors = colors;
|
||||||
|
this.gradientPositions = positions;
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasGradient() {
|
||||||
|
return this.gradientColors != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnderline(boolean underline) {
|
||||||
|
this.underline = underline;
|
||||||
|
getPaint().setUnderlineText(underline);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStrikethrough(boolean strikethrough) {
|
||||||
|
this.strikethrough = strikethrough;
|
||||||
|
getPaint().setStrikeThruText(strikethrough);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
|
||||||
|
if (hasGradient()) {
|
||||||
|
if (hasShadow() || strikethrough || underline) {
|
||||||
|
getPaint().setShader(null);
|
||||||
|
|
||||||
|
// draw the shadow
|
||||||
|
if (hasShadow()) {
|
||||||
|
// shadowColor must be opaque.
|
||||||
|
setTextColor(0x00ffffff);
|
||||||
|
getPaint().setAlpha((int) (255 * shadowAlpha));
|
||||||
|
getPaint().setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor);
|
||||||
|
}
|
||||||
|
getPaint().setStrikeThruText(strikethrough);
|
||||||
|
getPaint().setUnderlineText(underline);
|
||||||
|
|
||||||
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw the gradient filled text
|
||||||
|
if (hasShadow()) {
|
||||||
|
getPaint().clearShadowLayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// gradient colors must be opaque, too.
|
||||||
|
setGradientTextColor(this, this.gradientAngle, this.gradientColors, this.gradientPositions);
|
||||||
|
super.onDraw(canvas);
|
||||||
|
} else {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setGradientTextColor(final TextView textView, final float angle, final int[] colors, final float[] positions) {
|
||||||
|
final Rect textBound = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
|
||||||
|
|
||||||
|
final Layout layout = textView.getLayout();
|
||||||
|
|
||||||
|
if (layout == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < textView.getLineCount(); i++) {
|
||||||
|
float left = layout.getLineLeft(i);
|
||||||
|
float right = layout.getLineRight(i);
|
||||||
|
if (left < textBound.left) textBound.left = (int) left;
|
||||||
|
if (right > textBound.right) textBound.right = (int) right;
|
||||||
|
}
|
||||||
|
textBound.top = layout.getLineTop(0);
|
||||||
|
textBound.bottom = layout.getLineBottom(textView.getLineCount() - 1);
|
||||||
|
if (textView.getIncludeFontPadding()) {
|
||||||
|
Paint.FontMetrics fontMetrics = textView.getPaint().getFontMetrics();
|
||||||
|
textBound.top += (fontMetrics.ascent - fontMetrics.top);
|
||||||
|
textBound.bottom -= (fontMetrics.bottom - fontMetrics.descent);
|
||||||
|
}
|
||||||
|
|
||||||
|
double angleInRadians = Math.toRadians(angle);
|
||||||
|
|
||||||
|
double r = Math.sqrt(Math.pow(textBound.bottom - textBound.top, 2) +
|
||||||
|
Math.pow(textBound.right - textBound.left, 2)) / 2;
|
||||||
|
|
||||||
|
float centerX = textBound.left + (textBound.right - textBound.left) / 2.f;
|
||||||
|
float centerY = textBound.top + (textBound.bottom - textBound.top) / 2.f;
|
||||||
|
|
||||||
|
float startX = (float) (centerX - r * Math.cos(angleInRadians));
|
||||||
|
float startY = (float) (centerY + r * Math.sin(angleInRadians));
|
||||||
|
|
||||||
|
float endX = (float) (centerX + r * Math.cos(angleInRadians));
|
||||||
|
float endY = (float) (centerY - r * Math.sin(angleInRadians));
|
||||||
|
|
||||||
|
Shader textShader = new LinearGradient(startX, startY, endX, endY, colors, positions,
|
||||||
|
Shader.TileMode.CLAMP);
|
||||||
|
|
||||||
|
textView.setTextColor(0xffffffff);
|
||||||
|
textView.getPaint().setShader(textShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
strikethrough = false;
|
||||||
|
underline = false;
|
||||||
|
|
||||||
|
shadowAlpha = 0;
|
||||||
|
shadowRadius = 0;
|
||||||
|
shadowDx = 0;
|
||||||
|
shadowDy = 0;
|
||||||
|
shadowColor = Color.TRANSPARENT;
|
||||||
|
|
||||||
|
gradientAngle = 0;
|
||||||
|
gradientColors = null;
|
||||||
|
gradientPositions = null;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
@ -16,20 +16,14 @@
|
|||||||
package pub.doric.shader;
|
package pub.doric.shader;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.LinearGradient;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.Shader;
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Layout;
|
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.core.content.res.ResourcesCompat;
|
import androidx.core.content.res.ResourcesCompat;
|
||||||
|
|
||||||
@ -60,14 +54,14 @@ import pub.doric.utils.ThreadMode;
|
|||||||
* @CreateDate: 2019-07-20
|
* @CreateDate: 2019-07-20
|
||||||
*/
|
*/
|
||||||
@DoricPlugin(name = "Text")
|
@DoricPlugin(name = "Text")
|
||||||
public class TextNode extends ViewNode<TextView> {
|
public class TextNode extends ViewNode<DoricTextView> {
|
||||||
public TextNode(DoricContext doricContext) {
|
public TextNode(DoricContext doricContext) {
|
||||||
super(doricContext);
|
super(doricContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TextView build() {
|
protected DoricTextView build() {
|
||||||
TextView tv = new TextView(getContext());
|
DoricTextView tv = new DoricTextView(getContext());
|
||||||
tv.setGravity(Gravity.CENTER);
|
tv.setGravity(Gravity.CENTER);
|
||||||
tv.setMaxLines(1);
|
tv.setMaxLines(1);
|
||||||
tv.setSingleLine(true);
|
tv.setSingleLine(true);
|
||||||
@ -90,7 +84,7 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void blend(final TextView view, final String name, final JSValue prop) {
|
protected void blend(final DoricTextView view, final String name, final JSValue prop) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "text":
|
case "text":
|
||||||
if (!prop.isString()) {
|
if (!prop.isString()) {
|
||||||
@ -178,7 +172,7 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setGradientTextColor(view, angle, colors, locations);
|
mView.setGradient(angle, colors, locations);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -312,12 +306,12 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
break;
|
break;
|
||||||
case "strikethrough":
|
case "strikethrough":
|
||||||
if (prop.isBoolean()) {
|
if (prop.isBoolean()) {
|
||||||
view.getPaint().setStrikeThruText(prop.asBoolean().value());
|
view.setStrikethrough(prop.asBoolean().value());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "underline":
|
case "underline":
|
||||||
if (prop.isBoolean()) {
|
if (prop.isBoolean()) {
|
||||||
view.getPaint().setUnderlineText(prop.asBoolean().value());
|
view.setUnderline(prop.asBoolean().value());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "htmlText":
|
case "htmlText":
|
||||||
@ -389,8 +383,8 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
break;
|
break;
|
||||||
case "shadow":
|
case "shadow":
|
||||||
if (prop.isObject()) {
|
if (prop.isObject()) {
|
||||||
mView.setAlpha((prop.asObject().getProperty("opacity").asNumber().toFloat()));
|
mView.setShadow(
|
||||||
mView.setShadowLayer(
|
prop.asObject().getProperty("opacity").asNumber().toFloat(),
|
||||||
prop.asObject().getProperty("radius").asNumber().toFloat(),
|
prop.asObject().getProperty("radius").asNumber().toFloat(),
|
||||||
DoricUtils.dp2px(prop.asObject().getProperty("offsetX").asNumber().toFloat()),
|
DoricUtils.dp2px(prop.asObject().getProperty("offsetX").asNumber().toFloat()),
|
||||||
DoricUtils.dp2px(prop.asObject().getProperty("offsetY").asNumber().toFloat()),
|
DoricUtils.dp2px(prop.asObject().getProperty("offsetY").asNumber().toFloat()),
|
||||||
@ -404,50 +398,6 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setGradientTextColor(final TextView textView, final float angle, final int[] colors, final float[] positions) {
|
|
||||||
final Rect textBound = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
|
|
||||||
|
|
||||||
final Layout layout = textView.getLayout();
|
|
||||||
|
|
||||||
if (layout == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < textView.getLineCount(); i++) {
|
|
||||||
float left = layout.getLineLeft(i);
|
|
||||||
float right = layout.getLineRight(i);
|
|
||||||
if (left < textBound.left) textBound.left = (int) left;
|
|
||||||
if (right > textBound.right) textBound.right = (int) right;
|
|
||||||
}
|
|
||||||
textBound.top = layout.getLineTop(0);
|
|
||||||
textBound.bottom = layout.getLineBottom(textView.getLineCount() - 1);
|
|
||||||
if (textView.getIncludeFontPadding()) {
|
|
||||||
Paint.FontMetrics fontMetrics = textView.getPaint().getFontMetrics();
|
|
||||||
textBound.top += (fontMetrics.ascent - fontMetrics.top);
|
|
||||||
textBound.bottom -= (fontMetrics.bottom - fontMetrics.descent);
|
|
||||||
}
|
|
||||||
|
|
||||||
double angleInRadians = Math.toRadians(angle);
|
|
||||||
|
|
||||||
double r = Math.sqrt(Math.pow(textBound.bottom - textBound.top, 2) +
|
|
||||||
Math.pow(textBound.right - textBound.left, 2)) / 2;
|
|
||||||
|
|
||||||
float centerX = textBound.left + (textBound.right - textBound.left) / 2.f;
|
|
||||||
float centerY = textBound.top + (textBound.bottom - textBound.top) / 2.f;
|
|
||||||
|
|
||||||
float startX = (float) (centerX - r * Math.cos(angleInRadians));
|
|
||||||
float startY = (float) (centerY + r * Math.sin(angleInRadians));
|
|
||||||
|
|
||||||
float endX = (float) (centerX + r * Math.cos(angleInRadians));
|
|
||||||
float endY = (float) (centerY - r * Math.sin(angleInRadians));
|
|
||||||
|
|
||||||
Shader textShader = new LinearGradient(startX, startY, endX, endY, colors, positions,
|
|
||||||
Shader.TileMode.CLAMP);
|
|
||||||
|
|
||||||
textView.setTextColor(Color.WHITE);
|
|
||||||
textView.getPaint().setShader(textShader);
|
|
||||||
textView.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File createFile(byte[] bfile, String filePath, String fileName) throws IOException {
|
private static File createFile(byte[] bfile, String filePath, String fileName) throws IOException {
|
||||||
BufferedOutputStream bos = null;
|
BufferedOutputStream bos = null;
|
||||||
@ -496,7 +446,5 @@ public class TextNode extends ViewNode<TextView> {
|
|||||||
mView.setMaxLines(1);
|
mView.setMaxLines(1);
|
||||||
mView.setSingleLine(true);
|
mView.setSingleLine(true);
|
||||||
mView.setEllipsize(TextUtils.TruncateAt.END);
|
mView.setEllipsize(TextUtils.TruncateAt.END);
|
||||||
mView.getPaint().setStrikeThruText(false);
|
|
||||||
mView.getPaint().setUnderlineText(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,31 @@ class TextDemo extends Panel {
|
|||||||
vlayout(
|
vlayout(
|
||||||
[
|
[
|
||||||
title("Text Demo"),
|
title("Text Demo"),
|
||||||
|
text({
|
||||||
|
text: '十万钻石场',
|
||||||
|
textSize: 60,
|
||||||
|
font: 'PingFangSC-Medium',
|
||||||
|
textAlignment: Gravity.Center,
|
||||||
|
shadow: {
|
||||||
|
color: Color.parse('#640000'),
|
||||||
|
opacity: 0.49,
|
||||||
|
radius: 0.5,
|
||||||
|
offsetX: 0,
|
||||||
|
offsetY: 1,
|
||||||
|
},
|
||||||
|
textColor: {
|
||||||
|
colors: [Color.parse('#FFFFFF'), Color.parse('#FFE6A5'), Color.parse('#FFE39B'), Color.parse('#FFF0CE')],
|
||||||
|
locations: [0.0, 0.7, 0.9, 1.0],
|
||||||
|
orientation: GradientOrientation.TOP_BOTTOM,
|
||||||
|
}
|
||||||
|
}).apply({
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.FIT,
|
||||||
|
heightSpec: LayoutSpec.FIT,
|
||||||
|
margin: { top: 131 },
|
||||||
|
alignment: Gravity.CenterX,
|
||||||
|
}
|
||||||
|
}),
|
||||||
input({
|
input({
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
|
Reference in New Issue
Block a user