Con algunos códigos personalizados que usan API cuidadosamente seleccionados, puede imitar la forma en que Android infla los archivos XML de diseño y aún así beneficiarse de las optimizaciones XML y los beneficios que Android tiene como compilados archivos XML y referencias a recursos arbitrarios dentro de sus archivos XML personalizados. No puede enganchar directamente en el LayoutInflater
existente porque esa clase solo puede ocuparse de inflar View
s. Para que el siguiente código funcione, coloque su archivo XML en 'res/xml' en su aplicación.
Primero, aquí está el código que analiza el archivo XML (compilado) e invoca el constructor Model
. Es posible que desee agregar algún mecanismo de registro para que pueda registrar fácilmente una clase para cualquier etiqueta, o puede usar ClassLoader.loadClass()
para que pueda cargar clases en función de su nombre.
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;
}
}
Con esto en su lugar, se puede poner el "análisis" de los atributos dentro de la clase Model
, al igual que View
lo hace:
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;
}
}
Por encima de que he referenciado atributos a través de la representación de cadena. Si desea ir más allá, puede definir recursos de atributos específicos de la aplicación y usarlos en su lugar, pero eso complicará bastante las cosas (consulte Declaring a custom android UI element using XML). De todos modos, con toda la disposición de recursos y esto en una actividad ficticia:
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);
}
}
obtendrá esta salida:
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
Tenga en cuenta que no se puede tener @resource/scout
en el archivo XML ya no es resource
un tipo de recurso válido, pero @string/foo
funciona bien. También debería poder usar, por ejemplo, @drawable/foo
con algunas modificaciones triviales en el código.
Gracias señor por la respuesta muy detallada; eres mi héroe. – AedonEtLIRA