Custom TextView - custom View, custom attribute base case

User defined view is the basis of Android development. There are not too many difficulties in itself, and there are not many knowledge points to be considered, mainly including canvas drawing related knowledge, view control measurement, user-defined attributes, etc. in this case, we try to demonstrate the above knowledge points through a user-defined control displaying text, so as to lay a good foundation for user-defined view. To understand this case, you need to have a certain foundation for the user-defined view. If you don't know anything about the user-defined view, it is recommended to read the case after consulting the relevant knowledge. Because there is no detailed step introduction in the case, only the core knowledge points are explained and annotated.

 

Custom TextView class:

The core knowledge points have been annotated. I hope it can help you to understand the code.

package hongzhen.com.defineviewdemo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import hongzhen.com.defineviewdemo.R;

/**
 * Custom TextView control for displaying text
 * Implement the custom attribute src - text content text - text color text - text size BG width - control width BG height - control height
 * Consider padding attribute
 * Consider style settings
 */
public class TextView extends View {
    private static final String TAG = "TextView";
    private Paint paint;
    private String mText = "";
    private String textColor;
    private float mHeight;
    private float textSize;
    private float mWidth;
    private int paddingLeft;
    private int paddingTop;
    private int paddingRight;
    private int paddingBottom;

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

    public TextView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FirstView,
                defStyleAttr, R.style.myDefaultStyle);
        mText = typedArray.getString(R.styleable.FirstView_src);
        if (mText == null) {
            mText = "";
        }
        textColor = typedArray.getString(R.styleable.FirstView_text_color);
        mWidth = typedArray.getDimension(R.styleable.FirstView_bg_width, 100);
        mHeight = typedArray.getDimension(R.styleable.FirstView_bg_height, 50);
        textSize = typedArray.getDimension(R.styleable.FirstView_text_size, 50);
        Log.i(TAG, "textColor-" + textColor);
        Log.i(TAG, "textSize-" + textSize);
        Log.i(TAG, "mWidth-" + mWidth);
        Log.i(TAG, "mHeight-" + mHeight);
        typedArray.recycle();
        //Consider the padding property of the custom view settings. Since the layout  margin related property is the responsibility of the viewgroup, only the custom view can be used
        //When viewing a group, you need to consider the effectiveness of the layout ﹣ margin property of its child view. View does not need to consider the layout ﹣ margin property
        paddingLeft = getPaddingLeft();
        paddingTop = getPaddingTop();
        paddingRight = getPaddingRight();
        paddingBottom = getPaddingBottom();
        init();
    }

    /**
     * The method defStyleRes parameter needs to be supported by API-21 or above, so it is not recommended to override this method and initialize it in this method
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     * @param defStyleRes
     */
    public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    /**
     * Initialize brush parameters
     */
    private void init() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //Set the brush size, which is the size of the text
        paint.setTextSize(textSize);
        //Set the color of the brush, that is, the color of the text
        paint.setColor(getColorFromXML(textColor));
    }

    @Override
    protected void onDraw(Canvas canvas) {
       /* Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.GRAY);
        paint.setTextSize(100);
        setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
        paint.setShadowLayer(10, 6, 6, Color.RED);
        canvas.drawText("Android Studio", 20, 120, paint);*/
        //Place text right in the middle
        Rect textRect = this.getTextRect();
        int viewWidth = getMeasuredWidth();
        int viewHeight = getMeasuredHeight();

        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        int x = (viewWidth - textRect.width()) / 2;
        int y = (int) (viewHeight / 2 +
                (fontMetrics.descent - fontMetrics.ascent) / 2
                - fontMetrics.descent);
        //x. Y coordinate point, about the position of the lower left corner of the text
        canvas.drawText(mText, x, y, paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //Get the width and height generated according to the text, and then determine the width and height required by the control
        Rect rect = getTextRect();
        int textWidth = rect.width();
        int textHeight = rect.height();
        int width = measureWidth(widthMeasureSpec, textWidth);
        int height = measureHeight(heightMeasureSpec, textHeight);
        setMeasuredDimension(width, height);
    }

    /**
     * Get the size of the text, rectangular area
     *
     * @return
     */
    private Rect getTextRect() {
        //Calculates the width of text based on Paint parameters set by Paint
        Rect rect = new Rect();
        //The size of the area occupied by the text is saved in rect
        if (mText != null) {
            paint.getTextBounds(mText, 0, mText.length(), rect);
        }
        return rect;
    }

    /**
     * Measure component width
     *
     * @param widthMeasureSpec
     * @param textWidth        Width of text
     * @return
     */
    private int measureWidth(int widthMeasureSpec, int textWidth) {
        int mode = MeasureSpec.getMode(widthMeasureSpec);
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int width = 0;

        if (mode == MeasureSpec.EXACTLY) {
            //When the width is match [parent] and the specific value, the size is taken as the width of the component directly
            width = size;
        } else if (mode == MeasureSpec.AT_MOST) {
            //Width is wrap [content, width needs to be calculated, here is text width
            if (textWidth > mWidth) {
                //If the set width is less than the text width, the text width is taken
                //Consider the value of padding
                width = textWidth + paddingLeft + paddingRight;
            } else {
                width = (int) mWidth;
            }
        }
        return width;
    }

    /**
     * Measure component height
     *
     * @param heightMeasureSpec
     * @param textHeight        Height of text
     * @return
     */
    private int measureHeight(int heightMeasureSpec, int textHeight) {
        int mode = MeasureSpec.getMode(heightMeasureSpec);
        int size = MeasureSpec.getSize(heightMeasureSpec);
        int height = 0;
        if (mode == MeasureSpec.EXACTLY) {
            //When the width is match [parent] and the specific value, directly use size as the height of the component
            height = size;
        } else if (mode == MeasureSpec.AT_MOST) {
            //The height is wrap [content, the height needs to be calculated, here is the text height
            //Consider the value of padding
            height = textHeight + paddingTop + paddingBottom;
        }
        return height;
    }

    /**
     * Convert the color string set by the xml page to the color of int, and convert the four formats to int color
     *
     * @param src
     * @return
     */
    private int getColorFromXML(String src) {
        if (TextUtils.isEmpty(src)) {
            return Color.BLACK;
        } else {
            if (src.contains("#")) {
                src = src.replace("#", "");
                StringBuffer result = new StringBuffer();
                if (src.length() == 3) {
                    char[] chars = src.toCharArray();
                    result.append("#");
                    result.append("ff");
                    for (int i = 0; i < chars.length; i++) {
                        result.append(chars[i]);
                        result.append(chars[i]);
                    }
                    return Color.parseColor(result.toString());
                } else if (src.length() == 4) {
                    char[] chars = src.toCharArray();
                    result.append("#");
                    for (int i = 0; i < chars.length; i++) {
                        result.append(chars[i]);
                        result.append(chars[i]);
                    }
                    return Color.parseColor(result.toString());
                } else if (src.length() == 6) {
                    result.append("#");
                    result.append("ff");
                    result.append(src);
                    return Color.parseColor(result.toString());
                } else if (src.length() == 8) {
                    result.append("#");
                    result.append(src);
                    return Color.parseColor(result.toString());
                } else {
                    return Color.BLACK;
                }
            } else {
                return Color.BLACK;
            }
        }
    }
}

Layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TextViewActivity">

    <hongzhen.com.defineviewdemo.view.TextView
        style="@style/myStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#aaa"
        app:text_size="30sp"
        app:src="Hello"/>

    <hongzhen.com.defineviewdemo.view.TextView
        style="@style/myStyle"
        android:layout_width="150dp"
        android:layout_height="60dp"
        android:background="#aaa"
        app:src="World"/>

    <hongzhen.com.defineviewdemo.view.TextView
        style="@style/myStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#aaa"
        app:src="Sure"/>
</LinearLayout>

attrs.xml file code:

<declare-styleable name="FirstView">
        <attr name="src" format="string"/>
        <attr name="text_color" format="string"></attr>
        <attr name="text_size" format="dimension"></attr>
        <attr name="bg_width" format="dimension"></attr>
        <attr name="bg_height" format="dimension"></attr>
    </declare-styleable>

style.xml file code:

<style name="myStyle">
        <item name="bg_width">36dp</item>
        <item name="text_color">#f00</item>
        <item name="android:layout_margin">5dp</item>
        <item name="android:padding">5dp</item>
    </style>

 

Keywords: Android xml Attribute less

Added by therealairness on Tue, 24 Dec 2019 17:37:07 +0200