Android關於Typedarray的使用

druidZ發表於2019-03-04

Android關於Typedarray的使用

一、Typedarray幹什麼用的

我們在寫介面佈局的xml時,經常要設定元件的屬性,比如常用的

android:id="@+id/id_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
複製程式碼

而假如我們需要對自定義的view、viewgroup等等新增自定義的屬性呢?就是需要typedarray了。這次我們的例子是給自定義的view新增屬性。

二、怎麼用Typedarray

1.準備一個自定義的view

code警告

public class ViewWithAttrs extends View {
    public ViewWithAttrs(Context context, AttributeSet attrs){
        super(context, attrs);
    }
}
複製程式碼

2.準備(新建)一個arrts.xml

Android關於Typedarray的使用
這個xml就是定義屬性的地方,我們開啟之後新增這個

<resources>
    <declare-styleable name="ViewWithAttrs">
        <attr name="paint_color" format="color"/>
    </declare-styleable>
</resources>
複製程式碼

注意第二行的name="ViewWithAttrs",name就是你要新增屬性的元件,一定要和view名字相同。

然後就是新增我們想要的屬性了,這裡我新增了一條屬性,屬性名是"paint_color",這個是自己定義的,屬性型別是:color。

還有其他可選的型別有:

reference 表示引用,參考某一資源ID
string 表示字串
color 表示顏色值
dimension 表示尺寸值
boolean 表示布林值
integer 表示整型值
float 表示浮點值
fraction 表示百分數
enum 表示列舉值
flag 表示位運算

3.主角出現:用typedarray獲取xml中的屬性

通過Contex的obtainStyledAttributes方法建立typedarray,然後用getXXX來獲取對應的屬性。

show me the code

public class ViewWithAttrs extends View {

    private Paint mPaint;

    public ViewWithAttrs(Context context, AttributeSet attrs){
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        //首先通過obtainStyledAttributes方法獲取typedarray
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.ViewWithAttrs);
        //使用typedarray獲取對應的屬性
        int paintColor = typedArray.getColor(R.styleable.ViewWithAttrs_paint_color, Color.BLACK);
        //最後的Color.BLACK是沒獲取到時的預設值
        mPaint.setColor(paintColor);
        typedArray.recycle();//不要忘記回收!
    }
}
複製程式碼

這樣我們獲取到了我們想要的屬性,可以在這個View中使用了

@Override
protected void onDraw(Canvas canvas){
    super.onDraw(canvas);
    canvas.drawCircle(200,200,50,mPaint);
}
複製程式碼

4.等等,我們還沒設定好屬性

在MainActivity的佈局檔案activity_main.xml中新增我們的view並設定好屬性

<com.idealcountry.test.ViewWithAttrs
        android:id="@+id/id_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:paint_color="@color/blue"/>
複製程式碼

這樣我們就能在程式碼中獲取xml設定好的paint_color了。//這裡我在color.xml中註冊了一個顏色:color/blue

結果圖:

Android關於Typedarray的使用

三、最後一點思考

AttributeSet的問題

小德我在寫自定義的View時,剛開始定義了一個類的屬性用來儲存從AttributeSet獲取的typedarray,但是手滑了把獲取typedarray寫到了onDraw()方法裡了,測試的時候後發現沒有成功獲取到xml中定義的屬性,通過debug這才知道在onDraw方法中拿到的值是空的,採用了預設值。但是這是為什麼呢?

經過查詢到layoutInflater這個類才清楚,大概意思就是AttributeSet並不是每個View都分配一個,而是有類似於快取一樣的機制,大家是共用的(大概可以這麼理解),所以到ondraw就有可能獲取到了不是我們需要的AttributeSet了。

typedarray回收?不存在的

為什麼一定要強調回收呢?這東西又不是佔了執行緒,我就不回收能怎麼樣?(其實不回收的話AS也會一直煩你,說你沒有回收)

為了弄清楚,我們需要進入原始碼來看一看。幾次goto declaration,我們最終在Typedarray.java中找到了,注意這個靜態方法裡的第一句。

static TypedArray obtain(Resources res, int len) {
        TypedArray attrs = res.mTypedArrayPool.acquire();
        if (attrs == null) {
            attrs = new TypedArray(res);
        }

        attrs.mRecycled = false;
        // Reset the assets, which may have changed due to configuration changes
        // or further resource loading.
        attrs.mAssets = res.getAssets();
        attrs.mMetrics = res.getDisplayMetrics();
        attrs.resize(len);
        return attrs;
    }
複製程式碼

而且這個類的構造方法隱藏了,是protected的,很明顯的一個單例模式。

   /** @hide */
    protected TypedArray(Resources resources) {
        mResources = resources;
        mMetrics = mResources.getDisplayMetrics();
        mAssets = mResources.getAssets();
    }
複製程式碼

這樣就清楚了,typedarray是我們在靜態方法Typedarray.obtain中獲取的,是從一個Typedarray池中取出來。不難理解,我們在view中使用typedarray,view會隨著activity的create而create,這樣我們總不能每次都new一個typedarray出來,所以採用了使用typedarray池來管理。這也就是官方一直強調,用完後要衝水,啊不是,用完後一定要回收。

O了個快速排序K

//作為Android開發的初學者,如果我有錯誤的地方或者不足的話歡迎大家指正。希望與大家一同進步

Android關於Typedarray的使用

相關文章