在下水平不高,不喜勿噴,有錯(cuò)的地方請積極提出,謝謝
Android:自定義控件你應(yīng)該知道的這些事_TypedArray
Android:自定義控件你應(yīng)該知道的這些事_事件分發(fā)機(jī)制
如果想要獲取Android的資源文件,我們會通過context.getresource.getXXX得到,那我們今天主要記錄的是利用TypeArray獲取自定義屬性,比如android:layout_width="match_parent"這些,其實(shí)已經(jīng)是android本身已經(jīng)定義好的屬性,那我們應(yīng)該怎么樣去創(chuàng)建自定義屬性,又怎么樣去使用自定屬性呢?
1.創(chuàng)建自定義屬性
首選創(chuàng)建values\attrs.xml,在attrs.xml中聲明自定義屬性
<declare-styleable name="MyFirstCustomerView">
<attr name="text" format="string" />
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension"/>
</declare-styleable>
- 自定義string類型,屬性名為text
- 自定義color類型,屬性名為textColor
- 自定義dimension類型,屬性名為textSize
簡單提一嘴:declare-styleable這個(gè)標(biāo)簽的作用其實(shí)就是可以為我們完成很多常量(int[]數(shù)組,下標(biāo)常量)等的編寫,簡化我們的開發(fā)工作
既然是簡單化,如果不使用declare-styleable標(biāo)簽來簡單化,我們該怎么做?
首先自定義屬性values\attrs.xml,如下
<resources>
<attr name="android:text" />
<attr name="text" format="string" />
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension"/>
</resources>
布局:
<com.ffcs.z.test.view.CustomView
android:layout_width="200dp"
android:layout_height="200dp"
android:text="aaaa"
app:text="@string/text"
app:textColor="@color/colorPrimaryDark"
app:textSize="18sp" />
獲取自定義屬性方法
private static final int[] mAttr = { android.R.attr.text,R.attr.text,R.attr.textColor,R.attr.textSize};//想要獲取的attr的id
TypedArray a = context.obtainStyledAttributes(attrs, mAttr);
String android_text = a.getString(0);
String text = a.getString(a.getIndex(1));
int color=a.getColor(a.getIndex(2),textColor);
int textSize=a.getDimensionPixelSize(a.getIndex(3),20);
//輸出 text = Hello world! , textAttr = 520
Log.e(TAG, "text = " + text + " , textAttr = " + text);
Log.i("TypedArray=>", "attrName_android_text:" + android_text +"\r\nattrName_text:" + text + "\r\nattrName_color:" + color + "\r\nattrName_textSize:" + textSize);
a.recycle();
Log輸出:
TypedArray=>: attrName_android_text:aaaa
attrName_text:這是一個(gè)自定以空間
attrName_color:-13615201
attrName_textSize:54
format還有如下類型
format | 介紹 |
---|---|
reference | 表示引用,參考某一資源ID |
string | 表示字符串 |
color | 表示顏色值 |
dimension | 表示尺寸值 |
boolean | 表示布爾值 |
integer | 表示整型值 |
float | 表示浮點(diǎn)值 |
fraction | 表示百分?jǐn)?shù) |
enum | 表示枚舉值 |
flag | 表示位運(yùn)算 |
2.布局文件中使用自定義屬性
首先需要在根布局中申明 xmlns:app="http://schemas.android.com/apk/res-auto"
<com.ffcs.z.test.view.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:text="這是自定義屬性"
app:textColor="@android:color/black"
app:textSize="18sp"/>
CustomView是自定義控件,引用了app:text、app:textColor、 app:textSize="18sp"三個(gè)自定義屬性
3.自定義控件CustomView
public class CustomView extends View {
public Paint paint;
private String text = "";//文本內(nèi)容
private int textColor = 0xDD333333; //字體顏色
private float textSize = 20;//字體大小設(shè)置
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray a = getResources().obtainAttributes(attrs, R.styleable.CustomerView);//獲取TypedArray
textColor = a.getColor(R.styleable.CustomerView_textColor, textColor);//獲取布局中設(shè)置的自定以顏色
textSize = a.getDimension(R.styleable.CustomerView_textSize, textSize);//獲取布局中設(shè)置的自定義字體大小
text = a.getString(R.styleable.CustomerView_text);//獲取布局中設(shè)置的自定義文本
paint = new Paint();//初始化 畫筆
paint.setTextSize(textSize);//畫筆字體大小設(shè)置
paint.setColor(textColor);//畫筆的顏色
paint.setStyle(Paint.Style.FILL);//畫筆風(fēng)格
a.recycle();//切記:在使用TypedArray后需要回收
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(text, 100, 100, paint);
}}
-TypedArray用完一定要回收
好像丑了點(diǎn),但是湊合看一下吧
了解自定義控件中構(gòu)造函數(shù)的參數(shù)
Parameters | 參數(shù)解釋 |
---|---|
Context | 當(dāng)前上下文 |
AttributeSet | 為xml里一個(gè)節(jié)點(diǎn)下面的屬性的集合,這個(gè)類一般都是系統(tǒng)在生成有xml配置的組件時(shí)生成,可以理解為當(dāng)前自定義控件下的所有屬性集合;提供TypedArray檢索的范圍 |
defStyleAttr | 在當(dāng)前包含了一個(gè)引用到為TypedArray提供默認(rèn)值的樣式資源的theme中的一種屬性??梢詾?,但是為0的時(shí)候就不會再去尋找默認(rèn)的;提供TypedArray檢索的范圍(注:這里的默認(rèn)也就是defStyleRes) |
AttributeSet
講個(gè)栗子,獲取屬性_字體的大小需要通過AttributeSet :
int textSize = attrs.getAttributeResourceValue(null, "textSize", 0);
if (textSize != 0) {
mEditText.setTextSize(textSize);
}
那么問題就來了,既然AttributeSet 和TypedArray都能獲取到xml文件下的屬性值,
直接用AttributeSet去獲取xml文件下的自定義屬性,為啥還要費(fèi)力去初始TypedArray
去獲取呢?接著看
<com.ffcs.z.test.view.CustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:text="@string/text"
app:textColor="@color/colorPrimaryDark"
app:textSize="18sp" />
分別輸出AttributeSet和TypedArray獲取到的屬性值
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String attrName = attrs.getAttributeName(i);
String attrVal = attrs.getAttributeValue(i);
Log.i("attrs=>", "attrName = " + attrName + " , attrVal = " + attrVal);
}
TypedArray a = getResources().obtainAttributes(attrs, R.styleable.CustomerView);//獲取TypedArray
textColor = a.getColor(R.styleable.CustomerView_textColor, textColor);//獲取布局中設(shè)置的自定以顏色
textSize = a.getDimension(R.styleable.CustomerView_textSize, textSize);//獲取布局中設(shè)置的自定義字體大小
text = a.getString(R.styleable.CustomerView_text);//獲取布局中設(shè)置的自定義文本
Log.i("TypedArray=>", "attrName_text:" + text + "\r\nattrName_textColor:" + textColor + "\r\nattrName_textSize:" + textSize);
輸出Log
attrs=>: attrName = layout_width , attrVal = 200.0dip
attrs=>: attrName = layout_height , attrVal = 200.0dip
attrs=>: attrName = text , attrVal = @2131099685
attrs=>: attrName = textColor , attrVal = @2131427350
attrs=>: attrName = textSize , attrVal = 18.0sp
TypedArray=>: attrName_text:這是一個(gè)自定以空間
attrName_textColor:-13615201
attrName_textSize:54.0
這邊我們看一下 text 用AttributeSet輸出的是什么鬼,肯定用不了,那我們看一下TypedArray獲取到的數(shù)值,松了一口氣,瞬間明白了AttributeSet和TypedArray獲取屬性的區(qū)別:如果布局中屬性的值是引用類型(比如:@string/text),使用AttributeSet去獲得最終的字符串,那么需要第一步拿到id,第二步再去解析id,而TypedArray正是幫我們簡化了這個(gè)過程。
那么有人就要問了,我們?nèi)绻カ@得android已經(jīng)定義好了的屬性,該咋整呢?舉個(gè)栗子,比如上面我們都用到text,app:text和android:text,功能都一樣,但是我更想要使用android:text,那么自定義組件中我改怎么去獲取呢?
<declare-styleable name="test">
<attr name="android:text" />
</declare-styleable>
這里我們是使用已經(jīng)定義好的屬性,不需要去添加format屬性,這是自定義和聲明的區(qū)別
TypedArray創(chuàng)建
TypedArray a =getResources().obtainAttributes();
-是 Resources 的函數(shù)
TypedArray a = context.obtainStyledAttributes();
-Resources.Theme 的函數(shù)
-此方法有四個(gè)重載方法
obtainStyledAttributes(int[] attrs)
obtainStyledAttributes(int resid, int[] attrs)
obtainStyledAttributes(AttributeSet set, int[] attrs)
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
簡單說,其中的參數(shù)為檢索的范圍 優(yōu)先級xml > style > defStyleAttr > defStyleRes > theme其中3/4構(gòu)造函數(shù)為最常用(PS:此處待補(bǔ)充)
獲取自定屬性正確姿勢
format | 介紹 |
---|---|
string | typedArray.getString(R.styleable.xxx); |
color | typedArray.getColor(R.styleable.xxx, 默認(rèn)值); |
boolean | typedArray.getBoolean(R.styleable.xxx, 默認(rèn)值); |
integer | typedArray.getInteger(R.styleable.xxx, 默認(rèn)值) |
float | typedArray.getFloat(R.styleable.xxx, 默認(rèn)值) |
fraction | typedArray.getFraction(R.styleable.xxx, 分子, 分母,默認(rèn)值) |
enum | typedArray.getInt(R.styleable.xxx, 默認(rèn)值); |
flag | typedArray.getInt(R.styleable.xxx,默認(rèn)值); |
dimension | sp:typedArray.getDimension(R.styleable.xxx, 20);dp:typedArray.getDimensionPixelOffset(typedArray.getIndex(R.styleable.xxx), 20); 或:private float radius = 3; radius=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,radius,getResources().getDisplayMetrics());radius = typedArray.getDimension(R.styleable.xxx, normalRadius); |
總結(jié)
- declare-styleable標(biāo)簽可以為我們完成很多常量(int[]數(shù)組,下標(biāo)常量)等的編寫,簡化我們的開發(fā)工作,可以不聲明,但是需要在自定義控件中,聲明引用自定義屬性數(shù)組
- TypedArray 可有obtainAttributes()、obtainStyledAttributes()方法創(chuàng)建,常用obtainStyledAttributes(int resid, int[] attrs)構(gòu)造方法
- TypedArray和AttributeSet區(qū)別在于,布局中引用參數(shù)為id時(shí)的不同(@string/text),所獲取的值不同
- format如果為dimension時(shí),注意:sp和dp需要轉(zhuǎn)化
此文部分理解于:
【張鴻洋的博客】Android 深入理解Android中的自定義屬性