点击Kotlin RecyclerView(卡片视图)加载详细信息视图



我有一个片段,它有一个RecyclerView,项目在CardViews中。我有一个适配器,它将用来自newsapi.org的数据填充RecyclerView。我需要实现的是当我点击一个项目(CardView(来加载一个带有图像、标题和描述的活动时。我是科特林的新手,发现自己被困在这里,需要帮助跟进。真的很有帮助。我会附上我的适配器和碎片(它有RecyclerView(。

困扰我的是,我是否应该在onBindViewHolder->中启动活动。。。。cardView.setOnClickListener还是其他?令人困惑的部分是将图像(来自url(设置为将值传递给细节视图。

适配器类

class ArticleAdapter(
private var articleList: ArrayList<Article>
) : RecyclerView.Adapter<ArticleViewHolder>() {
private val placeHolderImage = "https://picsum.photos/200/200/?blur"
private lateinit var viewGroupContext: Context
override fun onCreateViewHolder(viewGroup: ViewGroup, p1: Int): ArticleViewHolder {
viewGroupContext = viewGroup.context
val itemView: View =
LayoutInflater.from(viewGroup.context).inflate(R.layout.article_item, viewGroup, false)
return ArticleViewHolder(itemView)
}
override fun getItemCount(): Int {
return articleList.size
}
override fun onBindViewHolder(articleViewHolder: ArticleViewHolder, itemIndex: Int) {
val article: Article = articleList.get(itemIndex)
setPropertiesForArticleViewHolder(articleViewHolder, article)
articleViewHolder.cardView.setOnClickListener {
//do something
}
}
private fun setPropertiesForArticleViewHolder(
articleViewHolder: ArticleViewHolder,
article: Article
) {
checkForUrlToImage(article, articleViewHolder)
articleViewHolder.title.text = article?.title
articleViewHolder.description.text = article?.description
articleViewHolder.url.text = article?.url
}
private fun checkForUrlToImage(article: Article, articleViewHolder: ArticleViewHolder) {
if (article.urlToImage == null || article.urlToImage.isEmpty()) {
Picasso.get()
.load(placeHolderImage)
.centerCrop()
.fit()
.into(articleViewHolder.urlToImage)
} else {
Picasso.get()
.load(article.urlToImage)
.centerCrop()
.fit()
.into(articleViewHolder.urlToImage)
}
}
fun setArticles(articles: ArrayList<Article>) {
articleList = articles
notifyDataSetChanged()
}
}
//interface ItemClickListener{
//    fun onItemClick(articleList: Article, position:Int)
//}

碎片

class HomeFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
//    private lateinit var homeViewModel: HomeViewModel
private val ENDPOINT_URL by lazy { "https://newsapi.org/v2/" }
private lateinit var topHeadlinesEndpoint: TopHeadlinesEndpoint
private lateinit var newsApiConfig: String
private lateinit var articleAdapter: ArticleAdapter
private lateinit var articleList: ArrayList<Article>
private lateinit var userKeyWordInput: String
// RxJava related fields
private lateinit var topHeadlinesObservable: Observable<TopHeadlines>
private lateinit var compositeDisposable: CompositeDisposable
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
swipe_refresh.setOnRefreshListener {
queryTopHeadlines()
//            refreshAction()  //refresh the list
swipe_refresh.isRefreshing = false
}
//Network request
val retrofit: Retrofit = generateRetrofitBuilder()
topHeadlinesEndpoint = retrofit.create(TopHeadlinesEndpoint::class.java)
newsApiConfig = resources.getString(R.string.api_key)
swipe_refresh.setOnRefreshListener(this)
swipe_refresh.setColorSchemeResources(R.color.colorAccent)
articleList = ArrayList()
articleAdapter = ArticleAdapter(articleList)
//        userKeyWordInput = ""
compositeDisposable = CompositeDisposable()
recycler_viewHome.setHasFixedSize(true)
recycler_viewHome.layoutManager = LinearLayoutManager(context)
recycler_viewHome.itemAnimator = DefaultItemAnimator()
recycler_viewHome.adapter = articleAdapter
}
override fun onStart() {
super.onStart()
queryTopHeadlines()
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable.clear()
}
override fun onRefresh() {
queryTopHeadlines()
}
private fun queryTopHeadlines() {
swipe_refresh.isRefreshing = true
topHeadlinesObservable = topHeadlinesEndpoint.getTopHeadlines("us", newsApiConfig)
subscribeObservableOfArticle()
}
private fun subscribeObservableOfArticle() {
articleList.clear()
compositeDisposable.add(
topHeadlinesObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap {
Observable.fromIterable(it.articles)
}
.subscribeWith(createArticleObserver())
)
}
private fun createArticleObserver(): DisposableObserver<Article> {
return object : DisposableObserver<Article>() {
override fun onNext(article: Article) {
if (!articleList.contains(article)) {
articleList.add(article)
}
}
override fun onComplete() {
showArticlesOnRecyclerView()
}
override fun onError(e: Throwable) {
Log.e("createArticleObserver", "Article error: ${e.message}")
}
}
}
private fun showArticlesOnRecyclerView() {
if (articleList.size > 0) {
empty_text.visibility = View.GONE
retry_fetch_button.visibility = View.GONE
recycler_viewHome.visibility = View.VISIBLE
articleAdapter.setArticles(articleList)
} else {
recycler_viewHome.visibility = View.GONE
empty_text.visibility = View.VISIBLE
retry_fetch_button.visibility = View.VISIBLE
//            retry_fetch_button.setOnClickListener { checkUserKeywordInput()    }
}
swipe_refresh.isRefreshing = false
}
private fun generateRetrofitBuilder(): Retrofit {
return Retrofit.Builder()
.baseUrl(ENDPOINT_URL)
.addConverterFactory(GsonConverterFactory.create())
//Add RxJava2CallAdapterFactory as a Call adapter when building     your Retrofit instance
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
}

首先,您需要从cardView的onclick传递数据,然后启动活动,最后在所需的项目详细信息活动中处理这些数据。。。启动活动时需要context/AppCompatActivity,因此可以修改适配器的构造函数以接收Context:

class ArticleAdapter(
private val context: Context,
private var articleList: ArrayList<Article>
) : RecyclerView.Adapter<ArticleViewHolder>()

在从fragment:初始化时使用此构造函数

articleAdapter = ArticleAdapter(activity, articleList) // activity => getActivity()

在您的项目中点击监听器:

override fun onBindViewHolder(articleViewHolder: ArticleViewHolder, itemIndex: Int) {
val article: Article = articleList?.get(itemIndex)
setPropertiesForArticleViewHolder(articleViewHolder, article)
articleViewHolder.cardView.setOnClickListener {
val titleString = article.title
val descString = article.description
val urlString = article.url
val toPass = Bundle()
toPass.putString("url", urlString)
toPass.putString("title", titleString)
toPass.putString("desc", descString)
val intent =
Intent(context, YourActivity::class.java) //context we got from constructor
intent.putExtras(toPass)
context.startActivity(intent) // or we can use ContextCompat
}
}

现在处理这些数据并相应地设置视图:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val bundle = intent.extras
val url = bundle?.get("url")
val title = bundle?.get("title")
val desc = bundle?.get("desc")
// now handle those...
titleTextView.text = title!!
//            ...
}

更新:我看到有人已经给出了类似的答案。然而,我仍然保留着它,因为我看到了答案中的一些问题。不能用startActivity从Adapter启动活动,必须是activity.startActivity

我会帮你把东西打碎的。

首先,您需要将列表和上下文/活动传递给适配器。您可以通过适配器的构造函数传递它。

class ArticleAdapter(
val activity: AppCompatActivity
val itemList: MutableList<String>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
}

因此,从片段中,您需要发送如下的上下文/活动:

recyclerView.adapter = ArticleAdapter(activity!!, articleList)

现在,在您的适配器中,在onBindViewHolder方法中,您需要监听单击。

articleViewHolder.itemView.setOnClickListener {
val intent = Intent(activity, YourDesiredActivity::class.java)
activity.startActivity(intent)
}

如果itemView中有错误,请检查ArticleViewHolder类并从其构造函数中重命名视图。如果你遇到任何问题,请告诉我。

初始化适配器类时传递片段上下文
val adapter = ArticleAdapter(listItems, context);

内部onClickListener

articleViewHolder.cardView.setOnClickListener {
context.startActivity(context, TargetActivity);
}