Android自定義控件——自定義屬性

 咱們在自定義Android組件的時候,除了用Java構建出組件的樣子外,有時候還須要去申明一些「屬性」提供給項目使用,那麼什麼是組件的屬性呢?html

例如在清單文件中,建立一個TextView的時候,這是須要制定TextView的 android:layout_width="wrap_content" android:layout_height="wrap_content"等等這些都是組件的屬性,TextView是android系統爲咱們提供好 的組件,它的屬性亦是android系統爲咱們提供了。詳情查看android的源碼,我這裏舉例android2.3的源碼,路徑是
/frameworks/base/core/res/res/values/attrs.xml,這個attrs.xml定義了全部android系統組件的屬性。

當咱們自定義組件時,除了可使用android系統爲咱們提供好的屬性以外,還能夠自定義屬性。自定義屬性主要步驟以下:
1、在attrs.xml文件中聲明屬性,如:java

[html] view plain copyandroid

print?ios

  1. <declare-styleable name="MyToggleBtn">            // 聲名屬性集的名稱,即這些屬性是屬於哪一個控件的。  
  2. <attr name="current_state" format="boolean"/>   // 聲名屬性 current_state 格式爲 boolean 類型  
  3. <attr name="slide_button" format="reference"/>   // 聲名屬性 slide_button格式爲 reference 類型  
  4. </declare-styleable>   

全部的format類型
reference     引用
color            顏色
boolean       布爾值
dimension   尺寸值
float            浮點值
integer        整型值
string          字符串
enum          枚舉值

2、在佈局文件中使用:在使用以前必須聲名命名空間,xmlns:example="http://schemas.android.com/apk/res/com.example.mytogglebtn"
說明:xmlns      是XML name space 的縮寫; 
          example   可爲任意寫符       
          http://schemas.android.com/apk/res/    此爲android固定格式;      瀏覽器

          com.example.mytogglebtn    此應用的包名,如manifest配置文件中一致。app

佈局文件:ide

 

[html] view plain copy工具

print?佈局

  1. <com.example.mytogglebtn.MyToggleButton  
  2.     xmlns:example="http://schemas.android.com/apk/res/com.example.mytogglebtn"  
  3.     android:layout_width="wrap_content"  
  4.     android:layout_height="wrap_content"   
  5.     example:slide_button="@drawable/slide_button" />  

3、在代碼中對屬性進行解析,代碼以下:學習

[java] view plain copy

print?

  1. TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyToggleBtn);// 由attrs 得到 TypeArray  

以上是建立自定義屬性的大體步驟。下面,我將要建立一個自定義控件的Demo,來學習學習自定義屬性的相關知識點。

首先,須要建立一個自定義控件出來,而且繼承View。在工程的res/values文件夾下建立attrs.xml文件:

 

[html] view plain copy

print?

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <!-- 聲明屬性級的名稱 -->  
  5.     <declare-styleable name="MyView">  
  6.   
  7.         <!-- 聲明一個屬性,整型 -->  
  8.         <attr name="test_id" format="integer" />  
  9.         <!-- 聲明一個屬性,字符串 -->  
  10.         <attr name="test_msg" format="string" />  
  11.         <!-- 聲明一個屬性,引用,引用資源id -->  
  12.         <attr name="test_bitmap" format="reference" />  
  13.     </declare-styleable>  
  14.   
  15. </resources>  

而後在佈局文件中,引用這個自定義控件MyView

 

 

[html] view plain copy

print?

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     xmlns:example="http://schemas.android.com/apk/res/com.example.myattrs"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent" >  
  6.   
  7.     <com.example.myattrs.MyView  
  8.         android:layout_width="wrap_content"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_centerInParent="true"  
  11.         example:test_bitmap="@drawable/ic_launcher"  
  12.         example:test_msg="@string/app_name" />  
  13.   
  14. </RelativeLayout>  

 

因爲建立出來的自定義組件MyView是繼承於View的,因此必須得複寫View的構造方法,View中有三個構造方法,先來看看複寫帶一個參數的構造方法:

 

[java] view plain copy

print?

  1. package com.example.myattrs;  
  2.   
  3. import android.content.Context;  
  4. import android.view.View;  
  5.   
  6. public class MyView extends View {  
  7.   
  8.     public MyView(Context context) {  
  9.         super(context);  
  10.         // TODO Auto-generated constructor stub  
  11.     }  
  12. }  

