當(dāng)前位置:首頁 > IT技術(shù) > 移動(dòng)平臺(tái) > 正文

Android自定義控件(四)---實(shí)戰(zhàn)篇(詳解onDraw)
2021-09-16 11:37:23

講到這里,這個(gè)案例基本上快結(jié)束了,在繪制(onDraw)方法中,唯一的難點(diǎn)就是文字 基線的確定,這點(diǎn)請(qǐng)大家務(wù)必弄清楚。廢話不多說,上碼?。?!

首先,我們先不管基不基線的,先讓文字顯示出來再說

package com.example.mytextview;

//import javax.swing.text.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.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class mTextView extends View {
    //1、設(shè)置自定義屬性變量
    private int mTextSize = 16;
    private int mTextColor = Color.RED;
    private String mText;

    //設(shè)置文字畫筆
    private Paint textPaint;

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

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

    public mTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //2、獲取裝有自定義屬性值的數(shù)值
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.mTextView);
        //3、精確獲取自定義屬性值
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.mTextView_mTextSize,mTextSize);//源碼用的這個(gè)方法,參照 TextView
        mTextColor = typedArray.getColor(R.styleable.mTextView_mTextColor,mTextColor);
        mText = typedArray.getString(R.styleable.mTextView_mText);
        //4、回收 typedArray
        typedArray.recycle();

        initData();
    }

    private void initData() {
        textPaint = new Paint();
        //抗鋸齒
        textPaint.setAntiAlias(true);
        //設(shè)置顏色
        textPaint.setColor(mTextColor);
        //設(shè)置字體大小
        textPaint.setTextSize(mTextSize);
    }

    //5、測量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //獲取寬高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        //獲取模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //判斷 如果是 wrap_content 模式,則 布局寬高 與 字體大小和字體長度有關(guān)
        if(widthMode == MeasureSpec.AT_MOST){
            //設(shè)置文字邊界
            Rect bounds = new Rect();
            //獲取文字邊界
            textPaint.getTextBounds(mText,0,mText.length(),bounds);
            //獲取邊界寬度  ===  獲取文字寬度
            width = bounds.width();
        }
        if (heightMode == MeasureSpec.AT_MOST){
            //設(shè)置文字邊界
            Rect bounds = new Rect();
            //獲取文字邊界
            textPaint.getTextBounds(mText,0,mText.length(),bounds);
            //獲取邊界高度  ===  獲取文字高度
            height = bounds.height();
        }
        //最后設(shè)置寬高
        setMeasuredDimension(width,height);
    }

    //6、繪制
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //drawText 中存在四個(gè)參數(shù)分別是: 要顯示的文本,基線x方向的起始點(diǎn),基線y方向的起始點(diǎn),畫筆
        canvas.drawText(mText,0,getHeight(),textPaint);
    }
}

我們先來運(yùn)行一遍,看看效果:

Android自定義控件(四)---實(shí)戰(zhàn)篇(詳解onDraw)_基線

顯示不全的原因是我們把寬高寫死了,把布局文件里 控件的寬高改成 wrap_content 即可:

Android自定義控件(四)---實(shí)戰(zhàn)篇(詳解onDraw)_基線_02

?下面顯示不全,這就有關(guān)基線問題了,那我們先來看看什么是基線,怎么求,見圖:

Android自定義控件(四)---實(shí)戰(zhàn)篇(詳解onDraw)_基線_03

所以我們可以這樣求基線,代碼里有注釋,如下:

package com.example.mytextview;

//import javax.swing.text.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.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class mTextView extends View {
    //1、設(shè)置自定義屬性變量
    private int mTextSize = 16;
    private int mTextColor = Color.RED;
    private String mText;

    //設(shè)置文字畫筆
    private Paint textPaint;

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

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

    public mTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        //2、獲取裝有自定義屬性值的數(shù)值
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.mTextView);
        //3、精確獲取自定義屬性值
        mTextSize = typedArray.getDimensionPixelSize(R.styleable.mTextView_mTextSize,mTextSize);//源碼用的這個(gè)方法,參照 TextView
        mTextColor = typedArray.getColor(R.styleable.mTextView_mTextColor,mTextColor);
        mText = typedArray.getString(R.styleable.mTextView_mText);
        //4、回收 typedArray
        typedArray.recycle();

        initData();
    }

    private void initData() {
        textPaint = new Paint();
        //抗鋸齒
        textPaint.setAntiAlias(true);
        //設(shè)置顏色
        textPaint.setColor(mTextColor);
        //設(shè)置字體大小
        textPaint.setTextSize(mTextSize);
    }

    //5、測量
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //獲取寬高
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        //獲取模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //判斷 如果是 wrap_content 模式,則 布局寬高 與 字體大小和字體長度有關(guān)
        if(widthMode == MeasureSpec.AT_MOST){
            //設(shè)置文字邊界
            Rect bounds = new Rect();
            //獲取文字邊界
            textPaint.getTextBounds(mText,0,mText.length(),bounds);
            //獲取邊界寬度  ===  獲取文字寬度
            width = bounds.width();
        }
        if (heightMode == MeasureSpec.AT_MOST){
            //設(shè)置文字邊界
            Rect bounds = new Rect();
            //獲取文字邊界
            textPaint.getTextBounds(mText,0,mText.length(),bounds);
            //獲取邊界高度  ===  獲取文字高度
            height = bounds.height();
        }
        //最后設(shè)置寬高
        setMeasuredDimension(width,height);
    }

    //6、繪制
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //drawText 中存在四個(gè)參數(shù)分別是: 要顯示的文本,基線x方向的起始點(diǎn),基線y方向的起始點(diǎn),畫筆
        Paint.FontMetricsInt fontMetricsInt = textPaint.getFontMetricsInt();
        //botto 為正值 top 為負(fù)值(看圖中畫的坐標(biāo)系)
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;
        int baseLine = getHeight()/2 + dy;
        canvas.drawText(mText,0,baseLine,textPaint);
    }
}

我們?cè)賮磉\(yùn)行一把,看看效果:

Android自定義控件(四)---實(shí)戰(zhàn)篇(詳解onDraw)_基線_04

可見,我們成功了,這篇到此為止,下一篇我們將一些優(yōu)化。

本文摘自 :https://blog.51cto.com/u

開通會(huì)員,享受整站包年服務(wù)立即開通 >