天天看點

安卓開發學習之017 自定義控件之屬性擷取一、定義控件屬性二、自定義View的實作(.Java)三、在XML中使用自定義控件并給屬性指派四、java檔案中擷取屬性

每每看到别人寫的好看又實用的控件總是羨慕不已,你是不是也跟我一樣,想自己動手寫屬于自己的控件?希望這篇文章可以幫到你們。

談到自定義控件就少不了屬性的配置和擷取,通常需要以下幾個步驟:

1.通過<declare-styleable>為自定義View添加屬性
    2.自定義View的Java檔案
    3.在xml中為引用自定義View并給相應的屬性聲明屬性值
    4.在運作時(一般為構造函數)擷取屬性值
    5.将擷取到的屬性值應用到View
           

一、定義控件屬性

在res/values/attrs.xml下通過元素聲明需要的屬性

通常格式為

<declare-styleable name="CustomizeStyle">
        <attr name="attr_name" format="string" />
 </declare-styleable>
           

當然直接用attr 定義屬性也是可以的,如下

<attr name="attr_name" format="string" />
           

關于二者差別稍後通過執行個體做介紹

其中name為屬性的名字,format 為屬性的格式

format共有十種值可選

屬性值 說明
reference 參考某一資源ID
color 顔色值
boolean 布爾值
dimension 尺寸值
float 浮點值
integer 整型值
string 字元串
fraction 百分數
enum 枚舉值 多值選一
flag 位或運算 多值組合

下面為本文所使用的屬性檔案

/res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- reference:參考某一資源ID
    color:顔色值
    boolean:布爾值
    dimension:尺寸值
    float:浮點值
    integer:整型值
    string:字元串
    fraction:百分數
    enum:枚舉值 多值選一
    flag:位或運算,多值組合,用“|”分隔 -->

    <declare-styleable name="CustomTextViewStyle">
        <attr name="description" format="string"/>
        <attr name="text_size" format="dimension"/>
        <attr name="text_color" format="color"/>
        <attr name="text_content" format="string"/>
        <attr name="padding" format="dimension"/>

        <attr name="float_value" format="float"/>
        <attr name="integer_value" format="integer"/>
        <attr name="boolean_value" format="boolean"/>
        <attr name="fraction_value" format="fraction"/>
        <attr name="string_value" format="string"/>
        <attr name="dimension_value" format="dimension"/>
        <attr name="color_value" format="color"/>
        <attr name="reference_drawable_value" format="reference"/>
        <attr name="reference_array_value" format="reference"/>
        <attr name="enum_value" format="enum">
            <enum name="horizontal" value="0"/>
            <enum name="vertical" value="1"/>
        </attr>
        <attr name="flag_value">
            <flag name="normal" value="0"/>
            <flag name="bold" value="1"/>
            <flag name="italic" value="2"/>
        </attr>
    </declare-styleable>
    <attr name="CustomizeStyle" format="reference"/>
</resources>
           

關于二者定義方法的異同:

相同:都會在R檔案attr類中生成ID

public static final class attr {
    public static final int CustomizeStyle=;
    public static final int description=;
    public static final int text_size=;
    public static final int text_color=;
    public static final int text_content=;
    public static final int padding=;
    public static final int float_value=;
    public static final int integer_value=;
    public static final int boolean_value=;
    public static final int fraction_value=;
    public static final int string_value=;
    public static final int dimension_value=;
    public static final int color_value=;
    public static final int reference_drawable_value=;
    public static final int reference_array_value=;
    public static final int enum_value=;
    public static final int flag_value=;

}
           

不同:通過< declare-styleable>标簽定義的屬性還會在R檔案styleable類中生成相關屬性

public static final class styleable {
    public static final int[] CustomTextViewStyle = {
            , , , ,
            , , , ,
            , , , ,
            , , , 
        };
    public static final int CustomTextViewStyle_description = ;    
    public static final int CustomTextViewStyle_text_size = ;
    public static final int CustomTextViewStyle_text_color = ;
    public static final int CustomTextViewStyle_text_content = ;
    public static final int CustomTextViewStyle_padding = ;
    public static final int CustomTextViewStyle_float_value = ;
    public static final int CustomTextViewStyle_integer_value = ;
    public static final int CustomTextViewStyle_boolean_value = ;
    public static final int CustomTextViewStyle_fraction_value = ;
    public static final int CustomTextViewStyle_string_value = ;
    public static final int CustomTextViewStyle_dimension_value = ;
    public static final int CustomTextViewStyle_color_value = ;
    public static final int CustomTextViewStyle_reference_drawable_value = ;
    public static final int CustomTextViewStyle_reference_array_value = ;
    public static final int CustomTextViewStyle_enum_value = ;
    public static final int CustomTextViewStyle_flag_value = ;

 }
           

可以看出通過< declare-styleable>标簽定義的屬性,

在styleable類中生成了一個數組,數組下标索引用别名進行了定義

如“CustomTextViewStyle_description = 0”,數組中的值與attr類中的值一一對應。

在後面取值時會用到這個數組和下标索引值,

如果是直接通過attr标簽定義的屬性,可以自己建構這個數組,然後擷取值,後面會講到

跳轉檢視

二、自定義View的實作(.Java)

