web-dev-qa-db-fra.com

Comment définir l'attribut enum par valeur au lieu de nom dans Android Layout?

J'ai une vue personnalisée en tenant une autre. Hiérarchie:

MyOuterView
->MyInnerView

MyInnerView a un attribut enum tel que:

<attr name="myAttr" format="enum">
    <enum name="foo" value="0"/>
    <enum name="bar" value="1"/>
</attr>

Je peux donc instancier le composant dans MyOuterView XML comme:

<com.example.MyInnerView
....
app:myAttr="foo"/>

Ce qui fonctionne bien sûr. La MyOuterView offre un argument pour la personnalisation elle-même. Sur la base de cet argument, je souhaite définir l’argument de MyInnerView.

Le comportement souhaité est que je puisse travailler avec Data Binding comme:

<com.example.MyInnerView
....
app:myAttr="@{data.getMyAttr()}"/>

getMyAttr() ressemble à:

public int getMyAttr() {
    return myAttr; // returns 0 or 1
}

Le résultat est un problème de compilation.

****/data binding error **** msg: impossible de définir le paramètre de configuration pour l'attribut 'app: myAttr' avec le type de paramètre int sur com.example.MyInnerView

Donc, évidemment, je ne peux pas définir l'énumération par valeur mais uniquement par nom. Une idée en dehors de la création de la MyInnerView par programme? Veuillez noter que je ne peux pas changer MyInnerView.

17
Michael Troger

Je ne peux pas définir l'énumération par valeur mais uniquement par nom

Ceci n'est pas entièrement vrai. Vous ne seriez pas en mesure de définir la valeur avec DataBinding même par son nom, le fait est que la valeur d'un attribut défini dans un fichier xml est transmise à la variable View via le constructeur. Un View pourrait avoir à la fois un attribut et un séparateur, mais ce n'est pas nécessairement le cas. Par exemple, si vous définissez une valeur pour Android:text sur une TextView, cette valeur est définie sur com.Android.internal.R.styleable.TextView_text; elle est ensuite extraite de la AttributeSet dans le constructeur. Si vous utilisez DataBinding, setText() est appelé, mais ce sont deux choses complètement différentes, la fonctionnalité est la même mais elles ne sont pas liées dans le code.

Etant donné que vous ne pouvez pas modifier MyInnerView et qu’il n’existe aucun séparateur que vous pouvez appeler pour myAttr, votre seule option est de le transmettre par le constructeur. DataBinding n'est tout simplement pas réalisable, même avec un BinderAdapter, vous ne pourriez pas définir la valeur dans AttributeSet car View est déjà instancié à ce stade.

Option 1 - thèmes

Définir un nouvel attribut

<attr name="MyInnerViewAttrValue" format="integer" />

Puis résolvez cet attribut avec un style, par exemple, vous pourriez avoir

<style name="AppTheme.Foo">
    <item name="MyInnerViewAttrValue">0</item>
</style>

<style name="AppTheme.Bar">
    <item name="MyInnerViewAttrValue">1</item>
</style>

Définissez-le dans la mise en page xml

<com.example.MyInnerView
    ...
    app:myAttr="?attr/MyInnerViewAttrValue" />

Ensuite, appelez setTheme(int) dans la Activity avant l'instanciation des vues.

Option 2 - BindingAdapter personnalisé

Si vous vouliez définir votre valeur DataBinding (parce que vous aviez un setter ou parce que vous vouliez pirater MyInnerView avec réflexion), vous devrez créer un BindingAdapter personnalisé, par exemple

@BindingAdapter("myAttrValue")
public static void setMyAttr(MyInnerView myInnerView, int value) {
    switch (value) {
        case 0:
            myInnerView.foo();
            break;
        default:
            myInnerView.bar();
    }
}

Puis dans la mise en page xml

<com.example.lelloman.dummy.MyInnerView
    ...
    myAttrValue="@{model.attr}" />

Et donnez la valeur int de votre ViewModel

public class MyInnerViewModel {

    public int getAttr() {
        return 1234;
    }
}
4
lelloman

Malheureusement, vous pouvez utiliser un nom d'attribut tel que app:myAttr="@{...}" uniquement s'il existe un séparateur pour cela et que ce séparateur est associé à nom d'attribut ou qu'il peut être résolu par son nom. Dans votre cas, si cet attribut est utilisé uniquement dans le constructeur de View et view n’a aucune méthode publique pour changer cela - vous avez encore quelques solutions possibles: - étendre View et implémenter une logique pour personnaliser l’affichage comme cet attribut do (si possible) - make BindingAdapter et utilisez Reflection (si possible). - créez votre propre vue par copier-coller et corriger)

1
Roman_D