2011-12-04 18 views
7

¿Hay alguna manera de crear un tipo de datos XML personalizado para Android?¿Crear un tipo de datos XML personalizado?

Tengo una clase Model que contiene todas las estadísticas de mis entidades. Quiero poder inflar la clase Model de xml similar, bueno, exactamente como lo hace View. es posible?

Ejemplo:

<?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> 

Y la clase me gustaría inflar.

class Model [extends Object] { 
    public Model(Context context, AttributeSet attrs) { 
     // I think you understand what happens here. 
    } 
    // ... 
} 

Respuesta

8

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.

+0

Gracias señor por la respuesta muy detallada; eres mi héroe. – AedonEtLIRA

0

El mecanismo de serialización de XML no es coherente en Android. Recomendaría a Json en su lugar, posiblemente con la biblioteca Gson.

1

si extiende una clase de vista existente, por ejemplo TextView, EditText, puede llamarla dentro del diseño xml.

Este es el Android Reference para componente personalizado.

Y también puede definir atributos de xml personalizados, esto es example y another one.

Espero que te ayude!

+0

Gracias por la entrada, pero no estoy ampliando View o cualquier otro widget.Quiero tener un archivo xml sin formato que apunte a los recursos que he definido en mi cadena y en los archivos xml enteros [R]. – AedonEtLIRA

Cuestiones relacionadas