web-dev-qa-db-fra.com

Comment lier Android DataBinding à Menu?

Comme il prend en charge le menu Liaison de données dans Android? J'écris ce code, mais erreur: "Erreur: (16, 26) Aucun type de ressource spécifié (à 'visible' avec la valeur '@ {item.visible}')."

<menu xmlns:Android="http://schemas.Android.com/apk/res/Android"
      xmlns:app="http://schemas.Android.com/apk/res-auto">
<data>
   <variable
        name="item"
        type="ru.dixy.ubiworkerchecklistsmobile.Models.Fact"/>
    <import type="Android.view.View"/>
</data>
    <item
        Android:id="@+id/compliteitem"
        Android:title="mybutton"
        Android:icon="@drawable/complite"
        Android:visible="@{item.visible}"
        app:showAsAction="ifRoom"
         />
</menu>
8
Vadim Seleznev

"Pour le moment, la liaison de données concerne uniquement les ressources de structure, pas les ressources de menu"

Mais le comportement peut être obtenu avec Observable.OnPropertyChangedCallback . Vous devez d’abord définir OnPropertyChangedCallback:

private final Observable.OnPropertyChangedCallback propertyChangedCallback = new Observable.OnPropertyChangedCallback() {
    @Override
    public void onPropertyChanged(Observable observable, int i) {
        getActivity().invalidateOptionsMenu();
    }
};

En supposant que vous ayez une liaison pour Fact model dans votre fragment:

<variable
    name="item"
    type="ru.dixy.ubiworkerchecklistsmobile.Models.Fact"/>

Maintenant, vous devez inscrire propertyChangedCallback et le désenregistrer quand vous avez terminé:

@Override
public void onStart() {
    super.onStart();
    binding.getItem().addOnPropertyChangedCallback(propertyChangedCallback);
}

@Override
public void onStop() {
    super.onStop();
    binding.getItem().removeOnPropertyChangedCallback(propertyChangedCallback);
}

Nous sommes maintenant prêts à mettre à jour votre état d'affichage en fonction du modèle Fact :

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.menu_fact, menu);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    menu.findItem(R.id.compliteitem).setVisible(binding.getItem().isVisible());
}
17
Milan

Pour autant que je sache, pour le moment, la liaison de données concerne uniquement les ressources de présentation, pas les ressources de menu.

5
CommonsWare

Je me rends compte que c’est une vieille question, mais je voulais proposer une solution qui puisse aider les autres à résoudre le même problème. Ceci peut être réalisé en utilisant une vue d'action pour l'élément de menu. Cela nécessite pas mal de code, mais c'est une approche qui utilise MVVM et qui peut être utilisée pour toute liaison de données.

Ceci est un exemple où l'icône indique un nombre et change l'arrière-plan si le nombre est supérieur à 0.

Définir l'élément de menu

menu/main.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item
        Android:id="@+id/action_count"
        Android:enabled="true"
        Android:icon="@drawable/ic_menu_red_square"
        Android:title="@string/count"/>
</menu>

Définissez le modèle de vue pour l'élément de menu.

public class CountMenuViewModel extends BaseObservable {
    @Bindable
    int count;

    public CountMenuViewModel() {}

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
        if (this.count < 0) {
            this.count = 0;
        }
        notifyPropertyChanged(BR.count);
    }

    @Bindable({"count"})
    public @DrawableRes int getBackground() {
        if (count > 0) {
            return R.drawable.ic_menu_blue_square;
        }
        return R.drawable.ic_menu_red_square;
    }

    @Bindable({"count"})
    public String getCountText() {
        if (count > 0) {
            return String.valueOf(count);
        }
        return null;
    }
}

Définissez un rappel qui sera implémenté par l'activité lorsque l'utilisateur cliquera sur l'élément de menu.

public interface CountMenuActionCallback {
    void onCountMenuItemClicked();
}

Créez une mise en page pour la vue d'action. La mise en page utilise la classe de modèle de vue et définit le texte pour le nombre et l'arrière-plan. L'interface de rappel est utilisée pour OnClickListener pour la vue d'action.

layout/menu_action_count.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:tools="http://schemas.Android.com/tools">

    <data>
        <variable
            name="data"
            type="com.botnerd.samplesapp.CountMenuViewModel"
            />
        <variable
            name="callback"
            type="com.botnerd.samplesapp.CountMenuActionCallback"
            />
    </data>
    <FrameLayout
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:onClick="@{() -> callback.onCountMenuItemClicked()}"
        Android:background="?android:attr/actionBarItemBackground">

        <ImageView
            Android:layout_width="32dp"
            Android:layout_height="32dp"
            Android:layout_margin="4dp"
            Android:src="@{data.background}"
            tools:src="@drawable/ic_menu_red_square"
            />

        <TextView
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_gravity="center"
            Android:text="@{data.countText}"
            tools:text="30"
            Android:textSize="14dp"
            Android:maxLines="1"
            Android:textColor="@Android:color/white"
            tools:ignore="SpUsage"/>
    </FrameLayout>
