Создать пользовательский тип данных XML?

есть ли способ создать пользовательский тип данных XML для Android?

у меня есть класс Model который содержит всю статистику моих сущностей. Я хочу иметь возможность надувать Model класс из xml аналогичен-ну, 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.
    }
    // ...
}

3 ответов


С помощью некоторого пользовательского кода, используя тщательно отобранные API, вы можете имитировать, как Android раздувает XML-файлы макета и по-прежнему извлекать выгоду из оптимизации XML и лакомства, которые Android имеет как скомпилированные XML-файлы и ссылки на произвольные ресурсы в ваших пользовательских XML-файлов. Вы не можете напрямую подключиться к существующему LayoutInflater потому что класс может иметь дело только с надуванием Views. Для того, чтобы приведенный ниже код работал, поместите свой 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;
    }
}

выше Я ссылался на атрибуты через строковое представление. Если вы хотите пойти дальше, вы можете определить ресурсы атрибутов конкретного приложения и использовать их вместо этого, но это немного усложнит ситуацию (см. объявление пользовательского элемента пользовательского интерфейса android с помощью 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

обратите внимание, что вы не можете иметь @resource/scout в XML-файл с resource не является допустимым типом ресурса, но @string/foo работает нормально. Вы также должны иметь возможность использовать, например @drawable/foo С некоторыми тривиальными изменениями кода.


Если вы расширяете существующий класс представления, например TextView, EditText, вы можете вызвать его внутри макета xml.

Это Ссылка На Android для пользовательского компонента.

и вы можете определить пользовательские xml attrubutes тоже, это пример и еще один .

надеюсь, это поможет вам!


механизм сериализации XML не согласован на Android. Вместо этого я бы рекомендовал Json, возможно, с библиотекой Gson.