Cannot find a setter for ~ItemBinding~ that accepts parameter type 'androidx.lifecycle.LiveData<

Viewed 4499

Cannot find a setter for <the_derek.dogstuff.databinding.DogItemBinding app:product> that accepts parameter type 'androidx.lifecycle.LiveData<the_derek.dogstuff.db.entity.DogEntity>' If a binding adapter provides the setter, check that the adapter is annotated correctly and that the parameter type matches.

I’ve been going line-by-line through my app for over two days now. I used the Google provided “BasicSample” Android Room app to mock up my own app but am getting this error when I include this in my dog_fragment.xml

<include
 layout="@layout/dog_item"
 app:product="@{dogViewModel.dog}" />

The “dog_item” layout (dog_item.xml) is for showing a list of dogs, when you then click on it, it will bring you to a dog details screen (dog_fragment.xml). Without it, everything works great but it’s missing the “dog” tile to play into the details screen, and will only show a list of chew_toys.

dog_fragment.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">
  
  <data>
    <import type="android.view.View" />
    
    <variable
      name="isLoading"
      type="boolean" />
    
    <variable
      name="dog"
      type="the_derek.dogstuff.viewmodel.DogViewModel" />
  </data>
  
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/cardview_light_background"
    android:orientation="vertical">
    
    <include
      layout="@layout/dog_item"
      app:product="@{dogViewModel.dog}" />
    
    <FrameLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent">
      
      <TextView
        android:id="@+id/tv_loading_chew_toys"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/loading_chew_toys"
        app:visibleGone="@{isLoading}" />
      
      <FrameLayout
        android:id="@+id/chew_toys_list_wrapper"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/chew_toy_list"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:contentDescription="@string/cd_chew_toys_list"
          app:layoutManager="LinearLayoutManager"
          app:visibleGone="@{!isLoading}" />
      </FrameLayout>
    </FrameLayout>
  </LinearLayout>
</layout>

DogFragment.java

public class DogFragment extends Fragment {

 private static final String TAG = "\t\tDogFragment";
 private static final String KEY_DOG_ID = "dog_id";

 private final ChewToyClickCallback mChewToyClickCallback =
   chewToy -> {
    // no-op
   };
 private DogFragmentBinding mBinding;
 private ChewToyAdapter mChewToyAdapter;

 public static DogFragment forDog(int dogId) {
  DogFragment fragment = new DogFragment();
  Bundle args = new Bundle();
  args.putInt(KEY_DOG_ID, dogId);
  fragment.setArguments(args);
  return fragment;
 }

 @Nullable
 @Override
 public View onCreateView(
   @NonNull LayoutInflater inflater,
   @Nullable ViewGroup container,
   @Nullable Bundle savedInstanceState) {
  mBinding = DataBindingUtil.inflate(inflater, R.layout.dog_fragment, container, false);
 
  mChewToyAdapter = new ChewToyAdapter(mChewToyClickCallback);
  mBinding.chewToyList.setAdapter(mChewToyAdapter);
  return mBinding.getRoot();
 }

 @Override
 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
  DogViewModel.Factory factory =
    new DogViewModel.Factory(
      requireActivity().getApplication(), requireArguments().getInt(KEY_DOG_ID));
  final DogViewModel model =
    new ViewModelProvider(this, factory).get(DogViewModel.class);

  mBinding.setLifecycleOwner(getViewLifecycleOwner());
  mBinding.setDogViewModel(model);

  subscribeToModel(model);
 }

 private void subscribeToModel(final DogViewModel model) {
  model
    .getChewToys()
    .observe(
      getViewLifecycleOwner(),
      chewToyEntities -> {
       if (chewToyEntities != null) {
        mBinding.setIsLoading(false);
        mChewToyAdapter.submitList(chewToyEntities);
       } else {
        mBinding.setIsLoading(true);
       }
      });
 }

 @Override
 public void onDestroyView() {
  mBinding = null;
  mChewToyAdapter = null;
  super.onDestroyView();
 }
}

DogViewModel.java

public class DogViewModel extends AndroidViewModel {

  private static final String TAG = "\t\tDogViewModel";
  private final LiveData<DogEntity> mObservableDog;
  private final LiveData<List<ChewToyEntity>> mObservableChewToys;

  public DogViewModel(
      @NonNull Application application, DataRepository repository, final int dogId) {
    super(application);
    mObservableChewToys = repository.loadChewToysById(dogId);
    mObservableDog = repository.loadDog(dogId);
  }

  public LiveData<List<ChewToyEntity>> getChewToys() {
    return mObservableChewToys;
  }

  public LiveData<DogEntity> getDog() {
    return mObservableDog;
  }

  public static class Factory extends ViewModelProvider.NewInstanceFactory {

    @NonNull private final Application mApplication;

    private final int mDogId;

    private final DataRepository mRepository;

