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.
you can access the application context from getApplication(). getApplicationContext() from within the ViewModel. This is what you need to access resources, preferences, etc.. The ViewModel class does not have the getApplication method.
It is used to return the Context which is linked to the Application which holds all activities running inside it. When we call a method or a constructor, we often have to pass a Context and often we use “this” to pass the activity Context or “getApplicationContext” to pass the application Context.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With