Android進階之繪製-自定義View完全掌握(五)
- 2020 年 1 月 20 日
- 筆記
在自定義類繼承View實現自定義控制項的過程中,我們還應該對一些自定義屬性有所了解。 我們通過一個案例來學習一下。 新建一個android項目,然後我們創建一個類MyAttributeView繼承View。 貼出MyAttributeView的程式碼。
package com.itcast.test0501; import android.content.Context; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; /** * 自定義屬性 */ public class MyAttributeView extends View { public MyAttributeView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } }
然後我們在activity_main.xml文件來使用。
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.itcast.test0501.MainActivity"> <com.itcast.test0501.MyAttributeView app:my_age="100" app:my_name="Android0501" app:my_bg="@drawable/a" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
發現沒有,my_age,my_name,my_bg都是我們自定義的屬性,前面的命名空間我們可以隨意命名,不一定非得叫app。那現在運行項目的話顯然是不行的,因為我們屬性還沒去定義呢。 那麼接下來我們就去定義我們的屬性。 在values文件夾下新建attrs.xml文件。
<?xml version="1.0" encoding="utf-8"?> <resources> <!--定義名字為MyAttributeView的屬性集合--> <declare-styleable name="MyAttributeView"> <!--定義名字為my_name,並且類型為string的屬性--> <attr name="my_name" format="string" /> <!--定義名字為my_age,並且類型為integer的屬性--> <attr name="my_age" format="integer" /> <!--定義名字為my_bg,並且類型為reference|color的屬性--> <attr name="my_bg" format="reference|color" /> </declare-styleable> </resources>
這樣我們的屬性就定義好了。 我們可以思考一下,一個類在布局文件中使用,它是如何被呈現到螢幕上的,我們知道,在java中,一個類要想被使用,該類就必須被實例化,那麼在android中,它是如何對這個自定義的View類進行實例化顯示的呢?它使用的是反射技術。它會把所有屬性封裝到AttributeSet類中,那麼你會發現,在自定義類中重寫的構造方法的參數里就存在這麼一個屬性的集合類。所以我們就可以知道為什麼自定義的控制項需要寫類的全路徑,因為反射是需要一個類的完整路徑的。在編譯的時候,會對布局文件進行pull解析,遇到類,就利用類路徑通過反射技術封裝屬性。 那接下來就是獲取屬性了,獲取屬性我們有三種方式。
1、用命名空間去獲取
2、遍歷屬性集合
3、使用系統工具,獲取屬性
修改MyAttributeView類的程式碼。
package com.itcast.test0501; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; /** * 自定義屬性 */ public class MyAttributeView extends View { private int myAge; private String myName; private Bitmap myBg; public MyAttributeView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); //獲取屬性三種方式 //1、用命名空間去獲取 String age = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "my_age"); String name = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "my_name"); String bg = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "my_bg"); Log.d("TAG1","age==" + age + ",name==" + name + ",bg==" + bg); //2、遍歷屬性集合 for(int i = 0;i < attrs.getAttributeCount();i++){ Log.d("TAG2",attrs.getAttributeName(i) + "==" + attrs.getAttributeValue(i)); } //3、使用系統工具,獲取屬性 } }
運行項目,查看日誌。

這樣就把我們設置的屬性值拿出來了。 但是,不知道大家發現了沒有,這兩種方法拿到的圖片值都是地址值吧,我們要想將設置的圖片屬性值通過某種方法顯式到螢幕上,對於地址值,我們有辦法操作嗎?應該是沒有的,所以,我們採用第三種方式,使用系統工具來獲取屬性值。 我們修改MyAttributeView的程式碼。
package com.itcast.test0501; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; /** * 自定義屬性 */ public class MyAttributeView extends View { private int myAge; private String myName; private Bitmap myBg; public MyAttributeView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); //獲取屬性三種方式 //1、用命名空間去獲取 String age = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "my_age"); String name = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "my_name"); String bg = attrs.getAttributeValue("http://schemas.android.com/apk/res-auto", "my_bg"); Log.d("TAG1","age==" + age + ",name==" + name + ",bg==" + bg); //2、遍歷屬性集合 for(int i = 0;i < attrs.getAttributeCount();i++){ Log.d("TAG2",attrs.getAttributeName(i) + "==" + attrs.getAttributeValue(i)); } //3、使用系統工具,獲取屬性 TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MyAttributeView); for(int i = 0;i < typedArray.getIndexCount();i++){ int index = typedArray.getIndex(i); switch (index){ case R.styleable.MyAttributeView_my_age: myAge = typedArray.getInt(index,0); break; case R.styleable.MyAttributeView_my_name: myName = typedArray.getString(index); break; case R.styleable.MyAttributeView_my_bg: Drawable drawable = typedArray.getDrawable(index); BitmapDrawable drawable1 = (BitmapDrawable) drawable; myBg = drawable1.getBitmap(); break; } } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Paint paint = new Paint(); canvas.drawText(myName + "---" + myAge,50,50,paint); canvas.drawBitmap(myBg,60,60,paint); } }
通過系統工具,我們可以把圖片的屬性值轉換為Bitmap,然後在onDraw()方法中將點陣圖繪製出來。 我們運行項目,預覽效果。

我們設置的屬性資訊都被成功繪製上來了。 由此,我們必須得掌握第三種獲取屬性值的方法,前兩種有能力去掌握的也可以去理解一下。 源碼我已上傳至GitHub,感興趣的同學可以下載閱讀一下。 點擊下載源碼