在包中建立CustomTextView.java 檔案 繼承自TextView,簡單寫幾個構造函數,其他後續再擴充

public class CustomTextView extends TextView {
    //在java代碼中通過new CustomTextView(context) 時會調用此方法
    public CustomTextView(Context context) {
        super(context);
    }
    //在XML中引用自定義控件,會調用此方法
    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);   
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
        super(context, attrs, defStyle, defStyleRes);    
    }
           

三、在XML中使用自定義控件并給屬性指派

首先要在布局XML檔案中申明控件所使用的命名空間.

命名空間跟C++中的概念很相同

using namespace std; 就可以在代碼中直接使用命名空間中的成員 cout 等等

先看下系統所使用的命名空間

xmlns:android=”http://schemas.android.com/apk/res/android”

對于這個我們我們不陌生,我們最長用的就是

android:id="",android:layout_width=""
           

如果去掉這個命名空間,我們就不能在控件中使用android這個标簽了

接下來這個是我們自定義的命名空間

xmlns: myAttr=”http://schemas.android.com/apk/res-auto”

myAttr是命名空間名字,後面的是位址

“res-auto”在早期是要替換成/res/自己項目的包名

使用自定義控件

格式如下

<com.antex.customview.CustomTextView
        android:id="@+id/textview0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        myAttr:text_color="@color/colorAccent"
        myAttr:text_content="text from xml"
        myAttr:text_size="12sp"/>
           

下面為本文布局檔案

res/fragment_custom_view.xml

<LinearLayout
    android:id="@+id/linearLayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myAttr="http://schemas.android.com/apk/com.antex.customview"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".CustomViewActivityFragment"
    tools:showIn="@layout/activity_custom_view"
    >
    <com.antex.customview.CustomTextView
        android:id="@+id/textview0"
        text="@string/app_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        myAttr:text_color="@color/colorAccent"
        myAttr:text_content="text from xml"
        myAttr:text_size="12sp"
        style="@style/CustomStyle"/>
    <com.antex.customview.CustomTextView
        android:id="@+id/textview1"
        style="@style/CustomStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <com.antex.customview.CustomTextView
        android:id="@+id/textview2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        myAttr:boolean_value="true"
        myAttr:color_value="@color/colorAccent"
        myAttr:description="Show all type Attribute"
        myAttr:dimension_value="10.5dp"
        myAttr:enum_value="horizontal"
        myAttr:flag_value="bold|italic"
        myAttr:float_value="10.1"
        myAttr:fraction_value="30%p"
        myAttr:integer_value="@integer/default_int"
        myAttr:reference_array_value="@array/String_Array"
        myAttr:reference_drawable_value="@mipmap/yxs"
        myAttr:string_value="stringvalue"
        />

    <com.antex.customview.CustomTextView
        android:id="@+id/textview3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        myAttr:description="default none attr setting"/>
    <com.antex.customview.CustomTextView
        android:id="@+id/textview4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        myAttr:description="default none attr setting"/>
    <RadioGroup
        android:id="@+id/radiaGroup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <RadioButton
            android:id="@+id/rb1"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="AppTheme.WithCustomizeStyle.Activity.WithValues.withCustomizeStyle"/>
        <RadioButton
            android:id="@+id/rb2"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="AppTheme.WithCustomizeStyle.Activity.WithValues"/>
        <RadioButton
            android:id="@+id/rb3"
            android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:text="AppTheme.WithCustomizeStyle.Activity.NoValues"/>
        <RadioButton android:id="@+id/rb4"
                     android:layout_width="wrap_content" android:layout_height="wrap_content"
                     android:text="AppTheme.NoCustomizeStyle.Activity.NoValuess"/>
    </RadioGroup>

</LinearLayout>
           

說明:

id為 textview0 直接在XML中設定了屬性值,并且也指定了Style

id為 textview1 通過Style 設定屬性

id為 textview2 用來介紹 attr 十種類型資料擷取 和defStyleAttr=0 defStyleRes=0時取值情況

id為 textview3 示範defStyleAttr=0時取值情況

id為 textview4 示範defStyleAttr!=0 defStyleRes!=0時取值情況

還有一個RadioGroup 用來切換目前應用的Application和Activity的theme

四、java檔案中擷取屬性

擷取屬性值函數:obtainStyledAttributes

其函數定義為

public final TypedArray obtainStyledAttributes(
            AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
            @StyleRes int defStyleRes) {
        return getTheme().obtainStyledAttributes(
            set, attrs, defStyleAttr, defStyleRes);
    }
           

參數說明:

4個參數的意思分别是:

    set:屬性值的集合,一般為第二個構造函數中的attrs

    attrs:我們要擷取的屬性的資源ID的一個數組,在本文中為 R.styleable

.CustomTextViewStyle

    defStyleAttr:這個是目前Theme中的一個attribute,是指向style的一個引用,當在layout xml中和style中都沒有為View指定屬性時,會從Theme中這個attribute指向的Style中查找相應的屬性值,如果這個參數傳入0表示不向Theme中搜尋預設值。在本文中為R.attr.CustomizeStyle

    defStyleRes:這個也是指向一個Style的資源ID,但是僅在defStyleAttr為0或defStyleAttr不為0但Theme中沒有為defStyleAttr屬性指派時起作用。在本文中為R.style.DefaultCustomizeStyle

