Consider a layout data-bound to a viewModel. More specifically, the layout uses a layout variable for accessing this underlying viewModel. Whenever the binding is inflated, its viewModel and lifeCycleOwner are to be set. (Of course the viewModel contains some liveData, directly bound to some view properties).
A RecyclerView (created & set in an Activity) is passed a list of viewModels. For each viewModel, a ViewHolder is made via inflating a new copy of layout and its dataBinding.
The onBindViewHolder
strategy is to
- not touch the viewModels
- set the
ViewHolder.dataBinding.setViewModel(viewModels[position])
- but how to set the LifeCycleOwner?
- Is passing a LifeCycleOwner as an argument to the Adapter okay? Afterall, the adapter would only live as long as the RecyclerView which inturn will only be alive as long as the parent Activity.
- Is this a sensible way of using dataBinding in the context of RecyclerView?
fig1. layout_counter.xml :The layout of one single component that gets added to recyclerView.
Codes(if needed)
- MainViewModel.java
- Adapter.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;
}
}
Adapter.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>