Android中的TextView与大纲

pokxtpni  于 2023-05-27  发布在  Android
关注(0)|答案(1)|浏览(168)

我想在Android中有一个双笔划的textview,这是不原生支持的(甚至不是一个单一的笔划)。遵循Android textview大纲文本就可以轻松实现.

public class StrokedTextView extends androidx.appcompat.widget.AppCompatTextView {

// fields
private int strokeColorW, strokeColorB;
private float strokeWidthW, strokeWidthB;

// constructors
public StrokedTextView(Context context) {
    this(context, null, 0);
}

public StrokedTextView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public StrokedTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    strokeColorW = context.getColor(R.color.white);
    strokeWidthW = dpToPx(context, 2);
    strokeColorB = context.getColor(R.color.main_color_dark);
    strokeWidthB = dpToPx(context, 3);
}

// overridden methods
@Override
protected void onDraw(Canvas canvas) {
    //set paint to fill mode
    Paint p = getPaint();
    p.setStyle(Paint.Style.FILL);
    //draw the fill part of text
    super.onDraw(canvas);
    //save the text color
    int currentTextColor = getCurrentTextColor();
    //set paint to stroke mode and specify
    //stroke 1 color and width
    p.setStyle(Paint.Style.STROKE);
    p.setStrokeWidth(strokeWidthB);
    setTextColor(strokeColorB);
    //draw text stroke
    super.onDraw(canvas);
    //set paint to stroke mode and specify
    //stroke 2 color and width
    p.setStyle(Paint.Style.STROKE);
    p.setStrokeWidth(strokeWidthW);
    setTextColor(strokeColorW);
    //draw text stroke
    super.onDraw(canvas);
    //revert the color back to the one
    //initially specified
    setTextColor(currentTextColor);
}

public static int dpToPx(Context context, float dp)
{
    final float scale= context.getResources().getDisplayMetrics().density;
    return (int) (dp * scale + 0.5f);
}

}
问题是setTextColor调用无效,因此这福尔斯无限次调用onDraw。正如一些人建议的那样,我尝试用一个标志来控制它,该标志指示无效是否由setTextColor引起。但它仍然无限地调用onDraw

private boolean isDrawing = false;

@Override
public void invalidate() {
    if(isDrawing) return;
    super.invalidate();
}

@Override
protected void onDraw(Canvas canvas) {
    isDrawing = true;

    Paint p = getPaint();
    p.setStyle(Paint.Style.FILL);
    super.onDraw(canvas);
    int currentTextColor = getCurrentTextColor();

    p.setStyle(Paint.Style.STROKE);
    p.setStrokeWidth(strokeWidthB);
    setTextColor(strokeColorB);
    super.onDraw(canvas);

    p.setStyle(Paint.Style.STROKE);
    p.setStrokeWidth(strokeWidthW);
    setTextColor(strokeColorW);
    super.onDraw(canvas);

    setTextColor(currentTextColor);

    isDrawing = false;
}

正如其他用户在这个问题中所说的那样,我也尝试过使用Reflection来访问TextView的private字段并强行设置它:

private void setColor(int color){
    Field field = TextView.class.getDeclaredField("mCurTextColor");
    field.setAccessible(true);
    field.set(this, color);
}

但是,它表示Reflective access to mCurTextColor will throw an Exception when targeting API 33 and above
所以我想问的是,是否有人看到了一种克服这个问题的方法,与我已经尝试过并失败的方法不同。

gab6jxml

gab6jxml1#

解决方案是从头开始创建自己的自定义视图。

public class StrokedTextView extends View {
private final Paint TEXT_PAINT;
private final Paint WHITE_BORDER_PAINT;
private final Paint BROWN_BORDER_PAINT;

private String text;

private int desiredWidth, desiredHeight;
private final int bigBorderSize, halfMargin;

// constructors
public StrokedTextView(Context context) {
    this(context, null, 0);
}

public StrokedTextView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public StrokedTextView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    float textSize = 0;
    int textColor = context.getColor(R.color.main_color);
    text = "";
    if(attrs != null) {
        @SuppressLint("CustomViewStyleable") TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.StrokedTextAttrs);
        textSize = a.getDimensionPixelSize(R.styleable.StickerTextAttrs_textSize, 0);
        textColor = a.getColor(R.styleable.StrokedTextAttrs_textColor, textColor);
        text = a.getString(R.styleable.StrokedTextAttrs_text);
        a.recycle();
    }

    TEXT_PAINT = new Paint();
    TEXT_PAINT.setTextSize(textSize);
    TEXT_PAINT.setStyle(Paint.Style.FILL);
    TEXT_PAINT.setColor(textColor);

    int smallBorderSize = dpToPx(context, 2);
    bigBorderSize = smallBorderSize * 3;
    halfMargin = bigBorderSize / 2;

    WHITE_BORDER_PAINT = new Paint();
    WHITE_BORDER_PAINT.setTextSize(textSize);
    WHITE_BORDER_PAINT.setStyle(Paint.Style.STROKE);
    WHITE_BORDER_PAINT.setStrokeWidth(bigBorderSize);
    WHITE_BORDER_PAINT.setColor(context.getColor(R.color.white));

    BROWN_BORDER_PAINT = new Paint();
    BROWN_BORDER_PAINT.setTextSize(textSize);
    BROWN_BORDER_PAINT.setStyle(Paint.Style.STROKE);
    BROWN_BORDER_PAINT.setStrokeWidth(smallBorderSize);
    BROWN_BORDER_PAINT.setColor(context.getColor(R.color.main_color_dark));
    measure();
}

public void setText(String t){
    text = t;
    measure();
    invalidate();
    requestLayout();
}

private void measure(){
    Rect bounds = new Rect();
    TEXT_PAINT.getTextBounds(text, 0, text.length(), bounds);
    desiredHeight = bounds.height() + bigBorderSize;
    desiredWidth = bounds.width() + bigBorderSize;
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    int bottom = getHeight() - halfMargin;
    canvas.drawText(text, halfMargin, bottom, WHITE_BORDER_PAINT);
    canvas.drawText(text, halfMargin, bottom, BROWN_BORDER_PAINT);
    canvas.drawText(text, halfMargin, bottom, TEXT_PAINT);
}

public static int dpToPx(Context context, float dp)
{
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dp * scale + 0.5f);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int width;
    int height;

    //Measure Width
    if (widthMode == MeasureSpec.EXACTLY) {
        //Must be this size
        width = widthSize;
    } else if (widthMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        width = Math.min(desiredWidth, widthSize);
    } else {
        //Be whatever you want
        width = desiredWidth;
    }

    //Measure Height
    if (heightMode == MeasureSpec.EXACTLY) {
        //Must be this size
        height = heightSize;
    } else if (heightMode == MeasureSpec.AT_MOST) {
        //Can't be bigger than...
        height = Math.min(desiredHeight, heightSize);
    } else {
        //Be whatever you want
        height = desiredHeight;
    }

    setMeasuredDimension(width, height);
}

attr.xml中:

<declare-styleable name="StickerTextAttrs">
    <attr name="textColor" format="color"/>
    <attr name="textSize" format="dimension"/>
    <attr name="text" format="string"/>
</declare-styleable>

相关问题