Android自定義View---驗證碼

weixin_33860722發表於2018-07-10

之前學自定義View的時候,都是看一些部落格文章,然後下載相關demo去學習。但是時間久了,再重新提起自定View,還是很模糊,想寫個自定View一時還不知從哪下手。實在慚愧。
現在想在這裡記錄一下每一次學習自定View的過程。方便以後檢視,反思和總結。算是一種積累吧,不然過段時間又忘了,又得去找資源。而通過記錄,可以回翻,溫故而知新,說不定能糾正自己以前的錯誤理解呢。
嗯,那,就開始了。
這篇主要是記錄一下:看完這篇文章後(鴻洋大神寫的)https://blog.csdn.net/lmj623565791/article/details/24252901,覺得例子雖然不難,但是對整個自定義過程寫得很清楚。特此記錄。

首先先了解一下自定義View的步驟吧:
1.自定義View的屬性
2.在View的構造方法中獲得我們自定義的屬性
3.重寫onMesure(此步驟不一定是必須的,但大多數情況下還是需要重寫的)
4.重寫onDraw

1.自定義View的屬性

在res/values/下新建attrs.xml,在裡面定義屬性宣告樣式

<?xml version="1.0" encoding="utf-8"?>
<resources>
 
    <attr name="titleText" format="string" />
    <attr name="titleTextColor" format="color" />
    <attr name="titleTextSize" format="dimension" />
 
    <declare-styleable name="CustomTitleView">
        <attr name="titleText" />
        <attr name="titleTextColor" />
        <attr name="titleTextSize" />
    </declare-styleable>
 
</resources

屬性取值型別有以下幾種:string,color,demension,integer,enum,reference,float,boolean,fraction,flag

接著在佈局中宣告我們的自定義View

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <com.example.customview01.view.CustomTitleView
        android:layout_width="200dp"
        android:layout_height="100dp"
        custom:titleText="3712"
        custom:titleTextColor="#ff0000"
        custom:titleTextSize="40sp" />
 
</RelativeLayout>

一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我們的名稱空間,後面的包路徑指的是專案的package

2.在View的構造方法中,獲得我們的自定義的樣式和屬性

/**
     * 文字
     */
    private String mTitleText;
    /**
     * 文字的顏色
     */
    private int mTitleTextColor;
    /**
     * 文字的大小
     */
    private int mTitleTextSize;
 
    /**
     * 繪製時控制文字繪製的範圍
     */
    private Rect mBound;
    private Paint mPaint;
 
    public CustomTitleView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }
 
    public CustomTitleView(Context context)
    {
        this(context, null);
    }
 
    /**
     * 獲得我自定義的樣式屬性
     * 
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CustomTitleView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        /**
         * 獲得我們所定義的自定義樣式屬性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
            case R.styleable.CustomTitleView_titleText:
                mTitleText = a.getString(attr);
                break;
            case R.styleable.CustomTitleView_titleTextColor:
                // 預設顏色設定為黑色
                mTitleTextColor = a.getColor(attr, Color.BLACK);
                break;
            case R.styleable.CustomTitleView_titleTextSize:
                // 預設設定為16sp,TypeValue也可以把sp轉化為px
                mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                        TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                break;
 
            }
 
        }
        a.recycle();
 
        /**
         * 獲得繪製文字的寬和高
         */
        mPaint = new Paint();
        mPaint.setTextSize(mTitleTextSize);
        // mPaint.setColor(mTitleTextColor);
        mBound = new Rect();
        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
 
    }

我們重寫了3個構造方法,預設的佈局檔案呼叫的是兩個引數的構造方法,所以記得讓所有的構造呼叫我們的三個引數的構造,我們在三個引數的構造中獲得自定義屬性。

3.重寫onDraw,onMeasure呼叫系統提供的

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 
    @Override
    protected void onDraw(Canvas canvas)
    {
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
 
        mPaint.setColor(mTitleTextColor);
        canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
    }

效果如下:


1934363-80910929e69775f3.png
image.png

但現在有一個問題,當我們把佈局檔案中該View的寬高改為wrap_content的時候,效果如下(像系統控制元件TextView和Button等,即使寬高設定wrap_content,但是我們會發現它們也是有一個預設的大小的):

1934363-665382bb0f642b52.png
image.png

系統幫我們測量的高度和寬度都是MATCH_PARNET,當我們設定明確的寬度和高度時,系統幫我們測量的結果就是我們設定的結果,當我們設定為WRAP_CONTENT,或者MATCH_PARENT系統幫我們測量的結果就是MATCH_PARENT的長度。

所以我們需要重寫onMeasure方法,來處理WRAP_CONTENT的情況

    @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 ;
        if (widthMode == MeasureSpec.EXACTLY)
        {
            width = widthSize;
        } else
        {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
            float textWidth = mBounds.width();
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
            width = desired;
        }
 
        if (heightMode == MeasureSpec.EXACTLY)
        {
            height = heightSize;
        } else
        {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
            float textHeight = mBounds.height();
            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }
        
        
 
        setMeasuredDimension(width, height);
    }

修改後效果:


1934363-f52ef22fce91b4db.png
image.png

4.為View新增點選事件

在構造方法中新增:

this.setOnClickListener(new OnClickListener()
        {
 
            @Override
            public void onClick(View v)
            {
                mTitleText = randomText();
                postInvalidate();
            }
 
        });

隨機生成四個數:

private String randomText()
    {
        Random random = new Random();
        Set<Integer> set = new HashSet<Integer>();
        while (set.size() < 4)
        {
            int randomInt = random.nextInt(10);
            set.add(randomInt);
        }
        StringBuffer sb = new StringBuffer();
        for (Integer i : set)
        {
            sb.append("" + i);
        }
 
        return sb.toString();
    }

好了,記錄到此結束。自己的思路果然清晰了很多~~
再次提供原文連結:
Android 自定義View (一) - CSDN部落格 https://blog.csdn.net/lmj623565791/article/details/24252901
送上原始碼:https://github.com/huangyula/CustomView.git

相關文章