運行一下工程,那麼工程當即崩潰了,報錯也很清晰明瞭:

 

09-17 06:52:24.389: E/AndroidRuntime(1563): Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]

表示沒有找到某個帶兩個參數的構造方法,因而,知道自定義屬性必須得複寫父類的另一個構造方法,修改以下:

 

[java] view plain copy

print?

  1. package com.example.myattrs;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.view.View;  
  6.   
  7. public class MyView extends View {  
  8.   
  9.     public MyView(Context context, AttributeSet attrs) {  
  10.         super(context, attrs);  
  11.   
  12.         int count = attrs.getAttributeCount();  
  13.         for (int index = 0; index < count; index++) {  
  14.             String attributeName = attrs.getAttributeName(index);  
  15.             String attributeValue = attrs.getAttributeValue(index);  
  16.             System.out.println("name:" + attributeName + "  value:" + attributeValue);  
  17.         }  
  18.     }  
  19.   
  20. }  

打印結果以下:

 

AttributeSet:對佈局文件XML解析後的結果,封裝爲AttributeSet對象。存儲的都是原始數據,可是對數據進行了簡單的加工。

由此構造器幫咱們返回了佈局文件XML的解析結果,拿到這個結果,咱們該怎麼作呢?接下來,咱們來看看View類對於這個是怎麼處理的:

 

[java] view plain copy

print?

  1. public View(Context context, AttributeSet attrs) {  
  2.         this(context, attrs, 0);  
  3.     }  

[java] view plain copy

print?

  1. public View(Context context, AttributeSet attrs, int defStyle) {  
  2.         this(context);  
  3.   
  4.         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,  
  5.                 defStyle, 0);  

因而,找到一個跟屬性很相關的類TypeArray,那麼接下來,我在自定義控件的構造方法上也獲取一下TypeArray這個類:

翻看一下TypeArray的源碼會發現,TypeArray是不繼承任何類(除了Object)的,也就是說,TypeArray至關於一個工具 類,經過context.obtainStyledAttributes方法,將AttributeSet和屬性的類型傳遞進去,好比 AttributeSet至關於原材料,屬性類型至關於圖紙,context.obtainStyledAttributes至關於加工廠加工成所對象的 屬性,封裝到TypeArray這個類裏。

 

[java] view plain copy

print?

  1. package com.example.myattrs;  
  2.   
  3. import android.content.Context;  
  4. import android.content.res.TypedArray;  
  5. import android.util.AttributeSet;  
  6. import android.view.View;  
  7.   
  8. public class MyView extends View {  
  9.   
  10.     public MyView(Context context, AttributeSet attrs) {  
  11.         super(context, attrs);  
  12.   
  13.         TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyView);  
  14.         int count = ta.getIndexCount();  
  15.         for (int i = 0; i < count; i++) {  
  16.             int itemId = ta.getIndex(i);  
  17.             System.out.println("itemId::" + itemId); // 獲取屬性在R.java文件中的id  
  18.             switch (itemId) {  
  19.             case R.styleable.MyView_test_bitmap:  
  20.                 int bitmapId = ta.getResourceId(itemId, 100);  
  21.                 System.out.println("bitmapId::" + bitmapId);  
  22.                 break;  
  23.             case R.styleable.MyView_test_id:  
  24.                 int test_id = ta.getInteger(itemId, 10);  
  25.                 System.out.println("test_id" + test_id);  
  26.                 break;  
  27.             case R.styleable.MyView_test_msg:  
  28.                 String test_msg = ta.getString(itemId);  
  29.                 System.out.println("test_msg::" + test_msg);  
  30.                 break;  
  31.             default:  
  32.                 break;  
  33.             }  
  34.         }  
  35.     }  
  36.   
  37. }  

 

如下是TypeArray類裏的方法,這裏不寫註釋了,見名知意:

當在構造方法中獲取到這些設置好的屬性值時,取出其值,就能夠在代碼中進行處理了。

上篇博客提到了Android自定義控件——仿ios的滑動開關按鈕,接下來,就要爲這個滑動開關按鈕條件自定義的屬性,不熟悉上篇博客Demo的,能夠先去瀏覽器一下個人上篇博客,點這裏Android自定義控件——仿ios滑動開關按鈕