    public Factory(@NonNull Application application, int dogId) {
      mApplication = application;
      mDogId = dogId;
      mRepository = ((DogApp) application).getRepository();
    }

    @SuppressWarnings("unchecked")
    @Override
    @NonNull
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
      return (T) new DogViewModel(mApplication, mRepository, mDogId);
    }
  }
}

BindingAdapters.java

public class BindingAdapters {
  @BindingAdapter("visibleGone")
  public static void showHide(View view, boolean show) {
    view.setVisibility(show ? View.VISIBLE : View.GONE);
  }
}

DogClickCallback.java

public interface DogClickCallback {
 void onClick(Dog dog);
}

dao query

@Query("select * from dog_table where id = :dogId")
LiveData<DogEntity> loadDog(int dogId);

DogAdapter.java

public class DogAdapter extends RecyclerView.Adapter<DogAdapter.DogViewHolder> {
 private static final String TAG = "\t\tDogAdapter";

 @Nullable private final DogClickCallback mDogClickCallback;
 List<? extends Dog> mDogList;

 public DogAdapter(@Nullable DogClickCallback clickCallback) {
   Log.i(TAG, "DogAdapter: public constructor");
   mDogClickCallback = clickCallback;
   setHasStableIds(true);
 }

 public void setDogList(final List<? extends Dog> dogList) {
   if (mDogList == null) {
    mDogList = dogList;
    notifyItemRangeInserted(0, dogList.size());
   } else {
    DiffUtil.DiffResult result =
       DiffUtil.calculateDiff(
          new DiffUtil.Callback() {
            @Override
            public int getOldListSize() {
             return mDogList.size();
            }
            @Override
            public int getNewListSize() {
             return dogList.size();
            }
            @Override
            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
             return mDogList.get(oldItemPosition).getId()
                == dogList.get(newItemPosition).getId();
            }
            @Override
            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
             Dog newDog = dogList.get(newItemPosition);
             Dog oldDog = mDogList.get(oldItemPosition);
             return newDog.getId() == oldDog.getId()
                && TextUtils.equals(newDog.getName(), oldDog.getName());
            }
          });
    mDogList = dogList;
    result.dispatchUpdatesTo(this);
   }
 }

 @Override
 @NonNull
 public DogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
   DogItemBinding binding =
      DataBindingUtil.inflate(
         LayoutInflater.from(parent.getContext()), R.layout.dog_item, parent, false);
   binding.setCallback(mDogClickCallback);
   return new DogViewHolder(binding);
 }

 @Override
 public void onBindViewHolder(@NonNull DogViewHolder holder, int position) {
   holder.binding.setDog(mDogList.get(position));
   holder.binding.executePendingBindings();
 }

 @Override
 public int getItemCount() {
   return mDogList == null ? 0 : mDogList.size();
 }

 @Override
 public long getItemId(int position) {
   return mDogList.get(position).getId();
 }

 static class DogViewHolder extends RecyclerView.ViewHolder {
   final DogItemBinding binding;
   public DogViewHolder(DogItemBinding binding) {
    super(binding.getRoot());
    this.binding = binding;
   }
 }
}


(DogEntity also has Dog model class, if that helps) I've tried Invalidate Caches/Restart, I've tried Clean Project, Rebuild Project. I've started a new project and copied my files into it. Ooh, also, this is an error in addition:

import the_derek.dogstuff.databinding.DogFragmentBindingImpl;

It tells me it cannot resolve DogFragmentBindingImpl I don't know how it's not getting generated but I assume the problems are intertwined. I don't know if I missed putting any code that could help, please let me know.

(modeled after) android architecture-components-samples

4 Answers

the derek Fawcett's solution is telling you that the parameter name inside the include-tag has to match the variable name used inside the included layout.

<include
     layout="@layout/dog_item"
     app:dog="@{dogViewModel.dog}" />

dog_item

<data>
    
    <variable
      name="dog"
      type="$path.DogClass" />

</data>
    

There are no general parameter names to match variables when it comes to bindings, you can define the names yourself and then set them accordingly.

This is binding adapter error. If you write 'app:product' in your XML, there has to be binding adapter inside one of your kotlin or java classes with the name of 'product'. For example, for your

app:product="@{dogViewModel.dog}"

there needs to be something like this:

@BindingAdapter("product")
fun yourFunctionName(yourViewType: YourViewType, data: List<DogEntity>?) {
    // your binding code here
}

Read more about data binding and binding adapters.

I spent weeks or a month thinking that "app:product" was some sort of standard phrase or convention for XML. I thought "product" was a generic term used like "gravity" or "layout"..... if you get what I'm saying. Because when I posted the question I was new to Android, I never thought that the term after "app:" needed to be changed depending on the variable in data.

<include
     layout="@layout/dog_item"
     app:dog="@{dogViewModel.dog}" />

You're passing in a LiveData object here:

app:product="@{dogViewModel.dog}"

You need to pass in:

app:product="@{dogViewModel.dog.value}"
Related