13

考虑绑定到 viewModel 的布局数据。更具体地说,布局使用布局变量来访问这个底层视图模型。每当绑定膨胀时,它的 viewModel 和 lifeCycleOwner 都将被设置。(当然 viewModel 包含一些 liveData,直接绑定到一些视图属性)。

向 RecyclerView(在 Activity 中创建和设置)传递一个 viewModel 列表。对于每个视图模型,一个 ViewHolder 是通过扩展一个新的布局副本及其数据绑定来创建的。

onBindViewHolder策略是

  • 不要触摸视图模型
  • 设置ViewHolder.dataBinding.setViewModel(viewModels[position])
  1. 但是如何设置 LifeCycleOwner?
  2. 将 LifeCycleOwner 作为参数传递给适配器好吗?毕竟,适配器只会与 RecyclerView 一样长,而 RecyclerView 则只会与父 Activity 一样长。
  3. 这是在 RecyclerView 的上下文中使用 dataBinding 的明智方式吗?

图。1。layout_counter.xml :添加到 recyclerView 的单个组件的布局。

布局

代码(如果需要)

  • MainViewModel.java
  • 适配器.java
  • MainActivity.java
  • layout_counter.xml
  • layout_main.xml

MainViewModel.java

package com.gmail.example.rough;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

public class MainViewModel extends androidx.lifecycle.ViewModel {

    private MutableLiveData<String> date, time, name;

    public MainViewModel() {
        this.date = new MutableLiveData<>(LocalDateTime.now().toString());
        this.time = new MutableLiveData<>(LocalTime.now().toString());
        this.name = new MutableLiveData<>(UUID.randomUUID().toString().substring(0,10));
        Timer t=new Timer();
        t.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                date.postValue(LocalDateTime.now().toString());
                time.postValue(LocalTime.now().toString());
                }
        }, 0, 1000);
    }

    public LiveData<String> getDate() {
        return date;
    }

    public LiveData<String> getTime() {
        return time;
    }

    public LiveData<String> getName() {
        return name;
    }
}

适配器.java

package com.gmail.example.rough;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.gmail.example.rough.databinding.LayoutCounterBinding;
import java.util.List;

public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {

    public static class ViewHolder extends RecyclerView.ViewHolder {

        private LayoutCounterBinding binding;

        public ViewHolder(@NonNull View itemView, LayoutCounterBinding binding) {
            super(itemView);
            this.binding = binding;
        }

        public LayoutCounterBinding getBinding() {
            return binding;
        }
    }

    List<MainViewModel> vms;

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {


        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.layout_counter, parent, false);
        LayoutCounterBinding binding = DataBindingUtil.inflate(inflater, R.layout.layout_counter, parent, false);
        return new ViewHolder(view, binding);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.getBinding().setVm(vms.get(position));
        //how to set the LifeCycleOwner?
//        holder.getBinding().setLifecycleOwner(???);
    }

    @Override
    public int getItemCount() {
        return vms.size();
    }

   

    public Adapter(List<MainViewModel> vms) {
        this.vms = vms;
    }
}

MainActivity.java

package com.gmail.example.rough;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;

import android.os.Bundle;

import com.gmail.example.rough.databinding.LayoutMainBinding;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_main);
        LayoutMainBinding binding = LayoutMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        ArrayList<MainViewModel> vms = new ArrayList<>();
        vms.add(new MainViewModel());
        vms.add(new MainViewModel());
        vms.add(new MainViewModel());
        binding.recyclerView.setAdapter(new Adapter(vms));
        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));


    }


}

