如何使用安卓布局充气工厂注入和覆盖布局属性



有时我们想向默认的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));
} 

最新更新