Androidアーキテクチャコンポーネントとデータバインディングを使用していることがわかった例に基づいてコードを作成しました。これは私にとって新しい方法であり、コード化されているため、新しいアクティビティを適切に開くことが困難ですクリックされた投稿の情報。
これはポストのアダプターです
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
}
}
}
bind
メソッドは、ビューモデルクラス内から取得されます。
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?
}
}
レイアウトXMLでは、onClick
属性を設定します
Android:onClick = "@ {()-> viewModel.onClickPost()}"
このonClickPost
メソッドを指すことは機能しますが、そこからIntent
を初期化できません。 MainActivitiy
のコンテキストを取得するために、多くの方法を試しましたが、成功しませんでした。
val intent = Intent(MainActivity :: getApplicationContext、PostDetailActivity :: class.Java)
ただし、時間どおりにエラーが表示されます。
SingleLiveEvent を使用してみてください
これが Googles architecture samples repo からのコードです(レポから削除された場合に備えて):
import Android.Arch.lifecycle.LifecycleOwner;
import Android.Arch.lifecycle.MutableLiveData;
import Android.Arch.lifecycle.Observer;
import Android.support.annotation.MainThread;
import Android.support.annotation.Nullable;
import Android.util.Log;
import Java.util.concurrent.atomic.AtomicBoolean;
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
public class SingleLiveEvent<T> extends MutableLiveData<T> {
private static final String TAG = "SingleLiveEvent";
private final AtomicBoolean mPending = new AtomicBoolean(false);
@MainThread
public void observe(LifecycleOwner owner, final Observer<T> observer) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, new Observer<T>() {
@Override
public void onChanged(@Nullable T t) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
}
});
}
@MainThread
public void setValue(@Nullable T t) {
mPending.set(true);
super.setValue(t);
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
試してください:Android:onClick="@{(view) -> viewModel.onClickPost(view)}"
また、ビューを取り込むにはonClickPost
を変更します。次に、ビューでview.getContext()
メソッドを使用して、そのビューに格納されているコンテキストにアクセスできます。
ただし、ViewModelはビューまたはアクティビティのコンテキストを保持する他のクラスを参照してはならないため、アクティビティを開始するためのロジックをViewModelに配置することは非常に不適切です。そのためには、別の場所を検討する必要があります。
個人的に、私のコードでは、余分な手荷物がない単純なstartActivityの場合、静的メソッドを保持する別のクラスを作成します。データバインディングを通じて、そのクラスをインポートし、onClickで使用して、上記で述べた方法を使用して新しいアクティビティを開始します。
この例:
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>
ここでリスナーのバインディングを見てください: https://developer.Android.com/topic/libraries/data-binding/expressions#listener_bindings
ただし、必要なデータの量によっては、startActivityコードをアプリの設計に最適な他のクラスに配置することもできます。
アクティビティインスタンスをモデルまたはレイアウトに渡すこともできますが、私はそれを好みません。
好ましい方法は、行レイアウトにインターフェースを渡すことです。
レイアウトデータで変数を宣言する
<variable
name="onClickListener"
type="Android.view.View.OnClickListener"/>
クリックするとこれを呼び出す
<LinearLayout
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:onClick="@{onClickListener::onClick}"
>
また、このリスナーをアダプターから設定します
binding.viewModel = viewModel
binding.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
context.startActivity(new Intent(context, MainActivity.class));
}
});