问题描述
有时我们希望将自定义属性添加到默认的 Android 组件或覆盖已设置的属性,例如文本字段。
一种解决方案是扩展默认组件并为视图添加自定义样式属性。但该解决方案有一个主要缺点:您必须扩展此类视图的每个扩展组件。例如,如果您要扩展 TextView,它不会应用 EditView,除非您也扩展它。
解决方法
这个方案的优点是使用LayoutInflator Factory,布局不需要修改,可以应用于已经写好的代码。
//The LayoutInflater.Factory2 implementation
private class CustomLayoutFactory implements LayoutInflater.Factory2{
private View createView(String name,Context context,AttributeSet attrs){
View view = null;
//For example if we need to filter the views only from the android.widget package
//the rest of componans will be handled as ususal
String prefix = name.contains(".") ? null : "android.widget.";
try {
view = inflater.createView(name,prefix,attrs);
handle(view);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return view;
}
private static final class StyleableHelper{
static int[] TextViewStyleable = new int[0];
static int TextView_text = 0;
static {
try {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
Class clazz = Class.forName("com.android.internal.R$styleable");
TextViewStyleable = (int[]) clazz.getField("TextView").get(null);
TextView_text = clazz.getField("TextView_text").getInt(null);
}else{
//Pay attention if you are building apps for target SDK 30 and above the reflection for android internal objects are disabled
//In order to soleve this you have to lower the target SDK and extract styleable object and desired ids
TextViewStyleable = new int[]{16842766,16842804,16842901,16842902,16842903,16842904,16842905,16842906,16842907,16842923,16842927,16842928,16842929,16843039,16843040,16843071,16843072,16843086,16843087,16843088,16843089,16843090,16843091,16843092,16843093,16843094,16843095,16843096,16843097,16843098,16843099,16843100,16843101,16843102,16843103,16843104,16843105,16843106,16843107,16843108,16843109,16843110,16843111,16843112,16843113,16843114,16843115,16843116,16843117,16843118,16843119,16843120,16843121,16843287,16843288,16843293,16843296,16843299,16843300,16843364,16843365,16843366,16843461,16843462,16843463,16843540,16843541,16843542,16843614,16843615,16843618,16843636,16843660,16843666,16843667,16843692,16843869,16843958,16843959,16843990,16843991,16843997,16843998,16843999,16844085,16844086,16844087,16844088,16844102,16844135,16844144,16844155,16844157,16844158,16844159,16844165,16844178,17957193,17957194};
TextView_text = 18;
}
}catch (Exception e){
Log.e(LOG_MODULE_TAG,"Styleable reflection failed",e);
}
}
}
private void handle(View view){
TypedArray typedArray = null;
try {
if(view instanceof TextView) {
typedArray = view.getContext().obtainStyledAttributes(attrs,StyleableHelper.TextViewStyleable);
CharSequence s = null;
try {
int textId = typedArray.getResourceId(StyleableHelper.TextView_text,-1);
if(textId > 0){
//Here we are finding a new resource for that text id that for exaple can be sent by server side
s = TextMapper.getInstance()
.getByResourceID(typedArray.getResourceId(StyleableHelper.TextView_text,-1));
}
}catch (Resources.NotFoundException nfe) {
Log.e(LOG_TAG,"Resource for text: [" + typedArray.getString(StyleableHelper.TextView_text) + "] not found viewId: "
+ Integer.toHexString(view.getId());
}catch (Exception e){
Log.e(LOG_TAG,"Unhandled exception on view id: " + Integer.toHexString(view.getId())
+ " text: " + typedArray.getString(StyleableHelper.TextView_text));
}
if(s != null)
((TextView) view).setText(s);
}
}finally {
if(typedArray != null)
typedArray.recycle();
}
return view;
}
}
}
就是这样,现在您要做的就是用定制的充气机包裹原始充气机:
@Override
public void setContentView(@LayoutRes int layoutResID) {
LayoutInflater inflator = inflater.cloneInContext(this);
inflator.setFactory2(CustomLayoutFactory());
setContentView(
inflator.inflate(layoutResID,null));
}
,
我建议为每种类型的小部件编写一个 BaseSuperClass。一开始是分配的,但这样您就可以完全控制每个小部件,并且可以将关注点分开。