每每看到别人寫的好看又實用的控件總是羨慕不已,你是不是也跟我一樣,想自己動手寫屬于自己的控件?希望這篇文章可以幫到你們。
談到自定義控件就少不了屬性的配置和擷取,通常需要以下幾個步驟:
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示範效果:
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