在Android中为自定义View添加自定义属性可以让你的组件更加灵活和可配置。以下是实现自定义属性的完整步骤和最佳实践。
在res/values/目录下创建或修改attrs.xml文件:
<resources>
<declare-styleable name="CustomTextView">
<!-- 基本属性类型 -->
<attr name="customText" format="string"/>
<attr name="customTextColor" format="color"/>
<attr name="customTextSize" format="dimension"/>
<attr name="customEnable" format="boolean"/>
<attr name="customCount" format="integer"/>
<attr name="customFloat" format="float"/>
<!-- 枚举类型 -->
<attr name="customMode">
<enum name="normal" value="0"/>
<enum name="bold" value="1"/>
<enum name="italic" value="2"/>
</attr>
<!-- 标志位类型 -->
<attr name="customFlags">
<flag name="flagA" value="0x01"/>
<flag name="flagB" value="0x02"/>
</attr>
<!-- 引用已有属性 -->
<attr name="android:text"/> <!-- 重用系统属性 -->
</declare-styleable>
</resources>public class CustomTextView extends View {
private String mCustomText;
private int mCustomTextColor;
private float mCustomTextSize;
private boolean mCustomEnable;
private int mCustomMode;
private int mCustomFlags;
public CustomTextView(Context context) {
this(context, null);
}
public CustomTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 获取自定义属性
TypedArray a = context.obtainStyledAttributes(
attrs,
R.styleable.CustomTextView,
defStyleAttr,
0); // defStyleRes
try {
mCustomText = a.getString(R.styleable.CustomTextView_customText);
mCustomTextColor = a.getColor(
R.styleable.CustomTextView_customTextColor,
Color.BLACK); // 默认值
mCustomTextSize = a.getDimension(
R.styleable.CustomTextView_customTextSize,
16f); // 默认16sp
mCustomEnable = a.getBoolean(
R.styleable.CustomTextView_customEnable,
true);
mCustomMode = a.getInt(
R.styleable.CustomTextView_customMode,
0); // 默认normal
mCustomFlags = a.getInt(
R.styleable.CustomTextView_customFlags,
0);
// 读取系统属性
String text = a.getString(R.styleable.CustomTextView_android_text);
} finally {
a.recycle(); // 必须回收TypedArray
}
}
}<com.example.myapp.view.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="默认文本"
app:customText="自定义文本"
app:customTextColor="#FF5722"
app:customTextSize="20sp"
app:customEnable="true"
app:customMode="bold"
app:customFlags="flagA|flagB"/>注意:需要添加命名空间:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
...>public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 先读取默认样式
TypedArray a = context.obtainStyledAttributes(
attrs,
R.styleable.CustomTextView,
defStyleAttr,
R.style.DefaultCustomTextViewStyle); // 默认样式
// ...
}在res/values/styles.xml中定义默认样式:
<style name="DefaultCustomTextViewStyle">
<item name="customTextColor">#3F51B5</item>
<item name="customTextSize">18sp</item>
<item name="customMode">bold</item>
</style>public class CustomTextView extends View {
// ...
public void setCustomTextColor(int color) {
if (mCustomTextColor != color) {
mCustomTextColor = color;
invalidate(); // 触发重绘
requestLayout(); // 如果需要重新测量
}
}
// 为所有自定义属性提供setter方法...
}<declare-styleable name="CustomLayout">
<attr name="layout_maxChildren" format="integer"/>
<attr name="layout_orientation">
<enum name="horizontal" value="0"/>
<enum name="vertical" value="1"/>
</attr>
<!-- 子View的布局属性 -->
<attr name="layout_customGravity">
<flag name="left" value="0x01"/>
<flag name="right" value="0x02"/>
<flag name="center_h" value="0x04"/>
</attr>
</declare-styleable>在子View中读取布局属性:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
// 读取自定义布局参数
int gravity = lp.customGravity;
// ...
}
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(MATCH_PARENT, MATCH_PARENT);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends ViewGroup.LayoutParams {
public int customGravity;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(
attrs, R.styleable.CustomLayout_Layout);
customGravity = a.getInt(
R.styleable.CustomLayout_Layout_layout_customGravity,
0);
a.recycle();
}
}-
性能优化:
- 在
onDraw()外解析属性值 - 避免在
draw()或layout()中频繁读取属性
- 在
-
兼容性处理:
// 检查属性是否存在 if (a.hasValue(R.styleable.CustomTextView_customText)) { mCustomText = a.getString(R.styleable.CustomTextView_customText); }
-
文档注释:
<!-- customMode: 控制文本显示模式 0 - normal (默认) 1 - bold 2 - italic --> <attr name="customMode" format="enum"> <enum name="normal" value="0"/> <enum name="bold" value="1"/> <enum name="italic" value="2"/> </attr>
-
属性组合:
<attr name="customStyle" format="reference"/> <!-- 在style中组合多个属性 --> <style name="MyCustomStyle"> <item name="customTextColor">#FF9800</item> <item name="customMode">bold</item> </style> <!-- 使用 --> <CustomTextView app:customStyle="@style/MyCustomStyle" ... />
-
主题属性引用:
<!-- 定义主题属性 --> <attr name="colorPrimary" format="color"/> <!-- 在主题中设置 --> <style name="AppTheme" parent="Theme.Material.Light"> <item name="colorPrimary">#3F51B5</item> </style> <!-- 在自定义属性中引用 --> <attr name="customHeaderColor" format="reference|color"/> <!-- 使用 --> <CustomTextView app:customHeaderColor="?attr/colorPrimary" ... />
通过以上方法,你可以创建出高度可定制、易维护的自定义View组件。记得在实现后充分测试各种属性组合的效果,确保在不同Android版本和设备上表现一致。