layout_counter.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="vm"
                type="com.gmail.example.rough.MainViewModel" />



    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".logger.android.MainActivity">

        <TextView
                android:id="@+id/tvName"
                android:layout_width="wrap_content"
                android:layout_height="19dp"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                android:text="@{vm.name}"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        <RelativeLayout
                android:id="@+id/layoutRealtive"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="8dp"
                android:layout_marginTop="8dp"
                android:layout_marginEnd="8dp"
                android:gravity="center"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tvName">

            <TextView
                    android:id="@+id/tvTs"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignStart="@id/tvTime"
                    android:layout_alignTop="@id/tvTime"
                    android:text="@{vm.date}" />

            <TextView
                    android:id="@+id/tvTime"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"

                    android:background="?selectableItemBackground"
                    android:gravity="center"
                    android:textAlignment="center"
                    android:textSize="36sp"
                    android:textStyle="bold"

                    />


            <ImageView
                    android:id="@+id/ivReset"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignTop="@id/tvTime"
                    android:layout_alignBottom="@id/tvTime"
                    android:layout_alignParentEnd="true"

                    android:src="@drawable/ic_launcher_foreground"
                     />

        </RelativeLayout>


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

layout_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>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="409dp"
                android:layout_height="729dp"
                android:layout_marginStart="1dp"
                android:layout_marginTop="1dp"
                android:layout_marginEnd="1dp"
                android:layout_marginBottom="1dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

4

4 回答 4

4

您可以参考以下示例代码:

class SampleAdapter(private var list: List<String>,
                private val viewmodel: SampleViewModel,
                private val lifecycleOwner: LifecycleOwner) : RecyclerView.Adapter<SampleAdapter.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val binding: ItemRowBinding = DataBindingUtil.inflate(
            LayoutInflater.from(parent.context),
            R.layout.item_row, parent, false)
    val holder= ViewHolder(binding, lifecycleOwner)
    binding.lifecycleOwner=holder
    holder.lifecycleCreate()
    return holder
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.bind()
}

override fun getItemCount(): Int {
    return list.size
}

override fun onViewAttachedToWindow(holder: ViewHolder) {
    super.onViewAttachedToWindow(holder)
    holder.attachToWindow()
}

inner class ViewHolder(private val binding: ItemRowBinding,
                       private var lifecycleOwner: LifecycleOwner)
    : RecyclerView.ViewHolder(binding.root),LifecycleOwner {
    private val lifecycleRegistry = LifecycleRegistry(this)
    private var paused: Boolean = false

    init {
        lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
    }
    fun lifecycleCreate() {
        lifecycleRegistry.currentState = Lifecycle.State.CREATED
    }
    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }

    fun bind() {
        lifecycleOwner = this@SampleAdapter.lifecycleOwner
        binding.viewmodel = viewmodel
        binding.executePendingBindings()
    }

    fun attachToWindow() {
        if (paused) {
            lifecycleRegistry.currentState = Lifecycle.State.RESUMED
            paused = false
        } else {
            lifecycleRegistry.currentState = Lifecycle.State.STARTED
        }
    }
}

fun setList(list: List<String>) {
    this.list = list
    notifyDataSetChanged()
} }

我们需要传递生命周期所有者的原因是:ViewHolder 不是生命周期所有者,这就是它无法观察 LiveData 的原因。整个想法是通过实现 LifecycleOwner 使 Viewholder 成为生命周期所有者,然后开始其生命周期。

于 2020-09-29T05:32:30.507 回答
3

如果我从此页面正确理解,最好不要将 传递lifeCycleOwnerRecyclerView.Adapter绑定项,因为:

当 ViewHolder 被分离时,意味着它当前在屏幕上不可见,parentLifecycleOwner 仍处于恢复状态,因此 ViewDataBindings 仍处于活动状态并观察数据。这意味着当 LiveData 实例被更新时,它会触发 View 被更新,但 View 当前没有被显示!不理想。

Stephen Brewer 似乎提出了一些解决方案,但我没有测试它们

于 2020-10-04T20:01:46.337 回答
0

而不是将 LifeCycleOwner 传递给 RecyclerView。尝试observeForever(Observer)在从 RecyclerView 观察数据的情况下使用。并在工作完成后使用removeObserver(Observer).

参考文档: https://developer.android.com/reference/androidx/lifecycle/LiveData#observeForever(androidx.lifecycle.Observer%3C?%20super%20T%3E)

于 2021-04-30T06:03:29.753 回答
0

您可以将生命周期所有者传递给 onCreateViewHolder 方法中的绑定。

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    ...
    binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
    return ViewHolder(binding)
}
于 2021-05-14T19:24:45.540 回答