android TextView 可自定义图标的带角标文字组件

TextDrawable是一个自定义的Android TextView组件,它可以显示左侧、右侧和顶部的Drawable图标,并支持添加角标。角标可以自定义文字、颜色、大小等属性。该组件通过XML属性进行配置,例如设置图标尺寸和角标显示等。

效果图
效果图

TextDrawable.java

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntRange;
import androidx.appcompat.widget.AppCompatTextView;

import com.yourpackage.R;

import org.jetbrains.annotations.NotNull;

/**
 * @author octopus yan
 * @email octopus_yan@foxmail.com
 * @Description 可自定义图标的文字组件
 * @createTime 2021-12-06 14:58:16
 */
public class TextDrawable extends AppCompatTextView {
    private Drawable drawableLeft;
    private Drawable drawableRight;
    private Drawable drawableTop;
    private int tmpTopHeight = 0;
    private boolean showCornerMark;
    private String cornerMarkText;
    private Drawable cornerMarkBackground;
    private Integer cornerMarkBackgroundColor;
    private int cornerMarkWidth;
    private int cornerMarkHeight;
    private int cornerMarkTextSize;
    @ColorInt
    private int cornerMarkTextColor;
    private int cornerMarkTextPadding;
    private Paint cornerMarkPaint;
    private Paint cornerMarkBgPaint;
    private Path cornerMarkBgPath;
    private int leftWidth;
    private int rightWidth;
    private int topWidth;
    private int leftHeight;
    private int rightHeight;
    private int topHeight;
    private final Context mContext;

    private final int defaultDrawableSize;
    private final int defaultCornerMarkSize;

    private int viewWidth;
    private int viewHeight;

    public TextDrawable(Context context) {
        this(context, null);
    }

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

