web-dev-qa-db-fra.com

android liaison de données avec une vue personnalisée

Le guide de liaison de données Android traite des valeurs de liaison dans une activité ou un fragment, mais existe-t-il un moyen d'effectuer la liaison de données avec une vue personnalisée?

Je voudrais faire quelque chose comme:

<LinearLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <com.mypath.MyCustomView
        Android:id="@+id/my_view"
        Android:layout_width="match_parent"
        Android:layout_height="40dp"/>

</LinearLayout>

avec my_custom_view.xml:

<layout>

<data>
    <variable
        name="myViewModel"
        type="com.mypath.MyViewModelObject" />
</data>

<LinearLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <TextView
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:text="@{myViewModel.myText}" />

</LinearLayout>

</layout>

Bien qu'il semble possible de le faire en définissant des attributs personnalisés dans la vue personnalisée, cela deviendrait rapidement fastidieux s'il y a beaucoup de valeurs à associer.

Y a-t-il un bon moyen d'accomplir ce que j'essaie de faire?

40
Dave

Dans votre vue personnalisée, gonflez la mise en page comme vous le feriez normalement et fournissez un outil de définition pour l'attribut que vous souhaitez définir:

private MyCustomViewBinding mBinding;
public MyCustomView(...) {
    ...
    LayoutInflater inflater = (LayoutInflater)
        context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mBinding = MyCustomViewBinding.inflate(inflater);
}

public void setMyViewModel(MyViewModelObject obj) {
    mBinding.setMyViewModel(obj);
}

Ensuite, dans la mise en page, vous l'utilisez dans:

<layout xmlns...>
    <data>
        <variable
            name="myViewModel"
            type="com.mypath.MyViewModelObject" />
    </data>

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

        <com.mypath.MyCustomView
            Android:id="@+id/my_view"
            app:myViewModel="@{myViewModel}"
            Android:layout_width="match_parent"
            Android:layout_height="40dp"/>

    </LinearLayout>
</layout>

Dans ce qui précède, un attribut de liaison automatique est créé pour app: myViewModel car il existe un séparateur nommé setMyViewModel.

54
George Mount

Tout d’abord, ne faites pas cela si cette vue personnalisée est déjà en cours <include> dans une autre présentation, telle que activité, etc. Vous aurez juste une exception indiquant que la balise est une valeur inattendue. La liaison de données l'a déjà exécutée, vous êtes donc prêt.

Avez-vous essayé d'utiliser onFinishInflate pour exécuter la liaison? (Exemple Kotlin)

override fun onFinishInflate() {
    super.onFinishInflate()
    this.dataBinding = MyCustomBinding.bind(this)
}

Gardez à l'esprit que si vous utilisez la liaison dans votre vue, elle ne pourra pas être créée par programme, au moins il serait très compliqué de prendre en charge les deux, même si vous le pouvez.

6
androidguy

Suite à la solution présentée par george L'éditeur graphique de Android studio n'était plus en mesure de restituer la vue personnalisée. La raison en est qu'aucune vue n'est réellement gonflée dans le code suivant:

public MyCustomView(...) {
    ...
    LayoutInflater inflater = (LayoutInflater)
        context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mBinding = MyCustomViewBinding.inflate(inflater);
}

Je suppose que la reliure gère l'inflation, mais l'éditeur graphique ne l'a pas aimé.

Dans mon cas d'utilisation spécifique, je voulais lier un seul champ et non un modèle de vue complet. Je suis venu avec (kotlin entrant):

class LikeButton @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {

    val layout: ConstraintLayout = LayoutInflater.from(context).inflate(R.layout.like_button, this, true) as ConstraintLayout

    var numberOfLikes: Int = 0
      set(value) {
          field = value
          layout.number_of_likes_tv.text = numberOfLikes.toString()
      }
}

Le bouton J'aime est constitué d'une image et d'une vue texte. La vue texte contient le nombre de j'aime, que je veux définir via la liaison de données.

En utilisant le setter pour numberOfLikes comme attribut dans le XML suivant, la liaison de données crée automatiquement l'association:

<views.LikeButton
  Android:id="@+id/like_btn"
  Android:layout_width="wrap_content"
  Android:layout_height="wrap_content"
  app:numberOfLikes="@{story.numberOfLikes}" />

Pour en savoir plus: https://medium.com/google-developers/Android-data-binding-custom-setters-55a25a7aea47

4
Upvote

Il y a déjà de bonnes réponses ici, mais je voulais offrir ce que je crois être le plus simple.

Créez votre contrôle personnalisé avec les balises de présentation qui l'entourent, comme toute autre disposition. Voir la barre d'outils suivante pour exemple. cela est utilisé dans chacune des classes d'activité

<layout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    xmlns:app="http://schemas.Android.com/apk/res-auto">

<data>
    <variable name="YACustomPrefs" type="com.appstudio35.yourappstudio.models.YACustomPreference" />
</data>

<Android.support.design.widget.CoordinatorLayout
    Android:layout_width="match_parent"
    Android:layout_height="?attr/actionBarSize">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:theme="@style/YATheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            Android:background="@color/colorPrimary"
            app:popupTheme="@style/YATheme.PopupOverlay"/>

    </Android.support.design.widget.AppBarLayout>

</Android.support.design.widget.CoordinatorLayout>

Maintenant, cette mise en page personnalisée est un enfant de chaque activité. Vous le traitez simplement comme tel dans la configuration de la liaison onCreate.

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    binding.yaCustomPrefs = YACustomPreference.getInstance(this)
    binding.toolbarMain?.yaCustomPrefs = YACustomPreference.getInstance(this)
    binding.navHeader?.yaCustomPrefs = YACustomPreference.getInstance(this)

    binding.activity = this
    binding.iBindingRecyclerView = this
    binding.navHeader?.activity = this

    //local pointer for notify txt badge
    txtNotificationCountBadge = txtNotificationCount

    //setup notify if returned from background so we can refresh the drawer items
    AppLifeCycleTracker.getInstance().addAppToForegroundListener(this)

    setupFilterableCategories()
    setupNavigationDrawer()
}

Remarquez que je définis le contenu des enfants en même temps que je fais le parent et que tout se fait par un accès en notation par points. Tant que les fichiers sont entourés de balises de présentation et que vous les avez nommés, la procédure est simple.

Désormais, si la classe personnalisée a sa propre inflation de code associée, elle peut facilement faire sa propre liaison dans onCreate ou son constructeur, mais vous obtenez une image complète. Si vous avez votre propre classe, lancez ce qui suit dans le constructeur pour correspondre à la classe de liaison nommée. Il suit la convention de nom du fichier de formatage Pascal mis en forme, ce qui facilite sa recherche et son remplissage automatique.

    LayoutInflater inflater = (LayoutInflater)
    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mBinding = NameOfCustomControlBinding.inflate(inflater);

J'espère que ça t'as aidé.

0
Sam