如何在复合控件XML布局中直接访问自定义属性(无需代码解析)
无需代码解析,直接在复合视图布局中使用自定义属性的方案
好问题!你想要的是不用在Java代码里逐个设置子View的textColor,而是让复合视图的子布局直接引用自定义属性值——这其实可以通过给子布局注入包含自定义属性的主题上下文来实现,下面是具体步骤:
1. 修改自定义View的构造方法,注入主题属性
在MyView.java中,我们需要在inflate子布局前,把从XML中获取的自定义属性值注入到一个新的主题上下文中,让子布局的上下文能识别这些属性:
public class MyView extends ConstraintLayout { public MyView(Context context) { this(context, null); } public MyView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 解析XML中传入的自定义属性 TypedArray typedArray = context.obtainStyledAttributes( attrs, R.styleable.MyView, defStyleAttr, 0 ); // 创建带有默认主题覆盖的上下文 Context themedContext = new ContextThemeWrapper(context, R.style.ThemeOverlay_MyView); Resources.Theme viewTheme = themedContext.getTheme(); // 将解析到的属性值注入到主题中 if (typedArray.hasValue(R.styleable.MyView_numberTint)) { viewTheme.setAttribute( R.attr.numberTint, typedArray.getColor(R.styleable.MyView_numberTint, 0) ); } if (typedArray.hasValue(R.styleable.MyView_letterTint)) { viewTheme.setAttribute( R.attr.letterTint, typedArray.getColor(R.styleable.MyView_letterTint, 0) ); } typedArray.recycle(); // 使用注入了属性的上下文加载子布局 LayoutInflater.from(themedContext).inflate(R.layout.my_view_layout, this, true); } }
2. 定义ThemeOverlay,提供属性默认值
在res/values/styles.xml中添加一个主题覆盖样式,用来声明自定义属性的默认值,确保即使XML中没设置属性,子布局也能拿到有效值:
<style name="ThemeOverlay.MyView"> <!-- 自定义属性的默认值 --> <item name="numberTint">@android:color/white</item> <item name="letterTint">@android:color/black</item> </style>
3. 保持子布局的写法不变
你的复合视图布局(my_view_layout.xml)里的android:textColor="?attr/numberTint"写法完全不用改,现在它会自动从我们注入的主题上下文中获取对应的值:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:text="1" android:textColor="?attr/numberTint" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="A" android:textColor="?attr/letterTint" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="2" android:textColor="?attr/numberTint" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="B" android:textColor="?attr/letterTint" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="3" android:textColor="?attr/numberTint" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="C" android:textColor="?attr/letterTint" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </android.support.constraint.ConstraintLayout>
为什么原来的写法不行?
原来的子布局使用的是Activity的主题上下文,而你的numberTint和letterTint是自定义属性,并没有在Activity的主题中声明,所以直接用?attr/xxx会找不到对应的属性值。通过ContextThemeWrapper创建一个包含我们自定义属性的主题上下文,子布局就能正确识别这些属性了。
内容的提问来源于stack exchange,提问作者shredder
相关产品推荐
相关产品推荐

