I based my code on an example I found that uses Android Architecture Components and data binding. This is a new way for me, and the way it is coded makes it hard to properly open a new activity with the information of the post that was clicked.
This is the adapter of the posts
class PostListAdapter : RecyclerView.Adapter<PostListAdapter.ViewHolder>() {
private lateinit var posts: List<Post>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostListAdapter.ViewHolder {
val binding: ItemPostBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_post,
parent, false
)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: PostListAdapter.ViewHolder, position: Int) {
holder.bind(posts[position])
}
override fun getItemCount(): Int {
return if (::posts.isInitialized) posts.size else 0
}
fun updatePostList(posts: List<Post>) {
this.posts = posts
notifyDataSetChanged()
}
inner class ViewHolder(private val binding: ItemPostBinding) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = PostViewModel()
fun bind(post: Post) {
viewModel.bind(post)
binding.viewModel = viewModel
}
}
}
The bind
method comes from within the view model class:
class PostViewModel : BaseViewModel() {
private val image = MutableLiveData<String>()
private val title = MutableLiveData<String>()
private val body = MutableLiveData<String>()
fun bind(post: Post) {
image.value = post.image
title.value = post.title
body.value = post.body
}
fun getImage(): MutableLiveData<String> {
return image
}
fun getTitle(): MutableLiveData<String> {
return title
}
fun getBody(): MutableLiveData<String> {
return body
}
fun onClickPost() {
// Initialize new activity from here, perhaps?
}
}
And in the layout XML, setting on an onClick
attribute
android:onClick="@{() -> viewModel.onClickPost()}"
pointing to this onClickPost
method does work but I can't initialize the Intent
from there. I tried many ways to acquire the MainActivitiy
's context, without success, such as
val intent = Intent(MainActivity::getApplicationContext, PostDetailActivity::class.java)
But it displays an error on time.
Try: android:onClick="@{(view) -> viewModel.onClickPost(view)}"
Also change onClickPost
to take in a View. Then you can use the view.getContext()
method on the view to get access to the Context stored in that view.
However, since ViewModels shouldn't reference a view or any other class that holds an Activity's context, it's quite inappropriate to place your logic for starting an Activity in the ViewModel. You should definitely consider a separate place to do so.
Personally, for my code, if it's a simple startActivity without any extra baggage, I create a separate class that holds a static method. Through databinding, I'll import that class and use it in the onClick to start a new Activity using the method I said above.
An example of this:
public class ActivityHandler{
public static void showNextActivity(View view, ViewModel viewModel){
Intent intent = new Intent(); //Create your intent and add extras if needed
view.getContext().startActivity(intent);
}
}
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="whatever.you.want.ActivityHandler" />
<variable name="viewmodel" type="whatever.you.want.here.too.ViewModel" />
</data>
<Button
//Regular layout properties
android:onClick="@{(view) -> ActivityHandler.showNextActivity(view, viewmodel)}"
/>
</layout>
Look at Listener Bindings here: https://developer.android.com/topic/libraries/data-binding/expressions#listener_bindings
However, depending on the amount of data necessary, you might want to place your startActivity code in other classes that best fits your app's design.