首先,按照上面介紹的步驟,先在res/values目錄下建立一個屬性文件attrs.xml:

 

[html] view plain copy

print?

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <declare-styleable name="MyToggleBtn">  
  5.   
  6.         <!-- 滑動按鈕背景圖片 -->  
  7.         <attr name="switchBG" format="reference" />  
  8.         <!-- 滑動塊圖片 -->  
  9.         <attr name="slideBg" format="reference" />  
  10.         <!-- 設置當前的狀態 -->  
  11.         <attr name="currState" format="boolean" />  
  12.     </declare-styleable>  
  13.   
  14. </resources>  

而後,在引用自定義控件的佈局文件acticity_main.xml上設置自定義屬性,記住,引用這些屬性以前,必須先引用命名空間:

 

xmlns:mytogglebtn="http://schemas.android.com/apk/res/com.example.slidebutton"

其中:mytogglebtn 是任意取名,沒有強制要求,可是在控件中引用屬性的時候,要保持一致,不要寫錯了

          com.example.slidebutton 是工程的包名,千萬不要弄錯了,否則找不到屬性文件

 

[html] view plain copy

print?

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     xmlns:mytogglebtn="http://schemas.android.com/apk/res/com.example.slidebutton"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent" >  
  6.   
  7.     <com.example.slidebutton.view.SlideButton  
  8.         android:id="@+id/slidebutton"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_centerInParent="true"  
  12.         mytogglebtn:currState="false"  
  13.         mytogglebtn:slideBg="@drawable/slide_button_background"  
  14.         mytogglebtn:switchBG="@drawable/switch_background" />  
  15.   
  16. </RelativeLayout>  

有了上面的步驟,咱們就能夠自定義組件類的構造方法中,將屬性集解析成TypeArray了,從TypeArray中獲取相關的屬性值,並用於初始化自定義控,如下是主要代碼:

 

 

[java] view plain copy

print?

  1. public SlideButton(Context context, AttributeSet attrs) {  
  2.         super(context, attrs);  
  3.   
  4.         // 得到自定義屬性  
  5.         TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyToggleBtn);  
  6.   
  7.         int count = ta.getIndexCount();  
  8.         for (int i = 0; i < count; i++) {  
  9.             int itemId = ta.getIndex(i); // 獲取某個屬性的Id值  
  10.             switch (itemId) {  
  11.             case R.styleable.MyToggleBtn_currState: // 設置當前按鈕的狀態  
  12.                 currentState = ta.getBoolean(itemId, false);  
  13.                 break;  
  14.             case R.styleable.MyToggleBtn_switchBG: // 設置按鈕的背景圖  
  15.                 int backgroundId = ta.getResourceId(itemId, -1);  
  16.                 if (backgroundId == -1)  
  17.                     throw new RuntimeException("資源沒有被找到,請設置背景圖");  
  18.                 switchBG = BitmapFactory.decodeResource(getResources(), backgroundId);  
  19.                 break;  
  20.             case R.styleable.MyToggleBtn_slideBg: // 設置按鈕圖片  
  21.                 int slideId = ta.getResourceId(itemId, -1);  
  22.                 if (slideId == -1)  
  23.                     throw new RuntimeException("資源沒有找到,請設置按鈕圖片");  
  24.                 slideButtonBG = BitmapFactory.decodeResource(getResources(), slideId);  
  25.                 break;  
  26.             default:  
  27.                 break;  
  28.             }  
  29.         }  
  30.     }  

 

        從上能夠看到,自定義屬性其實很簡單。就是在構造方法中,將獲取到的屬性集加工成TypeArray對象,經過這個對象取出屬性的id,經過id取出每一個 屬性對應的值(畢竟Android下的佈局文件XML也是key-value形式的),最後將獲取到的屬性值(控件用戶自定義的數據)初始化到自定義控件 上,這樣,一個完整的自定義控件就完成。這種完整的自定義控件方式用的並很少見,由於在開發自定義控件時候,須要什麼數據就直接在Java代碼裏設置就好 了,方便多了。可是在特定的場合下,若是開發的控件某些數據不肯定,或者所開發控件須要提供給其餘人進行偏好設置什麼的,這種自定義屬性就顯得非用不可 了。

相關文章
相關標籤/搜索