    public TextDrawable(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        defaultDrawableSize = dip2px(context, 20);
        defaultCornerMarkSize = dip2px(context, 5);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TextDrawable);
        drawableLeft = typedArray.getDrawable(R.styleable.TextDrawable_leftDrawable);
        drawableRight = typedArray.getDrawable(R.styleable.TextDrawable_rightDrawable);
        drawableTop = typedArray.getDrawable(R.styleable.TextDrawable_topDrawable);
        if (drawableLeft != null) {
            leftWidth = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_leftDrawableWidth, defaultDrawableSize);
            leftHeight = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_leftDrawableHeight, defaultDrawableSize);
        }
        if (drawableRight != null) {
            rightWidth = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_rightDrawableWidth, defaultDrawableSize);
            rightHeight = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_rightDrawableHeight, defaultDrawableSize);
        }
        if (drawableTop != null) {
            topWidth = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_topDrawableWidth, defaultDrawableSize);
            topHeight = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_topDrawableHeight, defaultDrawableSize);
        }

        // 角标
        showCornerMark = typedArray.getBoolean(R.styleable.TextDrawable_cornerMarkShow, false);

        if (showCornerMark) {
            cornerMarkText = typedArray.getString(R.styleable.TextDrawable_cornerMark);
            try {
                cornerMarkBackground = typedArray.getDrawable(R.styleable.TextDrawable_cornerMarkBackgroud);
            } finally {
                if (cornerMarkBackground == null) {
                    cornerMarkBackgroundColor = typedArray.getColor(R.styleable.TextDrawable_cornerMarkBackgroud, context.getColor(R.color.transparent));
                }
            }
            cornerMarkWidth = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_cornerMarkWidth, defaultCornerMarkSize);
            cornerMarkHeight = typedArray.getDimensionPixelOffset(R.styleable.TextDrawable_cornerMarkHeight, defaultCornerMarkSize);
            cornerMarkTextSize = typedArray.getDimensionPixelSize(R.styleable.TextDrawable_cornerMarkTextSize, dip2px(context, 10));
            cornerMarkTextColor = typedArray.getColor(R.styleable.TextDrawable_cornerMarkTextColor, context.getColor(R.color.red));
            cornerMarkTextPadding = typedArray.getDimensionPixelSize(R.styleable.TextDrawable_cornerMarkPadding, dip2px(context, 4));

            cornerMarkPaint = new Paint();
            cornerMarkPaint.setTextSize(cornerMarkTextSize);
            cornerMarkPaint.setColor(cornerMarkTextColor);
            cornerMarkPaint.setTextAlign(Paint.Align.LEFT);// 右对齐
        }
    }

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int compoundPaddingTop = getCompoundPaddingTop();
        final int compoundPaddingRight = getCompoundPaddingRight();
        if (drawableLeft != null) {
            drawableLeft.setBounds(0, 0, leftWidth, leftHeight);
        }
        if (drawableRight != null) {
            drawableRight.setBounds(0, 0, rightWidth, rightHeight);
        }
        if (drawableTop != null) {
            drawableTop.setBounds(0, 0, topWidth, topHeight);
            tmpTopHeight = topHeight;
        }
        if (cornerMarkBackground != null && showCornerMark) {
            cornerMarkBackground.setBounds(
                    viewWidth - cornerMarkWidth - compoundPaddingRight - 2 * cornerMarkTextPadding,
                    compoundPaddingTop - tmpTopHeight,
                    viewWidth - compoundPaddingRight,
                    compoundPaddingTop + cornerMarkHeight + 2 * cornerMarkTextPadding - tmpTopHeight
            );
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewWidth = w;
        viewHeight = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int compoundPaddingTop = getCompoundPaddingTop();
        final int compoundPaddingRight = getCompoundPaddingRight();
        this.setCompoundDrawables(drawableLeft, drawableTop, drawableRight, null);

        // 画角标
        if (showCornerMark && cornerMarkText != null) {
            Paint.FontMetricsInt fontMetricsInt = cornerMarkPaint.getFontMetricsInt();
            int top = fontMetricsInt.top;
            int bottom = fontMetricsInt.bottom;

            // 角标文字宽高计算
            float textWidth = cornerMarkPaint.measureText(cornerMarkText);
            float textHeight = (bottom - top) / 2f + bottom;
            if (cornerMarkWidth < textWidth) cornerMarkWidth = (int) textWidth;
            if (cornerMarkHeight < textHeight) cornerMarkHeight = (int) textHeight;

            // 先画背景
            if (cornerMarkBackground != null) {
                cornerMarkBackground.draw(canvas);
            } else {
                if (cornerMarkBgPaint == null) {
                    cornerMarkBgPath = new Path();
                    cornerMarkBgPaint = new Paint();
                    cornerMarkBgPaint.setColor(cornerMarkBackgroundColor);
                }
                //将原点移动到原点
                canvas.translate(0, 0);
                //绘制角标背景
                cornerMarkBgPath.moveTo(viewWidth - textWidth - compoundPaddingRight - 2 * cornerMarkTextPadding, compoundPaddingTop - tmpTopHeight); //将Path的起点移动到A
                cornerMarkBgPath.lineTo(viewWidth - compoundPaddingRight, compoundPaddingTop - tmpTopHeight); //连接AB
                cornerMarkBgPath.lineTo(viewWidth - compoundPaddingRight, textHeight + compoundPaddingTop + 2 * cornerMarkTextPadding - tmpTopHeight); //连接BC
                cornerMarkBgPath.lineTo(viewWidth - textWidth - compoundPaddingRight - 2 * cornerMarkTextPadding, textHeight + compoundPaddingTop + 2 * cornerMarkTextPadding - tmpTopHeight); //连接CD
                cornerMarkBgPath.close(); //闭合Path,即连接DA
                canvas.drawPath(cornerMarkBgPath, cornerMarkBgPaint);
            }

            // 角标内容
            canvas.drawText(
                    cornerMarkText,
                    viewWidth - textWidth - compoundPaddingRight - cornerMarkTextPadding,
                    (bottom - top) / 2f + 1 + compoundPaddingTop + cornerMarkTextPadding - tmpTopHeight,
                    cornerMarkPaint
            );
        }
    }

    /**
     * 设置左侧图片并重绘
     */
    public void setDrawableLeft(Drawable drawableLeft) {
        this.drawableLeft = drawableLeft;
        invalidate();
    }

    /**
     * 设置左侧图片并重绘
     */
    public void setDrawableLeft(@DrawableRes int drawableLeftRes) {
        this.drawableLeft = mContext.getResources().getDrawable(drawableLeftRes);
        invalidate();
    }

    /**
     * 设置右侧图片并重绘
     */
    public void setDrawableRight(Drawable drawableRight) {
        this.drawableRight = drawableLeft;
        invalidate();
    }

    /**
     * 设置右侧图片并重绘
     */
    public void setDrawableRight(@DrawableRes int drawableRightRes) {
        this.drawableRight = mContext.getResources().getDrawable(drawableRightRes);
        invalidate();
    }

    /**
     * 设置上部图片并重绘
     */
    public void setDrawable(Drawable drawableTop) {
        this.drawableTop = drawableTop;
        invalidate();
    }

    /**
     * 设置右侧图片并重绘
     */
    public void setDrawableTop(@DrawableRes int drawableTopRes) {
        this.drawableTop = mContext.getResources().getDrawable(drawableTopRes);
        invalidate();
    }

    /**
     * 设置角标文字
     */
    public void setCornerMarkText(@NotNull String text) {
        this.cornerMarkText = text;
    }

    /**
     * 设置角标宽
     */
    public void setCornerMarkWidth(@IntRange(from = 0)int width) {
        this.cornerMarkWidth = width;
    }

    /**
     * 设置角标高
     */
    public void setCornerMarkHeight(@IntRange(from = 0)int height) {
        this.cornerMarkHeight = height;
    }

    /**
     * 设置角标文字大小 单位:px
     */
    public void setCornerMarkTextSize(@IntRange(from = 0)int textSize) {
        this.cornerMarkTextSize = textSize;
        if (cornerMarkPaint != null) {
            cornerMarkPaint.setTextSize(textSize);
        }
    }

    /**
     * 设置角标文字颜色
     */
    public void setCornerMarkTextColor(@ColorInt int color) {
        this.cornerMarkTextColor = color;
        if (cornerMarkPaint != null) {
            cornerMarkPaint.setColor(color);
        }
    }

    /**
     * 设置角标内边距
     */
    public void setCornerMarkTextPadding(@IntRange(from = 0) int padding) {
        this.cornerMarkTextPadding = padding;
    }
}


attrs.xml

	<declare-styleable name="TextDrawable">
        <!--左图标-->
        <attr name="leftDrawable" format="reference" />
        <attr name="leftDrawableWidth" format="dimension" />
        <attr name="leftDrawableHeight" format="dimension" />
        <!--右图标-->
        <attr name="rightDrawable" format="reference" />
        <attr name="rightDrawableWidth" format="dimension" />
        <attr name="rightDrawableHeight" format="dimension" />
        <!--上图标-->
        <attr name="topDrawable" format="reference" />
        <attr name="topDrawableWidth" format="dimension" />
        <attr name="topDrawableHeight" format="dimension" />
        <!--角标-->
        <attr name="cornerMarkShow" format="boolean" />
        <attr name="cornerMark" format="integer|string" />
        <attr name="cornerMarkPadding" format="dimension" />
        <attr name="cornerMarkTextSize" format="dimension" />
        <attr name="cornerMarkTextColor" format="color" />
        <attr name="cornerMarkBackgroud" format="reference" />
        <attr name="cornerMarkWidth" format="dimension" />
        <attr name="cornerMarkHeight" format="dimension" />
    </declare-styleable>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值