</layout>

Notez qu'un adaptateur de liaison personnalisé est utilisé pour l'attribut Android:src. C'est un bon adaptateur pour configurer ImageView src par liaison de données.

@BindingAdapter({"Android:src"})
public static void setSrc(ImageView view, @DrawableRes int resId) {
    try {
        view.setImageDrawable(ContextCompat.getDrawable(view.getContext(), resId));
    } catch (Resources.NotFoundException e) {
    }
}

Enfin, gonflez le menu et liez la présentation de la vue d'action dans l'activité.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);

    MenuItem menuItemCount = menu.findItem(R.id.action_count);
    MenuActionCountBinding binding = MenuActionCountBinding.inflate(getLayoutInflater());
    binding.setData(mCountMenuViewModel);
    binding.setCallback(mCountMenuActionCallback);

    MenuItemCompat.setActionView(menuItemCount, binding.getRoot());
    MenuItemCompat.setShowAsAction(menuItemCount, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);

    return super.onCreateOptionsMenu(menu);
}

Pour être complet, voici tous les fichiers de l'exemple qui ne sont pas définis ci-dessus.

drawable/ic_menu_blue_square.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <padding Android:bottom="4dp"
             Android:left="4dp"
             Android:right="4dp"
             Android:top="4dp"/>
    <solid Android:color="#000080"/>
    <corners Android:radius="2dp"/>

</shape>

drawable/ic_menu_red_square.xml

<shape xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <padding Android:bottom="4dp"
             Android:left="4dp"
             Android:right="4dp"
             Android:top="4dp"/>
    <solid Android:color="#800000"/>
    <corners Android:radius="2dp"/>

</shape>

layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools">

    <data>
        <variable
            name="callback"
            type="com.botnerd.samplesapp.MainActivityActionCallback"
            />
    </data>

    <Android.support.constraint.ConstraintLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        tools:context="com.botnerd.samplesapp.MainActivity">

        <Button
            Android:id="@+id/button"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="-"
            Android:onClick="@{() -> callback.onMinusClicked()}"
            Android:layout_marginStart="79dp"
            app:layout_constraintBaseline_toBaselineOf="@+id/button2"
            tools:layout_constraintBaseline_creator="1"
            tools:layout_constraintLeft_creator="1"
            app:layout_constraintLeft_toLeftOf="parent"/>

        <Button
            Android:id="@+id/button2"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="+"
            Android:onClick="@{() -> callback.onPlusClicked()}"
            tools:layout_constraintTop_creator="1"
            Android:layout_marginStart="25dp"
            Android:layout_marginTop="97dp"
            tools:layout_constraintLeft_creator="1"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/button"/>
    </Android.support.constraint.ConstraintLayout>
</layout>

MainActivityActionCallback.Java

public interface MainActivityActionCallback {
    void onPlusClicked();
    void onMinusClicked();
}

MainActivity.Java

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding mBinding;
    CountMenuViewModel mCountMenuViewModel;

    CountMenuActionCallback mCountMenuActionCallback = new CountMenuActionCallback() {
        @Override
        public void onCountMenuItemClicked() {
            Toast.makeText(MainActivity.this, "Count clicked!", Toast.LENGTH_SHORT)
                    .show();
        }
    };

    MainActivityActionCallback mActionCallback = new MainActivityActionCallback() {
        @Override
        public void onPlusClicked() {
            mCountMenuViewModel.setCount(mCountMenuViewModel.getCount() + 1);
        }

        @Override
        public void onMinusClicked() {
            mCountMenuViewModel.setCount(mCountMenuViewModel.getCount() - 1);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCountMenuViewModel = new CountMenuViewModel();

        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        mBinding.setCallback(mActionCallback);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);

        MenuItem menuItemCount = menu.findItem(R.id.action_count);
        MenuActionCountBinding binding = MenuActionCountBinding.inflate(getLayoutInflater());
        binding.setData(mCountMenuViewModel);
        binding.setCallback(mCountMenuActionCallback);

        MenuItemCompat.setActionView(menuItemCount, binding.getRoot());
        MenuItemCompat.setShowAsAction(menuItemCount, MenuItemCompat.SHOW_AS_ACTION_ALWAYS);

        return super.onCreateOptionsMenu(menu);
    }


}
2
botnerd