函數傳回值:

 TypedArray 是一個類,包含要擷取值的一個集合   

下面我們通過代碼來講解

4.1 TypedArray使用詳解:attr十種屬性值擷取

4.1.1 TypedArray中的方法

傳回類型 方法 說明
boolean getBoolean(int index, boolean defValue) 傳回index索引位置的布爾值.
int getChangingConfigurations() Return a mask of the configuration parameters for which the values in this typed array may change.(沒弄懂這個意思)
int getColor(int index, int defValue) 傳回index索引位置的顔色.
ColorStateList getColorStateList(int index) 傳回index索引位置的ColorStateList
float getDimension(int index, float defValue) 傳回index索引位置的尺寸.
int getDimensionPixelOffset(int index, int defValue) 傳回index索引位置的尺寸.
int getDimensionPixelSize(int index, int defValue) 傳回index索引位置的尺寸
Drawable getDrawable(int index) 傳回index索引位置的Drawable對象.
float getFloat(int index, float defValue) 傳回index索引位置的浮點數.
float getFraction(int index, int base, int pbase, float defValue) 傳回index索引位置的百分數.
int getIndex(int at) 傳回index索引位置的整形數.
int getIndexCount() 傳回數組中含有數值的元素總個數.
int getInt(int index, int defValue) 傳回index索引位置的整形數.
int getInteger(int index, int defValue) 傳回index索引位置的整形數.
int getLayoutDimension(int index, String name) 傳回指定位置和名稱的layout_width或layout_height屬性值.
int getLayoutDimension(int index, int defValue) 傳回layout_width或layout_height屬性值.
String getNonResourceString(int index) 傳回字元串,這個屬性必須是在XML檔案中直接指定.
String getPositionDescription() 傳回錯誤資訊字元串.
int getResourceId(int index, int defValue) 傳回資源ID.
Resources getResources() 傳回TypedArray所使用的Resources.
String getString(int index) 傳回字元串.
CharSequence getText(int index) 傳回字元串.
CharSequence[] getTextArray(int index) 傳回數組.
int getType(int index) 傳回值類型在TypedValue中有定義
boolean getValueAt(int index, TypedValue outValue) 判斷index索引位置的值是否與給定的TypedValue值相等.
boolean hasValue(int index) 判斷index索引位置是否有值.
boolean hasValueOrEmpty(int index) 判斷index索引位置是否有值,有值或者值為@empty時傳回true,沒有定義時傳回false.
int length() 傳回TypedArray中所有元素個數.
void recycle() 回收TypedArray, 以便後面再用.
String toString() 傳回TypedArray拼接成的字元串.

4.1.2 TypedValue中值類型

hehe

/** The value contains no data. */
    public static final int TYPE_NULL = ;

    /** The <var>data</var> field holds a resource identifier. */
    public static final int TYPE_REFERENCE = ;
    /** The <var>data</var> field holds an attribute resource
     *  identifier (referencing an attribute in the current theme
     *  style, not a resource entry). */
    public static final int TYPE_ATTRIBUTE = ;
    /** The <var>string</var> field holds string data.  In addition, if
     *  <var>data</var> is non-zero then it is the string block
     *  index of the string and <var>assetCookie</var> is the set of
     *  assets the string came from. */
    public static final int TYPE_STRING = ;
    /** The <var>data</var> field holds an IEEE 754 floating point number. */
    public static final int TYPE_FLOAT = ;
    /** The <var>data</var> field holds a complex number encoding a
     *  dimension value. */
    public static final int TYPE_DIMENSION = ;
    /** The <var>data</var> field holds a complex number encoding a fraction
     *  of a container. */
    public static final int TYPE_FRACTION = ;

    /** Identifies the start of plain integer values.  Any type value
     *  from this to {@link #TYPE_LAST_INT} means the
     *  <var>data</var> field holds a generic integer value. */
    public static final int TYPE_FIRST_INT = ;

    /** The <var>data</var> field holds a number that was
     *  originally specified in decimal. */
    public static final int TYPE_INT_DEC = ;
    /** The <var>data</var> field holds a number that was
     *  originally specified in hexadecimal (0xn). */
    public static final int TYPE_INT_HEX = ;
    /** The <var>data</var> field holds 0 or 1 that was originally
     *  specified as "false" or "true". */
    public static final int TYPE_INT_BOOLEAN = ;

    /** Identifies the start of integer values that were specified as
     *  color constants (starting with '#'). */
    public static final int TYPE_FIRST_COLOR_INT = ;

    /** The <var>data</var> field holds a color that was originally
     *  specified as #aarrggbb. */
    public static final int TYPE_INT_COLOR_ARGB8 = ;
    /** The <var>data</var> field holds a color that was originally
     *  specified as #rrggbb. */
    public static final int TYPE_INT_COLOR_RGB8 = ;
    /** The <var>data</var> field holds a color that was originally
     *  specified as #argb. */
    public static final int TYPE_INT_COLOR_ARGB4 = ;
    /** The <var>data</var> field holds a color that was originally
     *  specified as #rgb. */
    public static final int TYPE_INT_COLOR_RGB4 = ;

    /** Identifies the end of integer values that were specified as color
     *  constants. */
    public static final int TYPE_LAST_COLOR_INT = ;

    /** Identifies the end of plain integer values. */
    public static final int TYPE_LAST_INT = ;
           

