04.自定义属性
自定义属性
自定义属性步骤
系统自带的 View 可以在 xml 中配置属性,对于写的好的自定义 View 同样可以在 xml 中配置属性,为了使自定义的
View 的属性可以在 xml 中配置,需要以下 4 个步骤:
- 在
attrs.xml
通过<declare-styleable>
为自定义 View 添加属性 - 在 xml 中为相应的属性声明属性值(注意 namespace)
- 在运行时(一般为构造函数)通过 TypedArray 获取获取属性值
- 将获取到的属性值应用到 View
Android 自定义控件之自定义属性及自定义属性定义 style
系统 attrs.xml 路径
D:\Android_sdk\sdk\platforms\android-27\data\res\values\attrs.xml
在 attrs.xml
定义自定义属性值
attrs.xml 文件定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RatioImageView">
<attr name="ratio" format="float"/>
</declare-styleable>
<declare-styleable name="TaskItemView">
<attr name="task_item_title" format="string"/>
<attr name="task_item_titleColor" format="color"/>
<attr name="task_item_titleTextSize" format="dimension"/>
<attr name="task_item_desc" format="string"/>
<attr name="task_item_descColor" format="color"/>
<attr name="task_item_descTextSize" format="dimension"/>
<attr name="task_item_descIcon" format="reference"/>
<attr name="task_item_isDescIcon" format="boolean"/>
<attr name="task_item_isDesc" format="boolean"/>
<attr name="task_item_isArrow" format="boolean"/>
<attr name="task_item_isDivider" format="boolean"/>
</declare-styleable>
</resources>
获取自定义属性值
Java 代码获取:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private String mTitle;
private String mDesc;
private int mTitleColor;
private int mDescColor;
private boolean isDivider;
private boolean isDesc;
private boolean isArrow;
private boolean isDescIcon;
private float mTextSize;
private float mDescSize;
private int mDescIconResId;
private void init(Context context, AttributeSet attrs) {
mInflater = LayoutInflater.from(getContext());
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TaskItemView);
try {
mTitle = ta.getString(R.styleable.TaskItemView_task_item_title);
mDesc = ta.getString(R.styleable.TaskItemView_task_item_desc);
mTitleColor = ta.getColor(R.styleable.TaskItemView_task_item_titleColor, Color.BLACK);
mDescColor = ta.getColor(R.styleable.TaskItemView_task_item_descColor, Color.BLACK);
isDivider = ta.getBoolean(R.styleable.TaskItemView_task_item_isDivider, DEFAULT_DIVIDER_SHOW);
isDesc = ta.getBoolean(R.styleable.TaskItemView_task_item_isDesc, DEFAULT_DESC_SHOW);
isArrow = ta.getBoolean(R.styleable.TaskItemView_task_item_isArrow, DEFAULT_ARROW_SHOW);
isDescIcon = ta.getBoolean(R.styleable.TaskItemView_task_item_isDescIcon, DEFAULT_ARROW_SHOW);
mTextSize = ta.getDimensionPixelSize(R.styleable.TaskItemView_task_item_titleTextSize, TITLE_TEXT_SIZE_DEFAULT_SP);
mDescSize = ta.getDimensionPixelSize(R.styleable.TaskItemView_task_item_descTextSize, DESC_TEXT_SIZE_DEFAULT_SP);
mDescIconResId = ta.getResourceId(R.styleable.TaskItemView_task_item_descIcon, R.drawable.ic_task_item_red_packet);
} finally {
ta.recycle();
}
}
format 可选值
1
2
3
4
5
6
7
8
9
10
string:字符串
reference:引用资源
color:颜色
boolean:布尔值
dimension:尺寸值
float:浮点型
integer:整型
fraction:百分数
enum:枚举类型
flag:位或运算
string 字符串
代码获取 getString()
reference 引用
代码获取 Drawable getDrawable(@StyleableRes int index)
或者 int getResourceId(@StyleableRes int index, int defValue)
color 颜色值
getColor()
boolean 布尔值
getBoolean()
dimension 尺寸值
getDimension()
float 浮点型
getFloat()
integer 整数值
getInt()
或者 getInteger()
fraction 百分比
enum 枚举
1
2
3
4
5
<attr name="type">
<enum name="password" value="1" />
<enum name="checkButton" value="2" />
<enum name="phone" value="3" />
</attr>
其中,一般属性需要指定 name
和 format
,枚举属性只需指定 name
,然后用 enum
标签指定所有可能属性的 name
和 value
(注:value 只能为 int 型)
获取值还是通过 getInt()
flag 位
1
2
3
4
5
6
7
8
<attr name="mode">
<flag name="left_text" value="0x000001"/>
<flag name="left_image" value="0x000010"/>
<flag name="center_text" value="0x000100"/>
<flag name="center_image" value="0x001000"/>
<flag name="right_text" value="0x010000"/>
<flag name="right_image" value="0x100000"/>
</attr>
java 代码:
1
int mode = ta.getInt(attr, -1);
AttributeSet 和 TypedArray
AttributeSet
保存的是该 View 声明的所有的属性
TypedArray
通过 AttributeSet
获取的值,如果是引用都变成了@+ 数字的字符串;TypedArray
其实是用来简化我们的工作的。
比如,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用 AttributeSet
去获得最终的像素值,那么需要第一步拿到 id,第二步再去解析 id。而 TypedArray 正是帮我们简化了这个过程。
如果通过 AttributeSet
获取最终的像素值的过程:
1
2
int widthDimensionId = attrs.getAttributeResourceValue(0, -1);
Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));
系统中定义的属性:sdk/platforms/android-xx/data/res/values
declare-styleable
就是帮我们在 R.java
中生成代码
1
2
3
4
5
6
7
8
9
10
11
public static final class attr {
public static final int testAttr=0x7f0100a9;
}
public static final class styleable {
public static final int test_android_text = 0;
public static final int test_testAttr = 1;
public static final int[] test = {
0x0101014f, 0x7f0100a9
};
}
- 引用 Android 系统帮我们定义好的:
<attr name="android:text"/>
,布局中使用android:text="haha"
- 引用已经在
attrs.xml
定义好的attr
,没有写 format
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<attr name="test_attr" format="boolean"/>
<declare-styleable name="EmptyView">
<attr name="stringtest" format="string"/>
<attr name="integertest" format="integer"/>
<attr name="android:text"/>
<attr name="test_attr"/>
<attr name="emptyIcon" format="reference"/>
<attr name="emptyDescText" format="string"/>
<attr name="emptyDescTextSize" format="dimension"/>
<attr name="emptyDescTextColor" format="color"/>
</declare-styleable>
- format 定义多个值
1
<attr name="textColor" format="reference|color" />
附:Android 中自定义属性的格式详解
1. reference:参考某一资源 ID
1
2
3
4
5
6
7
8
9
10
(1)属性定义:
<declare-styleable name = "名称">
<attr name = "background" format = "reference" />
</declare-styleable>
(2)属性使用:
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/图片ID"
/>
2. color:颜色值
1
2
3
4
5
6
7
8
9
10
(1)属性定义:
<declare-styleable name = "名称">
<attr name = "textColor" format = "color" />
</declare-styleable>
(2)属性使用:
<TextView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:textColor = "#00FF00"
/>
3. boolean:布尔值
1
2
3
4
5
6
7
8
9
10
(1)属性定义:
<declare-styleable name = "名称">
<attr name = "focusable" format = "boolean" />
</declare-styleable>
(2)属性使用:
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
android:focusable = "true"
/>
4. dimension:尺寸值
1
2
3
4
5
6
7
8
9
(1)属性定义:
<declare-styleable name = "名称">
<attr name = "layout_width" format = "dimension" />
</declare-styleable>
(2)属性使用:
<Button
android:layout_width = "42dip"
android:layout_height = "42dip"
/>
5. float:浮点值
1
2
3
4
5
6
7
8
9
10
(1)属性定义:
<declare-styleable name = "AlphaAnimation">
<attr name = "fromAlpha" format = "float" />
<attr name = "toAlpha" format = "float" />
</declare-styleable>
(2)属性使用:
<alpha
android:fromAlpha = "1.0"
android:toAlpha = "0.7"
/>
6. integer:整型值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(1)属性定义:
<declare-styleable name = "AnimatedRotateDrawable">
<attr name = "visible" />
<attr name = "frameDuration" format="integer" />
<attr name = "framesCount" format="integer" />
<attr name = "pivotX" />
<attr name = "pivotY" />
<attr name = "drawable" />
</declare-styleable>
(2)属性使用:
<animated-rotate
xmlns:android = "http://schemas.android.com/apk/res/android"
android:drawable = "@drawable/图片ID"
android:pivotX = "50%"
android:pivotY = "50%"
android:framesCount = "12"
android:frameDuration = "100"
/>
7. string:字符串
1
2
3
4
5
6
7
8
9
10
(1)属性定义:
<declare-styleable name = "MapView">
<attr name = "apiKey" format = "string" />
</declare-styleable>
(2)属性使用:
<com.google.android.maps.MapView
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"
/>
8. fraction:百分数
1
2
3
4
5
6
7
8
9
10
11
(1)属性定义:
<declare-styleable name="RotateDrawable">
<attr name = "visible" />
<attr name = "fromDegrees" format = "float" />
<attr name = "toDegrees" format = "float" />
<attr name = "pivotX" format = "fraction" />
<attr name = "pivotY" format = "fraction" />
<attr name = "drawable" />
</declare-styleable>
(2)属性使用:
<rotate xmlns:android = "http://schemas.android.com/apk/res/android"
android:interpolator = “@anim/动画 ID”
android:fromDegrees = “0”
android:toDegrees = “360”
android:pivotX = “200%”
android:pivotY = “300%”
android:duration = “5000”
android:repeatMode = “restart”
android:repeatCount = “infinite”
/>
9. enum:枚举值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(1)属性定义:
<declare-styleable name="名称">
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
(2)属性使用:
<LinearLayout
xmlns:android = "http://schemas.android.com/apk/res/android"
android:orientation = "vertical"
android:layout_width = "fill_parent"
android:layout_height = "fill_parent"
>
</LinearLayout>
10. flag:位或运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(1)属性定义:
<declare-styleable name="名称">
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
</declare-styleable>
(2)属性使用:
<activity
android:name = ".StyleAndThemeActivity"
android:label = "@string/app_name"
android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
<intent-filter>
<action android:name = "android.intent.action.MAIN" />
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
注意: 属性定义时可以指定多种类型值
1
2
3
4
5
6
7
8
9
10
(1)属性定义:
<declare-styleable name = "名称">
<attr name = "background" format = "reference|color" />
</declare-styleable>
(2)属性使用:
<ImageView
android:layout_width = "42dip"
android:layout_height = "42dip"
android:background = "@drawable/图片ID|#00FF00"
/>
很多自定义属性模板代码
- 定义属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<declare-styleable name="CommonToolbar">
<attr name="toolbar_mode">
<flag name="comon" value="0x000110"/>
<flag name="left_text" value="0x000001"/>
<flag name="left_image" value="0x000010"/>
<flag name="center_text" value="0x000100"/>
<flag name="center_image" value="0x001000"/>
<flag name="right_text" value="0x010000"/>
<flag name="right_image" value="0x100000"/>
</attr>
<attr name="left_titleText" format="string"/>
<attr name="left_titleColor" format="color"/>
<attr name="left_titleTextSize" format="dimension"/>
<attr name="left_titleIcon" format="reference"/>
<attr name="center_titleText" format="string"/>
<attr name="center_titleColor" format="color"/>
<attr name="center_titleTextSize" format="dimension"/>
<attr name="center_titleIcon" format="reference"/>
<attr name="right_titleText" format="string"/>
<attr name="right_titleColor" format="color"/>
<attr name="right_titleTextSize" format="dimension"/>
<attr name="right_titleIcon" format="reference"/>
<attr name="left_padding_left" format="dimension"/>
<attr name="right_padding_right" format="dimension"/>
<attr name="left_margin_left" format="dimension"/>
<attr name="right_margin_right" format="dimension"/>
</declare-styleable>
- 很多自定义属性获取模板代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
private void initAttrs(Context context, AttributeSet attrs) {
int defaultTextSize = dip2px(getContext(), DEFAULT_TEXT_SIZE); // android 的默认字体大小为12.0sp
int defaultTextColorPrimary = getTextColorPrimary();
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CommonToolbar);
try {
int indexCount = ta.getIndexCount();
for (int i = 0; i < indexCount; i++) {
int attr = ta.getIndex(i);
switch (attr) {
case R.styleable.CommonToolbar_toolbar_mode:
int mode = ta.getInt(attr, -1);
switchMode(mode);
break;
case R.styleable.CommonToolbar_left_titleText:
String leftTitleText = ta.getString(attr);
setText(mTvLeftTitle, leftTitleText);
break;
case R.styleable.CommonToolbar_left_titleIcon:
mLeftTitleDrawableResId = ta.getResourceId(attr, DEFAULT_LEFT_ICON_RES);
break;
case R.styleable.CommonToolbar_left_titleColor:
mLeftTextColor = ta.getColor(attr, defaultTextColorPrimary);
break;
case R.styleable.CommonToolbar_left_titleTextSize:
mLeftTextSize = ta.getDimensionPixelSize(attr, defaultTextSize);
break;
case R.styleable.CommonToolbar_center_titleText:
String centerTitleText = ta.getString(attr);
setText(mTvCenterTitle, centerTitleText);
break;
case R.styleable.CommonToolbar_center_titleIcon:
mCenterTitleDrawableResId = ta.getResourceId(attr, DEFAULT_CENTER_ICON_RES);
break;
case R.styleable.CommonToolbar_center_titleColor:
mCenterTextColor = ta.getColor(attr, defaultTextColorPrimary);
break;
case R.styleable.CommonToolbar_center_titleTextSize:
mCenterTextSize = ta.getDimensionPixelSize(attr, defaultTextSize);
break;
case R.styleable.CommonToolbar_right_titleText:
String rightTitleText = ta.getString(attr);
setText(mTvRightTitle, rightTitleText);
break;
case R.styleable.CommonToolbar_right_titleIcon:
mRightTitleDrawableResId = ta.getResourceId(attr, DEFAULT_RIGHT_ICON_RES);
break;
case R.styleable.CommonToolbar_right_titleColor:
mRightTextColor = ta.getColor(attr, defaultTextColorPrimary);
break;
case R.styleable.CommonToolbar_right_titleTextSize:
mRightTextSize = ta.getDimensionPixelSize(attr, defaultTextSize);
break;
case R.styleable.CommonToolbar_left_padding_left:
mLeftPaddingleft = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_PADDING_DP));
break;
case R.styleable.CommonToolbar_right_padding_right:
mRightPaddingRight = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_PADDING_DP));
break;
case R.styleable.CommonToolbar_left_margin_left:
mLeftMarginLeft = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_MAGIN_DP));
break;
case R.styleable.CommonToolbar_right_margin_right:
mRightMarginRight = ta.getDimensionPixelSize(attr, dip2px(context, DEFAULT_MAGIN_DP));
break;
default:
break;
}
}
} finally {
ta.recycle();
}
}
示例
- kt 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
private fun initAttributes(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
context.obtainStyledAttributes(
attrs, R.styleable.LurePointCouponVerticalView, defStyleAttr, 0
).run {
try {
// 圆角
mRoundCornerRadius = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_round_corner_radius,
dp2px(DEFAULT_ROUND_CORNER_DP)
).toFloat()
mRoundCornerWidth = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_round_corner_width,
dp2px(DEFAULT_BORDER_WIDTH_DP)
).toFloat()
mRoundCornerColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_round_corner_color,
Color.WHITE
)
// 解析渐变色
val gradientStartColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_gradient_start_color,
mDefaultBgColor
)
val gradientMiddleColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_gradient_middle_color,
-1
)
val gradientEndColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_gradient_end_color,
-1
)
mGradientColors = if (gradientMiddleColor != -1) {
if (gradientEndColor != -1) {
intArrayOf(gradientStartColor, gradientMiddleColor, gradientEndColor)
} else {
intArrayOf(gradientStartColor, gradientMiddleColor)
}
} else {
intArrayOf(gradientStartColor, gradientStartColor)
}
// 解析渐变方向
mGradientOrientation = Orientation.values()[
getInt(
R.styleable.LurePointCouponVerticalView_lpc_gradient_orientation,
Orientation.VERTICAL.ordinal
)
]
// 解析边框颜色
mBorderWidth = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_border_width,
dp2px(DEFAULT_BORDER_WIDTH_DP)
).toFloat()
mBorderColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_border_color,
Color.WHITE
)
// 挖洞
mDashEnable =
getBoolean(R.styleable.LurePointCouponVerticalView_lpc_dash_enable, false)
mCutRadius = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_cut_radius,
dp2px(DEFAULT_CUT_RADIUS_DP)
).toFloat()
mCutBorderWidth = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_cut_border_width,
dp2px(DEFAULT_BORDER_WIDTH_DP)
).toFloat()
mCutPadding = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_cut_padding,
dp2px(DEFAULT_CUT_PADDING_DP)
).toFloat()
mCutBorderColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_cut_border_color,
Color.parseColor("#FFE2CF")
)
// 虚线
mDashColor = getColor(
R.styleable.LurePointCouponVerticalView_lpc_dash_color,
ResourcesCompat.getColor(resources, android.R.color.background_dark, null)
)
mDashStrokeWidth = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_dash_stroke_width,
SUIUtils.dp2px(context, DEFAULT_BORDER_WIDTH_DP)
)
mDashLength = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_dash_length,
SUIUtils.dp2px(context, DEFAULT_DASH_LENGTH_DP)
)
mDashSpaceWidth = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_dash_space,
SUIUtils.dp2px(context, DEFAULT_DASH_STROKE_WIDTH_DP)
)
mDashMarginVertical = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_dash_margin_vertical,
SUIUtils.dp2px(context, DEFAULT_DASH_MARGIN_DP)
)
mDashMarginTop = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_dash_margin_top,
SUIUtils.dp2px(context, mDashMarginVertical.toFloat())
)
mDashMarginBottom = getDimensionPixelSize(
R.styleable.LurePointCouponVerticalView_lpc_dash_margin_bottom,
SUIUtils.dp2px(context, mDashMarginVertical.toFloat())
)
mDashOrientation = getInt(
R.styleable.LurePointCouponVerticalView_lpc_dash_orientation,
Orientation.VERTICAL.ordinal
)
} finally {
recycle()
}
}
}
- xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<declare-styleable name="LurePointCouponVerticalView">
<!--渐变-->
<attr name="lpc_gradient_start_color" format="color" />
<attr name="lpc_gradient_middle_color" format="color" />
<attr name="lpc_gradient_end_color" format="color" />
<attr name="lpc_gradient_orientation" format="integer">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<!--边框-->
<attr name="lpc_border_color" format="color" />
<attr name="lpc_border_width" format="dimension" />
<!--圆角-->
<attr name="lpc_round_corner_radius" format="dimension" />
<attr name="lpc_round_corner_width" format="dimension" />
<attr name="lpc_round_corner_color" format="color" />
<!--挖洞-->
<attr name="lpc_cut_radius" format="dimension" />
<attr name="lpc_cut_border_width" format="dimension" />
<attr name="lpc_cut_border_color" format="color" />
<attr name="lpc_cut_padding" format="dimension" />
<!--虚线-->
<attr name="lpc_dash_enable" format="boolean" />
<attr name="lpc_dash_stroke_width" format="dimension" />
<attr name="lpc_dash_color" format="color" />
<attr name="lpc_dash_length" format="dimension" />
<attr name="lpc_dash_space" format="dimension" />
<attr name="lpc_dash_margin_top" format="dimension" />
<attr name="lpc_dash_margin_bottom" format="dimension" />
<attr name="lpc_dash_margin_vertical" format="dimension" />
<attr name="lpc_dash_orientation" format="integer">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
</declare-styleable>
Reference
- Android 自定义 View 之 自定义 View 属性
- Android 中自定义属性(attrs.xml,TypedArray 的使用)
format 格式介绍比较详细
http://www.cnblogs.com/zhangs1986/p/3243040.html