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/