4.1.3 利用TypedArray擷取屬性值

為了友善,我們指定defStyleAttr和defStyleRes都為0

我們來列印 id為 textview2 這個控件中的各個值

/**
     * 介紹TypedArray使用方法
     * 擷取 attr中formate 十種類型屬性值
     * float,integer,boolean,fraction,string,dimension,color,reference,enum,flag
     * <p/>
     *
     * @param context 上下文環境
     * @param attrs   屬性集合
     */
    private void printAttributes(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable
                .CustomTextViewStyle, , );

        float float_value = typedArray.getFloat(R.styleable.CustomTextViewStyle_float_value, f);
        int integer_value = typedArray.getInteger(R.styleable.CustomTextViewStyle_integer_value, );
        boolean boolean_value = typedArray.getBoolean(R.styleable
                .CustomTextViewStyle_boolean_value, false);

        //public float getFraction (int index, int base, int pbase, float defValue)
        // 如果值為% 則 fraction_value=%*base
        //如果值格式為%p,則fraction_value=%*pbase
        float fraction_value = typedArray.getFraction(R.styleable
                .CustomTextViewStyle_fraction_value, , , );
        String string_value = typedArray.getString(R.styleable.CustomTextViewStyle_string_value);

        //擷取像素值,浮點數  eg:
        float dimension_value_float = typedArray.getDimension(R.styleable
                .CustomTextViewStyle_dimension_value, f);
        //将取得浮點像素值四舍五入 eg:
        int dimension_value = typedArray.getDimensionPixelSize(R.styleable
                .CustomTextViewStyle_dimension_value, );
        //将取得浮點像素值直接截取整數部分 eg:
        int dimension_value_offset_ = typedArray.getDimensionPixelOffset(R.styleable
                .CustomTextViewStyle_dimension_value, );


        int color_value = typedArray.getColor(R.styleable.CustomTextViewStyle_color_value, );
        int reference_drawable_value = typedArray.getResourceId(R.styleable
                .CustomTextViewStyle_reference_drawable_value, );
        int reference_array_value = typedArray.getResourceId(R.styleable
                .CustomTextViewStyle_reference_array_value, );
        int enum_value = typedArray.getInt(R.styleable.CustomTextViewStyle_enum_value, -);
        int flag_value = typedArray.getInt(R.styleable.CustomTextViewStyle_flag_value, -);


        System.out.println("float_value = [" + float_value + "], integer_value = [" +
                integer_value + "], " +
                "boolean_value = [" + boolean_value + "], fraction_value = [" +
                fraction_value + "], string_value = [" + string_value + "], dimension_value = [" +
                dimension_value + "], color_value = [" + color_value + "], " +
                "reference_drawable_value =" +
                " [0x" +
                Integer.toHexString(reference_drawable_value) + "], enum_value = [" + enum_value
                + "], " +
                "flag_value1 = [" +
                flag_value + "]");


        //後期資料處理,設定左邊圖檔
        Drawable drawable;
        drawable = typedArray.getDrawable(R.styleable.CustomTextViewStyle_reference_drawable_value);
        //or
        drawable = context.getDrawable(reference_drawable_value);
        drawable.setBounds(new Rect(, , , ));
        setCompoundDrawables(drawable, null, null, null);
        //設定文字是否大寫,斜體
        if (flag_value >= ) {
            Typeface typeface = getTypeface();
            setTypeface(Typeface.defaultFromStyle(flag_value));
        }

        //其他方法getTextArray
        CharSequence[] arrays;
        arrays = typedArray.getTextArray(R.styleable.CustomTextViewStyle_reference_array_value);
        //or
        arrays = context.getResources().getTextArray(reference_array_value);
        for (int i = ; i < arrays.length; i++) {
            System.out.println("arrays[" + i + "] = " + arrays[i]);
        }

        //周遊TypedArray
        for (int i = , m = typedArray.getIndexCount(); i < m; i++) {
            System.out.println("typedArray" + i + " type= " + typedArray.getType(i) + " value=" +
                    typedArray.getString(i));
        }

        int textsize = typedArray.getDimensionPixelSize(R.styleable
                .CustomTextViewStyle_text_size, );
        int textcolor = typedArray.getColor(R.styleable.CustomTextViewStyle_text_color, Color
                .BLACK);
        String text = typedArray.getString(R.styleable.CustomTextViewStyle_text_content);
        int padding = typedArray.getDimensionPixelSize(R.styleable.CustomTextViewStyle_padding, );


        setTextColor(textcolor);
        setTextSize(textsize);
        setText(text);
        setPadding(padding, padding, padding, padding);

        typedArray.recycle();
           

4.2 obtainStyledAttributes方法詳解

TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr,int defStyleRes)
           

在4.1.3節中,我們對這個方法有了簡單的認識

下面我們來看看後面兩個參數不為0的情況下,是如何取值的.

4.2.1首先我們需要定義一些style

res/values/styles.xml

我們在Application的theme中增加幾個基礎屬性

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="text_size">sp</item>
        <item name="padding">dp</item>
        <item name="text_content">@string/from_App_theme</item>
        <item name="text_color">#f00</item>
    </style>
           

