Android custom control (entry level) - digital display

Posted by oni-kun on Sat, 23 Nov 2019 21:39:10 +0100

demand

1. The number is 1 digit and the circle is displayed
2. The number is 2-digit drawing stretch, left and right semicircles
3. If the number is greater than 999, 999 will be displayed+
4. Custom text color, custom background color

Effect (well, it looks low)

It's not complicated, but it's good as a calculation problem

1. Custom attribute
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--count TextView-->
    <declare-styleable name="CountTextView">
        <attr name="z_ctv_font_size" format="reference|dimension"/>
        <attr name="z_ctv_num" format="integer"/>
        <attr name="z_bg_color" format="reference|color"/>
        <attr name="z_txt_color" format="reference|color"/>
    </declare-styleable>
</resources>
2. analysis

Use rounded rectangle to draw background, Paint.getTextBounds to get text boundary rectangle
1). Draw the text first, and make the left vertex of the text coincide with the left vertex of the screen
2). Through calculation, draw two vertices of the rounded rectangle with one number (as shown below)
3) control the X of the two vertices of the rounded rectangle by the number of digits
4). Make the left vertex of the rounded rectangle at the canvas vertex through the canvas translation
5). Calculate the width and height of the rounded rectangle, and set the View size

public class CountTextView extends View {
    private int mCtvFontSize = sp(100);
    private int mCtvNum = 5;
    private int mCtvBgColor = 0xffBFF3F7;
    private int mCtvTxtColor = Color.WHITE;
    private Paint mPaint;//Master brush
    private Paint mTxtPaint;
    private Rect mRect;
    private String mStr;
    private int mOffSet;

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

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

    public CountTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CountTextView);
        mCtvFontSize = (int) a.getDimension(R.styleable.CountTextView_z_ctv_font_size, mCtvFontSize);
        mCtvNum = a.getInteger(R.styleable.CountTextView_z_ctv_num, mCtvNum);
        mCtvTxtColor = a.getColor(R.styleable.CountTextView_z_txt_color, mCtvTxtColor);
        mCtvBgColor = a.getColor(R.styleable.CountTextView_z_bg_color, mCtvBgColor);
        a.recycle();
        init();
    }

    private void init() {
        //Initialize main brush
        mTxtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTxtPaint.setColor(mCtvTxtColor);
        mTxtPaint.setTextSize(mCtvFontSize);

        mRect = new Rect();
        mStr = mCtvNum + "";
        if (mCtvNum >= 1000) {
            mStr = "999+";
        }
        mTxtPaint.getTextBounds(mStr, 0, mStr.length(), mRect);
        int AChartLen = mRect.width() / mStr.length();
        mOffSet = (int) ((mStr.length() - 1) * AChartLen * 0.7f);

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(mCtvBgColor);
        mPaint.setStrokeWidth(mRect.height());
        mPaint.setStrokeCap(Paint.Cap.ROUND);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //Offset of text left from center
        int offsetX = mRect.height() - mRect.width() / 2 + mOffSet;
        setMeasuredDimension(2 * offsetX + mRect.width(), 2 * mRect.height());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        int offsetX = mRect.height() - mRect.width() / 2 + mOffSet;
        canvas.translate(offsetX, mRect.height() / 2);
        //Rounded rectangle top left
        int topX = mRect.width() / 2 - mRect.height();
        int topY = -mRect.height() / 2;
        //Rounded rectangle bottom right
        int bottomX = mRect.height() + mRect.width() / 2;
        int bottomY = mRect.height() / 2 + mRect.height();
        
        canvas.drawRoundRect(topX - mOffSet, topY, bottomX + mOffSet, bottomY,
                mRect.height(), mRect.height(), mPaint);

        canvas.drawText(mStr, 0, mRect.height(), mTxtPaint);
        canvas.restore();
    }

    private int sp(float sp) {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }
}
3. use
<com.toly1994.c.view.CountTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:z_ctv_font_size="40sp"
    app:z_ctv_num="30"/>

Topics: Android Attribute xml encoding