Con un codice personalizzato che utilizza API accuratamente selezionate, è possibile imitare il modo in cui Android gonfia i file XML di layout e ancora beneficiare delle ottimizzazioni XML e dei gadget che Android ha come file XML compilati e riferimenti a risorse arbitrarie all'interno dei file XML personalizzati. Non è possibile connettersi direttamente allo LayoutInflater
esistente poiché questa classe può gestire solo il gonfiaggio di View
s. Affinché il codice sottostante funzioni, inserisci il tuo file XML in "res/xml" nella tua applicazione.
In primo luogo, ecco il codice che analizza il file (compilato) XML e invoca il costruttore Model
. Potresti voler aggiungere un meccanismo di registrazione in modo da poter facilmente registrare una classe per qualsiasi tag, oppure potresti voler usare ClassLoader.loadClass()
in modo che tu possa caricare le classi in base al loro nome.
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 questo in luogo, si può mettere il "parsing" degli attributi all'interno della classe Model
, molto simile View
lo fa:
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;
}
}
Sopra ho fatto riferimento attributi attraverso la rappresentazione di stringa. Se si desidera andare oltre, è possibile definire risorse di attributo specifiche dell'applicazione e utilizzarle, ma ciò complicherà un po 'le cose (vedere Declaring a custom android UI element using XML). In ogni caso, con tutta la messa a punto delle risorse e questo in un'attività fittizia:
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);
}
}
si otterrà questa uscita:
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
Si noti che non si può avere @resource/scout
nel file XML poiché non è resource
un tipo di risorsa valido, ma @string/foo
funziona correttamente. Dovresti anche essere in grado di usare ad esempio @drawable/foo
con alcune piccole modifiche al codice.
Grazie signore per la risposta molto dettagliata; tu sei il mio eroe. – AedonEtLIRA