有没有办法为
Android创建自定义XML数据类型?
我有一个类模型,其中包含我的实体的所有统计信息.我希望能够从xml类似地扩展Model类 – 嗯,exaclty就像View一样.这可能吗?
例:
<?xml version="1.0" encoding="utf-8"?> <models xmlns:android="http://schemas.android.com/apk/res/android"> <model name="tall_model" type="@string/infantry" stat_attack="5" >Tall Gunner</model> <model name="short_model" type="@string/infantry" stat_attack="3" ability="@resource/scout" >Short Gunner</model> <model name="big_tank" type="@string/vehicle" stat_attack="7" armour="5" >Big Tank</model> </models>
而我想要膨胀的课程.
class Model [extends Object] { public Model(Context context,AttributeSet attrs) { // I think you understand what happens here. } // ... }
解决方法
使用一些使用精心选择的API的自定义代码,您可以模仿Android扩展布局XML文件的方式,并且仍然可以从Android的XML优化和好处中获益,这些XML优化和好处就像编译XML文件和引用自定义XML文件中的任意资源一样.您无法直接挂入现有的LayoutInflater,因为该类只能处理膨胀的视图.为了使下面的代码能够工作,请将XML文件放在应用程序的“res / xml”中.
首先,这是解析(编译的)XML文件并调用Model构造函数的代码.您可能希望添加一些注册机制,以便您可以轻松地为任何标记注册类,或者您可能希望使用ClassLoader.loadClass(),以便您可以根据其名称加载类.
public class CustomInflator { public static ArrayList<Model> inflate(Context context,int xmlFileResId) throws Exception { ArrayList<Model> models = new ArrayList<Model>(); XmlResourceParser parser = context.getResources().getXml(R.xml.models); Model currentModel = null; int token; while ((token = parser.next()) != XmlPullParser.END_DOCUMENT) { if (token == XmlPullParser.START_TAG) { if ("model".equals(parser.getName())) { // You can retrieve the class in other ways if you wish Class<?> clazz = Model.class; Class<?>[] params = new Class[] { Context.class,AttributeSet.class }; Constructor<?> constructor = clazz.getConstructor(params); currentModel = (Model)constructor.newInstance(context,parser); models.add(currentModel); } } else if (token == XmlPullParser.TEXT) { if (currentModel != null) { currentModel.setText(parser.getText()); } } else if (token == XmlPullParser.END_TAG) { // FIXME: Handle when "model" is a child of "model" if ("model".equals(parser.getName())) { currentModel = null; } } } return models; } }
有了这个,您可以将属性的“解析”放在Model类中,就像View一样:
public class Model { private String mName; private String mType; private int mStatAttack; private String mText; public Model(Context context,AttributeSet attrs) { for (int i = 0; i < attrs.getAttributeCount(); i++) { String attr = attrs.getAttributeName(i); if ("name".equals(attr)) { mName = attrs.getAttributeValue(i); } else if ("type".equals(attr)) { // This will load the value of the string resource you // referenced in your XML int stringResource = attrs.getAttributeResourceValue(i,0); mType = context.getString(stringResource); } else if ("stat_attack".equals(attr)) { mStatAttack = attrs.getAttributeIntValue(i,-1); } else { // Todo: Parse more attributes } } } public void setText(String text) { mText = text; } @Override public String toString() { return "model name=" + mName + " type=" + mType + " stat_attack=" + mStatAttack + " text=" + mText; } }
上面我通过字符串表示引用了属性.如果你想更进一步,你可以定义特定于应用程序的属性资源并使用它们,但这会使事情变得复杂(见Declaring a custom android UI element using XML).无论如何,所有资源设置和虚拟活动:
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { for (Model m : CustomInflator.inflate(this,R.xml.models)) { Log.i("Example","Parsed: " + m.toString()); } } catch (Exception e) { Log.e("Example","Got " + e); } }
你会得到这个输出:
I/Example ( 1567): Parsed: model name=tall_model type=Example3 stat_attack=5 text=Tall Gunner I/Example ( 1567): Parsed: model name=short_model type=Example3 stat_attack=3 text=Short Gunner I/Example ( 1567): Parsed: model name=big_tank type=Example2 stat_attack=7 text=Big Tank
请注意,您的XML文件中不能包含@ resource / scout,因为资源不是有效的资源类型,但@ string / foo工作正常.您还应该能够使用例如@ drawable / foo对代码进行一些微不足道的修改.