Lifecycle+liveData+DataBinding三部曲

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lifecycle+liveData+DataBinding三部曲相关的知识,希望对你有一定的参考价值。

参考技术A 对Lifecycle liveData的理解:
System已经做了:Fragment/Actvity已经实现了LifecycleOwner 的接口:
Userdo:1-->viewMode(或者IPresenter )implements LifecycleObserver
2-->只需在Fragment/Activity中调用:getLifecycle().addObserver(viewModel);
3-->viewModel.dataList.observe(owner, Observer);
1--2---原理:
Fragment/Activity的构造方法中已经调用了下列方法:

getLifecycle()返回的对象mLifecycleRegistry,在LifecycleOwner( Activity/Fragment)的生命周期方法中都做了监听,这就方便了我们Observer(viewMode或者Presenter)对LifecycleOwner生命周期的感知
--3--LiveData原理:
viewModel.dataList.observe(owner, Observer)------> LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);它实现了GenericLifecycleObserver,而GenericLifecycleObserver继承了LifecycleObserver接口。当组件(Fragment/Activity)生命周期变化时会通过onStateChanged()方法回调过来。Observer接口就是观察者,其中定义了LiveData数据变化的回调方法onChanged()。
因此我们就可以使用三部曲搞定 data对ui的生命周期的监控

Databinding就更简单了,直接跟着撸码就ok了,Fragment、Activity通过DataBindingUtil.inflate()就可以联系起来,而且可以双向绑定哦。
Lifecycle+liveData+DataBinding才是最爽的编码方式。

具体原理:[ https://blog.csdn.net/zhuzp_blog/article/details/78871527]

找不到接受参数类型 'androidx.lifecycle.LiveData< 的 ~ItemBinding~ 的设置器

【中文标题】找不到接受参数类型 \'androidx.lifecycle.LiveData< 的 ~ItemBinding~ 的设置器【英文标题】:Cannot find a setter for ~ItemBinding~ that accepts parameter type 'androidx.lifecycle.LiveData<找不到接受参数类型 'androidx.lifecycle.LiveData< 的 ~ItemBinding~ 的设置器 【发布时间】:2020-12-11 04:04:37 【问题描述】:

找不到二传手 那 接受参数类型 'androidx.lifecycle.LiveData' 如果绑定适配器提供了设置器,请检查适配器是否 注释正确且参数类型匹配。

两天多来,我一直在逐行浏览我的应用程序。我使用 Google 提供的“BasicSample”Android Room 应用程序来模拟我自己的应用程序,但是当我将它包含在我的 dog_fragment.xml 中时出现此错误

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

“dog_item”布局(dog_item.xml)用于显示狗的列表,当您单击它时,它将带您进入狗详细信息屏幕(dog_fragment.xml)。没有它,一切都很好,但它缺少在详细信息屏幕中播放的“狗”图块,并且只会显示一个咀嚼玩具列表。

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_
    android:layout_
    android:background="@color/cardview_light_background"
    android:orientation="vertical">
    
    <include
      layout="@layout/dog_item"
      app:product="@dogViewModel.dog" />
    
    <FrameLayout
      android:layout_
      android:layout_>
      
      <TextView
        android:id="@+id/tv_loading_chew_toys"
        android:layout_
        android:layout_
        android:text="@string/loading_chew_toys"
        app:visibleGone="@isLoading" />
      
      <FrameLayout
        android:id="@+id/chew_toys_list_wrapper"
        android:layout_
        android:layout_>
        
        <androidx.recyclerview.widget.RecyclerView
          android:id="@+id/chew_toy_list"
          android:layout_
          android:layout_
          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);

道查询

@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 也有 Dog 模型类,如果有帮助的话) 我试过无效缓存/重启,我试过清理项目,重建项目。我已经开始了一个新项目并将我的文件复制到其中。 哦,还有,这是一个错误补充:

import the_derek.dogstuff.databinding.DogFragmentBindingImpl;

它告诉我它无法解析 DogFragmentBindingImpl 我不知道它是如何产生的,但我认为这些问题是相互交织的。我不知道我是否错过了任何可以提供帮助的代码,请告诉我。

(仿照) android architecture-components-samples

【问题讨论】:

【参考方案1】:

这是binding adapter 错误。如果您在 XML 中编写“app:product”,则在您的 kotlin 或 java 类之一中必须有一个名为“product”的绑定适配器。例如,对于您的

app:product="@dogViewModel.dog"

应该是这样的:

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

阅读有关数据绑定和绑定适配器的更多信息。

【讨论】:

当我试图将变量发送到另一个布局(在“包含”中)时,“app:”之后的值需要是变量在“”部分中的名称xml.【参考方案2】:

我花了数周或一个月的时间认为“app:product”是 XML 的某种标准短语或约定。我认为“产品”是一个通用术语,如“重力”或“布局”......如果你明白我在说什么。因为当我发布我是 Android 新手的问题时,我从未想过“app:”后面的术语需要根据数据中的变量进行更改。

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

【讨论】:

这个解决方案的意义何在?我在使用 &lt;include ...&gt; 布局时遇到了类似的问题,我不知道您的解决方案有什么帮助。【参考方案3】:

您在这里传递了一个 LiveData 对象:

app:product="@dogViewModel.dog"

你需要传入:

app:product="@dogViewModel.dog.value"

【讨论】:

假设加载了狗实体,下面有所有的细节/咀嚼玩具。此外,如果我将其更改为“@dogViewModel.dog.value”(例如 - 字符串),它会告诉我:“找不到接受参数类型 'java.lang 的 ... DogItemBinding app:product> 的设置器.String'" 试试“@dogViewModel.getDog().value” 只是 .getDog() 或 .getDog() 值产生相同的错误:-( 刚刚在 Google 的 Basic Sample App 上发现,如果我更改了他们的字段。 <include layout="@layout/product_item" app:product="@productViewModel.product" /> 到 &lt;include layout="@layout/product_item" app:product="@productViewModel.product.id" /&gt; 它会产生与我得到的完全相同的错误消息

以上是关于Lifecycle+liveData+DataBinding三部曲的主要内容,如果未能解决你的问题,请参考以下文章

Lifecycle+liveData+DataBinding三部曲

Jetpack笔记

Android Jetpack组件 - ViewModel,LiveData使用以及原理

Android LiveData简介

ViewModel与LiveData的简单使用

ViewModel与LiveData的简单使用