再此基礎上又定義了一個Style,包含屬性值“CustomizeStyle“(方法中第三個參數defStyleAttr),它又引用一個Style

<style name="AppTheme.WithCustomizeStyle">
        <item name="CustomizeStyle">@style/CustomizeStyleInAppTheme</item>
    </style>

 <style name="CustomizeStyleInAppTheme">
        <item name="text_size">sp</item>
        <item name="padding">dp</item>
        <item name="text_color">@color/colorPrimaryDark</item>
        <item name="text_content">text from AppTheme refrence</item>
    </style>
           

還定義了個方法中第四個參數defStyleRes指向的的style

<style name="DefaultCustomizeStyle">
        <item name="text_content">text form DefaultCustomizeStyle</item>
        <item name="padding">dp</item>
        <!--<item name="text_color">#7ff</item>-->
    </style>
           

此外定義了若幹Activity的theme,有包含屬性”CustomizeStyle“和不包含此屬性的

全部代碼為

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="text_size">sp</item>
        <item name="padding">dp</item>
        <item name="text_content">@string/from_App_theme</item>
        <item name="text_color">#f00</item>
    </style>
    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

    <style name="AppTheme.NoCustomizeStyle"/>
    <style name="AppTheme.WithCustomizeStyle">
    <item name="CustomizeStyle">@style/CustomizeStyleInAppTheme</item>
</style>

    <style name="AppTheme.NoCustomizeStyle.Activity">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>

    </style>
    <style name="AppTheme.WithCustomizeStyle.Activity">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>

    </style>
    <style name="AppTheme.NoCustomizeStyle.Activity.NoValues"/>
    <style name="AppTheme.WithCustomizeStyle.Activity.NoValues"/>
    <style name="AppTheme.NoCustomizeStyle.Activity.WithValues">
        <item name="text_color">#7ff</item>
        <item name="text_size">sp</item>
        <item name="text_content">@string/from_Activity_theme</item>
        <item name="padding">dp</item>
    </style>
    <style name="AppTheme.WithCustomizeStyle.Activity.WithValues">
        <item name="text_color">#f0f</item>
        <item name="text_size">sp</item>
        <item name="text_content">@string/from_Activity_theme</item>
        <item name="padding">dp</item>
    </style>
    <style name="AppTheme.NoCustomizeStyle.Activity.WithValues.withCustomizeStyle">
        <item name="CustomizeStyle">@style/CustomizeStyleInActivityTheme</item>
    </style>
    <style name="AppTheme.WithCustomizeStyle.Activity.WithValues.withCustomizeStyle">
        <item name="CustomizeStyle">@style/CustomizeStyleInActivityTheme</item>
    </style>

    <style name="CustomizeStyleInAppTheme">
        <item name="text_size">sp</item>
        <item name="padding">dp</item>
        <item name="text_color">@color/colorPrimaryDark</item>
        <item name="text_content">text from AppTheme refrence</item>
    </style>

    <style name="CustomizeStyleInActivityTheme">
        <item name="text_size">sp</item>
        <item name="padding">dp</item>
        <item name="text_color">@color/colorPrimaryDark</item>
        <item name="text_content">text from ActivityTheme refrence</item>
    </style>

    <style name="CustomStyle">
        <item name="text_size">sp</item>
        <item name="text_color">#0f0</item>
        <item name="text_content">text form Style</item>
        <item name="padding">dp</item>
    </style>

    <style name="DefaultCustomizeStyle">
        <item name="text_content">text form DefaultCustomizeStyle</item>
        <item name="padding">dp</item>
        <!--<item name="text_color">#7ff</item>-->
    </style>

    <style name="DefaultCustomizeStyle_Notext">
        <item name="padding">dp</item>
    </style>

</resources>
           

字元串在res/values/strings.xml中有定義

<resources>
    <string name="app_name">_CustomView</string>
    <string name="action_settings">Settings</string>
    <string name="create_by_java">I am created by Java Code</string>
    <string name="from_App_theme">text from AppTheme</string>
    <string name="from_Activity_theme">text from ActivityTheme</string>
    <string name="from_Activity_theme_noCustomizeStyle">text from ActivityTheme with noCustomizeStyle </string>

</resources>
           

4.2.2下面我們再來更改下CustomTextView.java檔案

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.antex.customview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;


public class CustomTextView extends TextView {
    private static final String TAG = CustomTextView.class.getSimpleName();
    private String default_namespace = "http://schemas.android.com/apk/res/android";
    private String namespace = "http://schemas.android.com/apk/res-auto";

