每每看到别人写的好看又实用的控件总是羡慕不已,你是不是也跟我一样,想自己动手写属于自己的控件?希望这篇文章可以帮到你们。
谈到自定义控件就少不了属性的配置和获取,通常需要以下几个步骤:
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=0x7f010000;
public static final int description=0x7f010041;
public static final int text_size=0x7f010042;
public static final int text_color=0x7f010043;
public static final int text_content=0x7f010044;
public static final int padding=0x7f010045;
public static final int float_value=0x7f010046;
public static final int integer_value=0x7f010047;
public static final int boolean_value=0x7f010048;
public static final int fraction_value=0x7f010049;
public static final int string_value=0x7f01004a;
public static final int dimension_value=0x7f01004b;
public static final int color_value=0x7f01004c;
public static final int reference_drawable_value=0x7f01004d;
public static final int reference_array_value=0x7f01004e;
public static final int enum_value=0x7f01004f;
public static final int flag_value=0x7f010050;
}
不同:通过< declare-styleable>标签定义的属性还会在R文件styleable类中生成相关属性
public static final class styleable {
public static final int[] CustomTextViewStyle = {
0x7f010041, 0x7f010042, 0x7f010043, 0x7f010044,
0x7f010045, 0x7f010046, 0x7f010047, 0x7f010048,
0x7f010049, 0x7f01004a, 0x7f01004b, 0x7f01004c,
0x7f01004d, 0x7f01004e, 0x7f01004f, 0x7f010050
};
public static final int CustomTextViewStyle_description = 0;
public static final int CustomTextViewStyle_text_size = 1;
public static final int CustomTextViewStyle_text_color = 2;
public static final int CustomTextViewStyle_text_content = 3;
public static final int CustomTextViewStyle_padding = 4;
public static final int CustomTextViewStyle_float_value = 5;
public static final int CustomTextViewStyle_integer_value = 6;
public static final int CustomTextViewStyle_boolean_value = 7;
public static final int CustomTextViewStyle_fraction_value = 8;
public static final int CustomTextViewStyle_string_value = 9;
public static final int CustomTextViewStyle_dimension_value = 10;
public static final int CustomTextViewStyle_color_value = 11;
public static final int CustomTextViewStyle_reference_drawable_value = 12;
public static final int CustomTextViewStyle_reference_array_value = 13;
public static final int CustomTextViewStyle_enum_value = 14;
public static final int CustomTextViewStyle_flag_value = 15;
}
可以看出通过< 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个参数的意思分别是:
attrs
R.styleable
.CustomTextViewStyle
R.attr.CustomizeStyle
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 = 0x00;
/** The <var>data</var> field holds a resource identifier. */
public static final int TYPE_REFERENCE = 0x01;
/** 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 = 0x02;
/** 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 = 0x03;
/** The <var>data</var> field holds an IEEE 754 floating point number. */
public static final int TYPE_FLOAT = 0x04;
/** The <var>data</var> field holds a complex number encoding a
* dimension value. */
public static final int TYPE_DIMENSION = 0x05;
/** The <var>data</var> field holds a complex number encoding a fraction
* of a container. */
public static final int TYPE_FRACTION = 0x06;
/** 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 = 0x10;
/** The <var>data</var> field holds a number that was
* originally specified in decimal. */
public static final int TYPE_INT_DEC = 0x10;
/** The <var>data</var> field holds a number that was
* originally specified in hexadecimal (0xn). */
public static final int TYPE_INT_HEX = 0x11;
/** The <var>data</var> field holds 0 or 1 that was originally
* specified as "false" or "true". */
public static final int TYPE_INT_BOOLEAN = 0x12;
/** Identifies the start of integer values that were specified as
* color constants (starting with '#'). */
public static final int TYPE_FIRST_COLOR_INT = 0x1c;
/** The <var>data</var> field holds a color that was originally
* specified as #aarrggbb. */
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
/** The <var>data</var> field holds a color that was originally
* specified as #rrggbb. */
public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
/** The <var>data</var> field holds a color that was originally
* specified as #argb. */
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
/** The <var>data</var> field holds a color that was originally
* specified as #rgb. */
public static final int TYPE_INT_COLOR_RGB4 = 0x1f;
/** Identifies the end of integer values that were specified as color
* constants. */
public static final int TYPE_LAST_COLOR_INT = 0x1f;
/** Identifies the end of plain integer values. */
public static final int TYPE_LAST_INT = 0x1f;
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, 0, 0);
float float_value = typedArray.getFloat(R.styleable.CustomTextViewStyle_float_value, 0f);
int integer_value = typedArray.getInteger(R.styleable.CustomTextViewStyle_integer_value, 0);
boolean boolean_value = typedArray.getBoolean(R.styleable
.CustomTextViewStyle_boolean_value, false);
//public float getFraction (int index, int base, int pbase, float defValue)
// 如果值为10% 则 fraction_value=10%*base
//如果值格式为10%p,则fraction_value=10%*pbase
float fraction_value = typedArray.getFraction(R.styleable
.CustomTextViewStyle_fraction_value, 1, 1, 0);
String string_value = typedArray.getString(R.styleable.CustomTextViewStyle_string_value);
//获取像素值,浮点数 eg:27.5625
float dimension_value_float = typedArray.getDimension(R.styleable
.CustomTextViewStyle_dimension_value, 0f);
//将取得浮点像素值四舍五入 eg:28
int dimension_value = typedArray.getDimensionPixelSize(R.styleable
.CustomTextViewStyle_dimension_value, 0);
//将取得浮点像素值直接截取整数部分 eg:27
int dimension_value_offset_ = typedArray.getDimensionPixelOffset(R.styleable
.CustomTextViewStyle_dimension_value, 0);
int color_value = typedArray.getColor(R.styleable.CustomTextViewStyle_color_value, 0);
int reference_drawable_value = typedArray.getResourceId(R.styleable
.CustomTextViewStyle_reference_drawable_value, 0);
int reference_array_value = typedArray.getResourceId(R.styleable
.CustomTextViewStyle_reference_array_value, 0);
int enum_value = typedArray.getInt(R.styleable.CustomTextViewStyle_enum_value, -1);
int flag_value = typedArray.getInt(R.styleable.CustomTextViewStyle_flag_value, -1);
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(0, 0, 50, 50));
setCompoundDrawables(drawable, null, null, null);
//设置文字是否大写,斜体
if (flag_value >= 0) {
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 = 0; i < arrays.length; i++) {
System.out.println("arrays[" + i + "] = " + arrays[i]);
}
//遍历TypedArray
for (int i = 0, 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, 10);
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, 0);
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">8sp</item>
<item name="padding">15dp</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">10sp</item>
<item name="padding">2dp</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">4dp</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">8sp</item>
<item name="padding">15dp</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">12sp</item>
<item name="text_content">@string/from_Activity_theme</item>
<item name="padding">15dp</item>
</style>
<style name="AppTheme.WithCustomizeStyle.Activity.WithValues">
<item name="text_color">#f0f</item>
<item name="text_size">12sp</item>
<item name="text_content">@string/from_Activity_theme</item>
<item name="padding">15dp</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">10sp</item>
<item name="padding">2dp</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">10sp</item>
<item name="padding">2dp</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">12sp</item>
<item name="text_color">#0f0</item>
<item name="text_content">text form Style</item>
<item name="padding">3dp</item>
</style>
<style name="DefaultCustomizeStyle">
<item name="text_content">text form DefaultCustomizeStyle</item>
<item name="padding">4dp</item>
<!--<item name="text_color">#7ff</item>-->
</style>
<style name="DefaultCustomizeStyle_Notext">
<item name="padding">4dp</item>
</style>
</resources>
字符串在res/values/strings.xml中有定义
<resources>
<string name="app_name">017_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, 0);
}
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", 0);
switch (id) {
case R.id.textview2:
//defStyle=0 and defStyleRes=0
printAttributes(context, attrs);
return;
case R.id.textview3:
defStyle = 0;
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, 10);
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, 0);
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(0, 10);
int textcolor1 = typedArray2.getColor(1, Color.BLACK);
String text1 = typedArray2.getString(2);
int padding1 = typedArray2.getDimensionPixelSize(3, 0);
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", 0);
if (resouceId > 0) {
textcolor2 = context.getResources().getColor(resouceId);
} else textcolor2 = Color.BLACK;
int textsize2 = attrs.getAttributeIntValue(namespace, "text_size", 10);
String text2 = attrs.getAttributeValue(namespace, "text_content");
int padding2 = attrs.getAttributeIntValue(namespace, "padding", 0);
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", 0);
if (resouceId > 0) {
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 = 0, 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 = 0; 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, 0);
for (int i = 0; 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, 0, 0);
System.out.println("typedArray.getChangingConfigurations() = " + Integer.toHexString
(typedArray.getChangingConfigurations()));
float float_value = typedArray.getFloat(R.styleable.CustomTextViewStyle_float_value, 0f);
int integer_value = typedArray.getInteger(R.styleable.CustomTextViewStyle_integer_value, 0);
boolean boolean_value = typedArray.getBoolean(R.styleable
.CustomTextViewStyle_boolean_value, false);
//public float getFraction (int index, int base, int pbase, float defValue)
// 如果值为10% 则 fraction_value=10%*base
//如果值格式为10%p,则fraction_value=10%*pbase
float fraction_value = typedArray.getFraction(R.styleable
.CustomTextViewStyle_fraction_value, 1, 1, 0);
String string_value = typedArray.getString(R.styleable.CustomTextViewStyle_string_value);
//获取像素值,浮点数 eg:27.5625
float dimension_value_float = typedArray.getDimension(R.styleable
.CustomTextViewStyle_dimension_value, 0f);
//将取得浮点像素值四舍五入 eg:28
int dimension_value = typedArray.getDimensionPixelSize(R.styleable
.CustomTextViewStyle_dimension_value, 0);
//将取得浮点像素值直接截取整数部分 eg:27
int dimension_value_offset_ = typedArray.getDimensionPixelOffset(R.styleable
.CustomTextViewStyle_dimension_value, 0);
int color_value = typedArray.getColor(R.styleable.CustomTextViewStyle_color_value, 0);
int reference_drawable_value = typedArray.getResourceId(R.styleable
.CustomTextViewStyle_reference_drawable_value, 0);
int reference_array_value = typedArray.getResourceId(R.styleable
.CustomTextViewStyle_reference_array_value, 0);
int enum_value = typedArray.getInt(R.styleable.CustomTextViewStyle_enum_value, -1);
int flag_value = typedArray.getInt(R.styleable.CustomTextViewStyle_flag_value, -1);
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(0, 0, 50, 50));
setCompoundDrawables(drawable, null, null, null);
//设置文字是否大写,斜体
if (flag_value >= 0) {
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 = 0; i < arrays.length; i++) {
System.out.println("arrays[" + i + "] = " + arrays[i]);
}
//遍历TypedArray
for (int i = 0, m = typedArray.getIndexCount(); i < m; i++) {
System.out.println("typedArray" + i + " type= " + typedArray.getType(i) + " value=" +
typedArray.getString(i));
}
//defStyle=0,defStyleRes =0
int textsize = typedArray.getDimensionPixelSize(R.styleable
.CustomTextViewStyle_text_size, 10);
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, 0);
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, 10);
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(0, 10);
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", 0);
if (resouceId > 0) {
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", 0);
if (resouceId > 0) {
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", 0);
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, 0, 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 = 0;
switch (checkedId) {
case R.id.rb1:
id = 0;
break;
case R.id.rb2:
id = 1;
break;
case R.id.rb3:
id = 2;
break;
case R.id.rb4:
id = 3;
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