今天使用一个自定义控件继承自 AppCompatButton, 字体不是居中的, 最后排查发现是构造方法的问题;
对比 AppCompatButton 的源码发现是 defStyleAttr 参数的问题
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 |
public class TButton extends android.support.v7.widget.AppCompatButton { public TButton(Context context) { this(context, null); } public TButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TButton(Context context, AttributeSet attrs, int defStyleAttr{ super(context, attrs, defStyleAttr); } ... } // AppCompatButton类 public class AppCompatButton extends Button implements TintableBackgroundView, AutoSizeableTextView { private final AppCompatBackgroundHelper mBackgroundTintHelper; private final AppCompatTextHelper mTextHelper; public AppCompatButton(Context context) { this(context, null); } public AppCompatButton(Context context, AttributeSet attrs) { this(context, attrs, R.attr.buttonStyle); } public AppCompatButton(Context context, AttributeSet attrs, int defStyleAttr) { super(TintContextWrapper.wrap(context), attrs, defStyleAttr); } ''' } |
对 defStyleAttr 参数的解释:
The resource identifier of an attribute in the current theme whose value is the the resource id of a style. The specified style’s attribute values serve as default values for the button. Set this parameter to 0 to avoid use of default values.
当前 Theme 主题中的一个资源标识符, 其值引向一个 style 样式; 这个样式里有一些 button 的属性默认值; 如果设置为0就不使用默认值;
从源码中查看 AppCompatButton 的 R.attr.buttonStyle
attrs.xml 中定义的 buttonStyle:
/frameworks/base/core/res/res/values/attrs.xml
1 2 |
<!-- Normal Button style. --> <attr name="buttonStyle" format="reference" /> |
Theme 和 style 中定义的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- 当前的 Theme(父级) 中 --> <item name="buttonStyle">@style/Widget.AppCompat.Button</item> <!-- 指向的默认style 中 tag 的值 --> <style name="Widget.AppCompat.Button" parent="Base.Widget.AppCompat.Button"/> <style name="Base.Widget.AppCompat.Button" parent="android:Widget"> <item name="android:background">@drawable/abc_btn_default_mtrl_shape</item> <item name="android:textAppearance">?android:attr/textAppearanceButton</item> <item name="android:minHeight">48dip</item> <item name="android:minWidth">88dip</item> <item name="android:focusable">true</item> <item name="android:clickable">true</item> <item name="android:gravity">center_vertical|center_horizontal</item> </style> |
所以第三个参数为 0 时, android:gravity 的默认值未设置, 导致文本位于左上角;
从上面的例子中可以看出,通过 下列方法可以写自己的 style 加到主题中
1: 在 attrs.xml 文件中添加属性 name
2: Theme 中将步骤一的name, 对应到一个样式 style
3: 在自定义 view 的 defStyleAttr 处使用这个样式
继承 View 的自定义控件不需要处理第三个构造参数
1 2 3 4 5 6 |
public View(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } |
可以看到继承 View 的子类不需要特殊的 style, 所以一开始自定义 View 从继承 View 开始练习, 很容易忽视对第三个参数的处理;
结论: Android 自定义 View, 如果继承自非 View 的控件, 需要看下父控件的源码, 考虑 defStyleAttr 的取值;
解析属性的优先级: xml > style > defStyleAttr > defStyleRes(defStyleAttr为0时的默认 style) > theme
补充:
- 第二个参数 attrs 是 xml 指定的属性
- 可以借助下面的方法获取完整的属性值
TypedArray a = theme.obtainStyledAttributes(attrs,defStyleAttr, defStyleAttr, defStyleRes);
0 Comments