    public CustomTextView(Context context) {
        super(context);
        Log.d(TAG, "CustomTextView.CustomTextView1");
    }

    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.d(TAG, "CustomTextView.CustomTextView2");
        init(context, attrs, R.attr.CustomizeStyle, R.style.DefaultCustomizeStyle);
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        Log.d(TAG, "CustomTextView.CustomTextView3");
        init(context, attrs, defStyle, );
    }

    public CustomTextView(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
        super(context, attrs, defStyle, defStyleRes);
        Log.d(TAG, "CustomTextView.CustomTextView4");
        init(context, attrs, defStyle, defStyleRes);

    }

    public void init(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
        if (attrs != null) {
            try {
                int id = attrs.getAttributeResourceValue(default_namespace, "id", );
                switch (id) {
                    case R.id.textview2:
                        //defStyle= and defStyleRes=
                        printAttributes(context, attrs);
                        return;
                    case R.id.textview3:
                        defStyle = ;
                        break;
                }
                int textsize, textcolor, padding;
                String text = "no value";
                //擷取自定義屬性值的三種方法
                //方法一 在declare-styleable中定義的屬性
                TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable
                        .CustomTextViewStyle, defStyle, defStyleRes);

                textsize = typedArray.getDimensionPixelSize(R.styleable
                        .CustomTextViewStyle_text_size, );
                textcolor = typedArray.getColor(R.styleable.CustomTextViewStyle_text_color, Color
                        .BLACK);
                text = typedArray.getString(R.styleable.CustomTextViewStyle_text_content);
                padding = typedArray.getDimensionPixelSize(R.styleable
                        .CustomTextViewStyle_padding, );

                System.out.println("textsize = " + textsize);
                System.out.println("textcolor = " + textcolor);
                System.out.println("text = " + text);
                System.out.println("padding = " + padding);
                //方法二、自定義屬性數組,非常注意,數組中的值一定是從小到大排序,要不然後面會取不到正确的值
                int[] CustomTextViewStyle = {R.attr.text_size, R.attr.text_color, R.attr
                        .text_content, R.attr.padding};
                TypedArray typedArray2 = context.obtainStyledAttributes(attrs,
                        CustomTextViewStyle, defStyle, defStyleRes);

                int textsize1 = typedArray2.getDimensionPixelSize(, );
                int textcolor1 = typedArray2.getColor(, Color.BLACK);
                String text1 = typedArray2.getString();
                int padding1 = typedArray2.getDimensionPixelSize(, );

                System.out.println("textsize1 = " + textsize1);
                System.out.println("textcolor1 = " + textcolor1);
                System.out.println("text1 = " + text1);
                System.out.println("padding1 = " + padding1);

                //方法三 利用命名空間,根據屬性名擷取屬性值

                int textcolor2 = attrs.getAttributeIntValue(namespace, "textcolor", Color.BLACK);
                //or
                int resouceId = attrs.getAttributeResourceValue(namespace, "text_color", );
                if (resouceId > ) {
                    textcolor2 = context.getResources().getColor(resouceId);
                } else textcolor2 = Color.BLACK;

                int textsize2 = attrs.getAttributeIntValue(namespace, "text_size", );
                String text2 = attrs.getAttributeValue(namespace, "text_content");

                int padding2 = attrs.getAttributeIntValue(namespace, "padding", );
                System.out.println("textsize2  = " + textsize2);
                System.out.println("textcolor2 = " + textcolor2);
                System.out.println("text2 = " + text2);
                System.out.println("padding2 = " + padding2);
                //namespace is null
                resouceId = attrs.getAttributeResourceValue(null, "text", );
                if (resouceId > ) {
                    String null_namespace = context.getResources().getText(resouceId).toString();
                    System.out.println("null_namespace = " + null_namespace);
                }


                setTextColor(textcolor);
                setTextSize(textsize);
                setText(text + "");
                setPadding(padding, padding, padding, padding);


                //周遊attrs中所有屬性和值
                for (int i = , m = attrs.getAttributeCount(); i < m; i++) {
                    System.out.println("AttributeName = " + attrs.getAttributeName(i) + ", " +
                            "AttributeValue = " + attrs.getAttributeValue(i));
                }

                //介紹getAttributeNameResource(int index)方法
                int[] ids = new int[attrs.getAttributeCount()];
                for (int i = ; i < attrs.getAttributeCount(); i++) {
                    ids[i] = attrs.getAttributeNameResource(i);
                    //System.out.println("ids[" + i + "] = 0x" + Integer.toHexString(ids[i]));
                }

                TypedArray a = context.obtainStyledAttributes(attrs, ids, defStyle, );

                for (int i = ; i < attrs.getAttributeCount(); i++) {
                    String attrName = attrs.getAttributeName(i);
                    if (attrName == null) continue;
                    System.out.println("attrName = " + attrName + ",attrValue = " + a.getString(i));
                }
                a.recycle();
                typedArray.recycle();
                typedArray2.recycle();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    }

    /**
     * 介紹TypedArray使用方法
     * 擷取 attr中formate 十種類型屬性值
     * float,integer,boolean,fraction,string,dimension,color,reference,enum,flag
     * <p/>
     *
     * @param context 上下文環境
     * @param attrs   屬性集合
     */
    private void printAttributes(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable
                .CustomTextViewStyle, , );
        System.out.println("typedArray.getChangingConfigurations() = " + Integer.toHexString
                (typedArray.getChangingConfigurations()));
        float float_value = typedArray.getFloat(R.styleable.CustomTextViewStyle_float_value, f);
        int integer_value = typedArray.getInteger(R.styleable.CustomTextViewStyle_integer_value, );
        boolean boolean_value = typedArray.getBoolean(R.styleable
                .CustomTextViewStyle_boolean_value, false);

        //public float getFraction (int index, int base, int pbase, float defValue)
        // 如果值為% 則 fraction_value=%*base
        //如果值格式為%p,則fraction_value=%*pbase
        float fraction_value = typedArray.getFraction(R.styleable
                .CustomTextViewStyle_fraction_value, , , );
        String string_value = typedArray.getString(R.styleable.CustomTextViewStyle_string_value);

        //擷取像素值,浮點數  eg:
        float dimension_value_float = typedArray.getDimension(R.styleable
                .CustomTextViewStyle_dimension_value, f);
        //将取得浮點像素值四舍五入 eg:
        int dimension_value = typedArray.getDimensionPixelSize(R.styleable
                .CustomTextViewStyle_dimension_value, );
        //将取得浮點像素值直接截取整數部分 eg:
        int dimension_value_offset_ = typedArray.getDimensionPixelOffset(R.styleable
                .CustomTextViewStyle_dimension_value, );


        int color_value = typedArray.getColor(R.styleable.CustomTextViewStyle_color_value, );
        int reference_drawable_value = typedArray.getResourceId(R.styleable
                .CustomTextViewStyle_reference_drawable_value, );
        int reference_array_value = typedArray.getResourceId(R.styleable
                .CustomTextViewStyle_reference_array_value, );
        int enum_value = typedArray.getInt(R.styleable.CustomTextViewStyle_enum_value, -);
        int flag_value = typedArray.getInt(R.styleable.CustomTextViewStyle_flag_value, -);


        System.out.println("float_value = [" + float_value + "], integer_value = [" +
                integer_value + "], " +
                "boolean_value = [" + boolean_value + "], fraction_value = [" +
                fraction_value + "], string_value = [" + string_value + "], dimension_value = [" +
                dimension_value + "], color_value = [" + color_value + "], " +
                "reference_drawable_value =" +
                " [0x" +
                Integer.toHexString(reference_drawable_value) + "], enum_value = [" + enum_value
                + "], " +
                "flag_value1 = [" +
                flag_value + "]");


        //後期資料處理,設定左邊圖檔
        Drawable drawable;
        drawable = typedArray.getDrawable(R.styleable.CustomTextViewStyle_reference_drawable_value);
        //or
        drawable = context.getDrawable(reference_drawable_value);
        drawable.setBounds(new Rect(, , , ));
        setCompoundDrawables(drawable, null, null, null);
        //設定文字是否大寫,斜體
        if (flag_value >= ) {
            Typeface typeface = getTypeface();
            setTypeface(Typeface.defaultFromStyle(flag_value));
        }

        //其他方法getTextArray
        CharSequence[] arrays;
        arrays = typedArray.getTextArray(R.styleable.CustomTextViewStyle_reference_array_value);
        //or
        arrays = context.getResources().getTextArray(reference_array_value);
        for (int i = ; i < arrays.length; i++) {
            System.out.println("arrays[" + i + "] = " + arrays[i]);
        }

        //周遊TypedArray
        for (int i = , m = typedArray.getIndexCount(); i < m; i++) {
            System.out.println("typedArray" + i + " type= " + typedArray.getType(i) + " value=" +
                    typedArray.getString(i));
        }


        //defStyle=,defStyleRes =
        int textsize = typedArray.getDimensionPixelSize(R.styleable
                .CustomTextViewStyle_text_size, );
        int textcolor = typedArray.getColor(R.styleable.CustomTextViewStyle_text_color, Color
                .BLACK);
        String text = typedArray.getString(R.styleable.CustomTextViewStyle_text_content);
        int padding = typedArray.getDimensionPixelSize(R.styleable.CustomTextViewStyle_padding, );


        setTextColor(textcolor);
        setTextSize(textsize);
        setText(text);
        setPadding(padding, padding, padding, padding);

        typedArray.recycle();
    }

}
           

