1. 程式人生 > >Android 自定義控制元件之繼承view

Android 自定義控制元件之繼承view

一.自定義控制元件的型別:

           1.繼承view(自繪檢視:view中的內容是我們自己繪製出來的,需要重寫onDraw方法)

           2.繼承已有原生控制元件

           3.自定義組合控制元件(將系統原生的控制元件組合到一起)

本次要講解的是繼承自view的控制元件,那麼除了Android系統原生的view的屬性之外,我們還可以自定義控制元件的屬性

二.自定義view控制元件的步驟

1.首先,在專案res/values目錄下新建attrs.xml,宣告自定義的屬性

<?xml version="1.0"encoding="utf-8"?>
<resources>
   <declare-styleable name="CustomTextView">
        <attrname="customText" format="string"/>
        <attrname="customTextColor" format="color"/>
        <attrname="customTextSize" format="dimension"/>
   </declare-styleable>
</resources>

name:屬性名     format:屬性型別

所有屬性型別如下8種:

reference     引用
color             
顏色
boolean       布林值
dimension   尺寸值
float              浮點值
integer         整型值
string            字串
enum            列舉值

如同Android原始碼中TextView控制元件宣告屬性:(路徑:SDK路徑\platforms\android-19\data\res\values\attrs.xml)


2.在佈局檔案中引用自定義屬性並定義屬性的值

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <com.example.zhaojungao.myapplication.CustomTextView
        android:id="@+id/customTextView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:padding="10dp"
        app:customText="haha"
        app:customTextColor="#ff0000"
        app:customTextSize="15sp"
        />
</RelativeLayout>

在Android Studio中,自定義的屬性名稱空間可自動生成

xmlns:app="http://schemas.android.com/apk/res-auto"

3.重寫view的構造方法中獲取到自定義屬性

那麼,涉及到如下三種形式的建構函式

public View (Context context)// 只在java程式碼建立檢視的情況下被呼叫 
public View (Context context, AttributeSet attrs) //在xml中建立檢視,但是沒有指定style的時候被呼叫
public View (Context context, AttributeSet attrs, int defStyle) //在xml中建立並指定style的時候被呼叫,即系統是不呼叫的,要由view顯示呼叫

在構造方法中通過如下該方法獲取TypedArray

public TypedArrayobtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, intdefStyleRes)

(set:屬性值的集合

attrs:我們要獲取的屬性的資源ID的一個數組 

defStyleAttr:這個是當前Theme中的一個attribute,是指向style的一個引用,當在layout xml中和style中都沒有為View指定屬性時,會從Theme中這個attribute指向的Style中查詢相應的屬性值,這就是defStyle的意思,如果沒有指定屬性值,就用這個值,所以是預設值,但這個attribute要在Theme中指定,且是指向一個Style的引用,如果這個引數傳入0表示不向Theme中搜索預設值

defStyleRes:這個也是指向一個Style的資源ID,但是僅在defStyleAttr為0或defStyleAttr不為0但Theme中沒有為defStyleAttr屬性賦值時起作用)

 public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.e("CustomTextView", "==========================================");
        /**獲取自定義的樣式屬性*/
        final TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTextView, defStyleAttr, 0);
        final int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            final int index = typedArray.getIndex(i);
            switch (index) {
                case R.styleable.CustomTextView_customText:
                    mTextString = typedArray.getString(index);
                    break;
                case R.styleable.CustomTextView_customTextColor:
                    //設定預設顏色為黑色
                    mTextColor = typedArray.getColor(index, Color.BLACK);
                    break;
                case R.styleable.CustomTextView_customTextSize:                          //將數值統一轉換成px  將16dp的值通過value * metrics.density轉換成多少px
                    mTextSize = typedArray.getDimensionPixelSize(index, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }
        typedArray.recycle();
        /**
         * 繪製文字寬,高
         * (mPaint.setTextSize(mTextSize);
         * mPaint.getTextBounds(mTextString, 0, mTextString.length(), mRect);//獲取文字的寬高
         * 還是設定一下為好,因為不論view寬高設定成哪種模式,都統一了文字大小為15sp,寬高也確定)
         */
        mPaint = new Paint();
        mPaint.setTextSize(mTextSize);
        mRect = new Rect();
        mPaint.getTextBounds(mTextString, 0, mTextString.length(), mRect);//獲取文字的寬高
    }

到此獲取到了view的屬性,下面就需要利用這些屬性走三部曲;

對於一個view在螢幕中的顯示需要依次進行三個步驟:

1). 測量view的大小(onMeasure)

2)設定view擺放的位置(onLayout,只是佈局通常是由父佈局 ViewGroup 來決定)

3). 最終在螢幕中繪製view(onDraw)

4.重寫onMeasure,onLayout方法

如果佈局檔案中view寬高為:具體數值/match_parent,則view的寬高即是系統的測量值

若view寬高設定為wrap_content,預設其大小是由父控制元件決定的,demo中父控制元件設定為match_parent,則

效果如下,這並不是我們想要的效果,所以需要重寫onMeasure方法,重新測量view的寬高

//當在xml檔案中設定控制元件寬高為wrap_content時,需要重寫該方法明確具體寬高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.e("onMeasure", "==========================================");
        super.onMeasure(widthMeasureSpec, 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)//對應match_parent和具體數值兩種
        {
            width = widthSize;//view寬度用系統的測量值
        } else {//wrap_content  預設設定為wrap_content時,大小是由父控制元件決定,所以需要重新設定
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mTextString, 0, mTextString.length(), mRect);
            float textWidth = mRect.width();
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());//view寬度用計算的值
            width = desired;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mTextString, 0, mTextString.length(), mRect);
            float textHeight = mRect.height();
            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }

        setMeasuredDimension(width, height);
    }

總結:1.onMeasure方法不是必須要重寫,其重寫的目的是要自定義view的尺寸;

            2.若自定義view設定wrap_content屬性,就必須重寫onMeasure方法指定wrap_content時的view大小,否則仍由父容器決定尺寸。

5.重寫onDraw方法 

在view初始化完成之後呼叫,用於繪製view(在此方法中不要做耗時操作)

//用於繪製view,系統會頻繁呼叫該方法,所以不要做耗時操作
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e("onDraw", "==========================================");
        mPaint.setColor(Color.YELLOW);//畫筆置黃色
        //繪製一個矩形
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
        mPaint.setColor(mTextColor);//畫筆置紅色
        //繪製文字  
        canvas.drawText(mTextString, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mPaint);
    }
至此,自定義的控制元件建立完畢。

資原始碼:http://download.csdn.net/detail/aurora_bessie/9753343

參考資料:http://blog.csdn.net/lmj623565791/article/details/24252901/