4.2.3.1 主要方法介紹

1.使用系統生成的int[] attrs數組R.styleable.CustomTextViewStyle,擷取屬性值時調用系統生成的數組下标索引
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable
                        .CustomTextViewStyle, defStyle, defStyleRes);

                textsize = typedArray.getDimensionPixelSize(R.styleable
                        .CustomTextViewStyle_text_size, );
           
2.自己封裝int[] attrs數組,由于數組是自己定義的,是以擷取什麼屬性用什麼數組下标索引自己是清楚的

陷阱:這個數組并不是你想怎麼組裝就怎麼組裝的,數組裡面值要從小到大排列,至于大小,到R檔案attr類中檢視

int[] CustomTextViewStyle = {R.attr.text_size, R.attr.text_color, R.attr
                        .text_content, R.attr.padding};
                TypedArray typedArray2 = context.obtainStyledAttributes(attrs,
                        CustomTextViewStyle, defStyle, defStyleRes);

                int textsize1 = typedArray2.getDimensionPixelSize(, );
           
3.使用命名空間擷取屬性
private String namespace = "http://schemas.android.com/apk/res-auto";
int textcolor2 = attrs.getAttributeIntValue(namespace, "textcolor", Color.BLACK);
//還有另外一種寫法,利用getAttributeResourceValue()擷取該屬性值是否在系統中定義了
int resouceId = attrs.getAttributeResourceValue(namespace, "text_color", );
if (resouceId > ) {
    textcolor2 = context.getResources().getColor(resouceId);
} else textcolor2 = Color.BLACK;
           

如果沒有使用命名空間,namespace可以置為null

<com.antex.customview.CustomTextView
        android:id="@+id/textview0"
        text="@string/app_name"
        ……
        />
//namespace is null
resouceId = attrs.getAttributeResourceValue(null, "text", );
if (resouceId > ) {
    String null_namespace = context.getResources().getText(resouceId).toString();
    System.out.println("null_namespace = " + null_namespace);
}        
           

4.2.3 Activity代碼

src/java/CustomViewActivityFragment.java

package com.antex.customview;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;

/**
 * A placeholder fragment containing a simple view.
 */
public class CustomViewActivityFragment extends Fragment {
    private int Themes[] = {R.style
            .AppTheme_WithCustomizeStyle_Activity_WithValues_withCustomizeStyle, R.style
            .AppTheme_WithCustomizeStyle_Activity_WithValues, R.style
            .AppTheme_WithCustomizeStyle_Activity_NoValues, R.style
            .AppTheme_NoCustomizeStyle_Activity_NoValues};

    public CustomViewActivityFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
            savedInstanceState) {
        Intent intent = getActivity().getIntent();
        int checkedID = intent.getIntExtra("checkedID", );
        getActivity().setTheme(Themes[checkedID]);
        View view = inflater.inflate(R.layout.fragment_custom_view, container, false);
        LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.linearLayout);

        CustomTextView customTextView1 = new CustomTextView(getActivity());

        customTextView1.setText("New CustomTextView");

        linearLayout.addView(customTextView1, , new ViewGroup.LayoutParams(ViewGroup
                .LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));


        final RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.radiaGroup);
        ((RadioButton) radioGroup.getChildAt(checkedID)).setChecked(true);
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                int id = ;
                switch (checkedId) {
                    case R.id.rb1:
                        id = ;
                        break;
                    case R.id.rb2:
                        id = ;
                        break;
                    case R.id.rb3:
                        id = ;
                        break;
                    case R.id.rb4:
                        id = ;
                        break;
                    default:
                        break;

                }

                startActivity(new Intent(getActivity(), CustomViewActivity.class).putExtra
                        ("checkedID", id));
                getActivity().finish();
            }
        });

        return view;
    }

}
           

4.2.4示範效果:

安卓開發學習之017 自定義控件之屬性擷取一、定義控件屬性二、自定義View的實作(.Java)三、在XML中使用自定義控件并給屬性指派四、java檔案中擷取屬性
安卓開發學習之017 自定義控件之屬性擷取一、定義控件屬性二、自定義View的實作(.Java)三、在XML中使用自定義控件并給屬性指派四、java檔案中擷取屬性
安卓開發學習之017 自定義控件之屬性擷取一、定義控件屬性二、自定義View的實作(.Java)三、在XML中使用自定義控件并給屬性指派四、java檔案中擷取屬性
安卓開發學習之017 自定義控件之屬性擷取一、定義控件屬性二、自定義View的實作(.Java)三、在XML中使用自定義控件并給屬性指派四、java檔案中擷取屬性

4.2.5效果解釋

頁面下有四個theme

編号 Activity theme是否引用defStyleAttr Application theme是否引用defStyleAttr Activity values Application values
1
2 ×
3 × ×
4 × × ×

總共6個TextView

第一個是在java檔案中通過new生成的

第二個是在XML中直接指定的,并且也指定了style

第三個是設定了Style的

這三個無論是在哪個theme下顯示效果都是一樣的

說明了兩點

1.在XML中指定值優先級>在style中指定值

2.在style中指定值的優先級>大于通過defStyleAttr,

和defStyleRes和在theme中指定

我們主要關注下下面三個TextView

第四個TextView:

defStyleAttr=0 && defStyleRes=0

3.不論在theme中是否引用defStyleAttr,是否指定defStyleRes,都是不起作用的

選擇前兩個theme時,Activity Theme中有屬性值,顯示的是Activity Theme中的值“text from ActivityTheme”

選擇後兩個theme時,Activity Theme中沒有有屬性值,顯示的是Application Theme中的值“text from AppTheme”

說明

4.在Activity Theme指定值的優先級>在Application Theme中指定的值

第五個TextView:

defStyleAttr=0 defStyleRes!=0

“DefaultCustomizeStyle”中有指定文字,但沒有指定文字顔色

四中情況下都是顯示的“text form DefaultCustomizeStyle”,

但是選擇前兩個theme時,顯示的是Activity Theme中顔色

選擇後兩個theme時,顯示的是Application Theme中顔色

說明

5.defStyleRes>Activity Theme指定值的優先級>在Application Theme中指定的值

第六個TextView:

defStyleAttr!=0 && defStyleRes!=0

6.Activity Theme中指定 defStyleAttr>Application Theme中指定 defStyleAttr

7.Application Theme中指定 defStyleAttr>指定defStyleRes

優先級總結:

在XML中直接指定>在style中指定值>在Activity theme中指定了defStyleAttr(>0)>在Application theme中指定了defStyleAttr(>0)>指定了defStyleRes(>0)>在Activity theme中指定的值>在Application theme中指定的值

開發工具:Android Studio1.5

SDK: Android 6.0

API 23

代碼下載下傳:017_CustomView_AttributeSet.zip