【问题标题】:getActivity() / context in a ViewHolder in Kotlin Android在 Kotlin Android 中的 ViewHolder 中的 getActivity()/上下文
【发布时间】:2017-07-28 07:37:11
【问题描述】:

我正在为 Fragment 构建 ViewHolder 和 Adapter,当我尝试为 ViewHolder 创建 OnClick 时,我传入的所有上下文都不起作用。 getActivity() 中的 activity 没有我可以使用的,p0!!.context 也没有 itemView.context 工作。我应该从哪里获取上下文,以及如何引用它?

package com._________.criminalintent

import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast

class CrimeListFragment: Fragment() {
    private var mCrimeRecyclerView: RecyclerView? = null
    private var mAdapter: CrimeAdapter? = null

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    // fragment_crime_list.xml has a RecyclerView element = crime_recycler_view
    // inflate the fragment into the activity
        val view = inflater!!.inflate(R.layout.fragment_crime_list, container, false)

        // grab the recyclerView and give it a required layoutManager
        mCrimeRecyclerView = view.findViewById(R.id.crime_recycler_view)
        mCrimeRecyclerView!!.layoutManager = LinearLayoutManager(activity)
        updateUI()
        return view
    }

    private fun updateUI() {
        val crimeLab = CrimeLab.get(activity)
        val crimes = crimeLab.getCrimes()
        mAdapter = CrimeAdapter(crimes)
        // Connect the adapter to the recyclerView
        mCrimeRecyclerView!!.adapter = mAdapter
    }

    /**
     * in Kotlin, we must give the view passed into the constructor directly
     * as a substitute for a super() call
     *
     * create a ViewHolder that holders the crime list item's view
     *
     * super(itemView) = super(inflater!!.inflate(R.layout.list_item_crime, parent, false))
     * MUST give it the direct value in Kotlin
     */
    private class CrimeHolder(inflater: LayoutInflater?, parent: ViewGroup):
        RecyclerView.ViewHolder(inflater!!.inflate(R.layout.list_item_crime, parent, false)),
        View.OnClickListener {

        private var mCrime: Crime? = null

        /**
         * When given a crime, this CrimeHolder will update the title and date for this Crime
         */
        fun bind(crime: Crime) {
           mCrime = crime
            val titleTextView = itemView.findViewById<TextView>(R.id.crime_title)
            val dateTextView = itemView.findViewById<TextView>(R.id.crime_date)
            titleTextView.text = mCrime!!.mTitle
            dateTextView.text = mCrime!!.mDate.toString()
        }

        override fun onClick(p0: View?) {

            Toast.makeText(WHAT_TO_PUT_HERE, "${mCrime!!.mTitle} clicked!", Toast.LENGTH_SHORT / 2)
                .show()
    }
}


    private class CrimeAdapter(private var mCrimes: MutableList<Crime>):
        RecyclerView.Adapter<CrimeHolder>() {

        /**
         * - Calls our CrimeHolder to make our custom ViewHolders
         * - Called by RecyclerView when it needs a new view to display
         * - Gets the layoutInflater from the ViewGroup and returns a CrimeHolder of it
         */
        override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): CrimeHolder
        = CrimeHolder(LayoutInflater.from(parent!!.context), parent)


        /**
         * Bind the crime (data) to the CrimeHolder
         */
        override fun onBindViewHolder(holder: CrimeHolder?, position: Int) {
            holder!!.bind(mCrimes[position])
        }

        /**
         * Sees how many items are in the RecyclerView that need to be shown
         */
        override fun getItemCount(): Int = mCrimes.size
    }
}

【问题讨论】:

  • 我知道这不是代码审查,但这是我的 2 美分。通过将您的视图设置为lateinit var,您就不需要在任何地方都使用!!
  • 天哪。我怎么从来不知道...谢谢!
  • 谈论 2 美分,总是使用最接近的上下文。您需要布局管理器的上下文吗?使用recyclerView.context。 /// inflater in onCreateView 永远不会为空。 container 可能为空。 /// 不要在CrimeHolder 中添加上下文!视图持有者可以访问他们的itemView,因此可以访问itemView.context
  • 在 Kotlin 中,嵌套类默认是静态的。如果您需要内部类,只需写inner class。小心内存泄漏!
  • @EugenPechanec 从来不知道您可以更改覆盖上的空安全性。谢谢!

标签: android kotlin android-viewholder kotlin-android-extensions


【解决方案1】:

在您的实现中,您可以安全地使用从View 提供给您的OnClickListenerContext

override fun onClick(p0: View) {
          Toast.makeText(p0.context, "${mCrime!!.mTitle} clicked!", Toast.LENGTH_SHORT / 2)
                .show()
    }

只记得设置onclick:

fun bind(crime: Crime) {
           mCrime = crime
            val titleTextView = itemView.findViewById<TextView>(R.id.crime_title)
            val dateTextView = itemView.findViewById<TextView>(R.id.crime_date)
            titleTextView.text = mCrime!!.mTitle
            dateTextView.text = mCrime!!.mDate.toString()
            itemView.setOnClickListener(this)
}

此外,默认情况下,所有 Kotlin 类都是嵌套的 (static)。所以你的private class CrimeHolder 相当于Java 中的private static class CrimeHolder。这就是为什么您无法从CrimeHolder 中访问getActivity()

【讨论】:

  • setOnClickListener 成功了!因此,无论是否有 onClick,我都需要为视图设置 onClick。为什么会传入this?我认为 static 是 Kotlin 等价物的伴生对象。
  • val titleTextView = itemView.findViewById&lt;TextView&gt;(R.id.crime_title) 等应该直接在视图持有者上定义不是每次绑定视图持有者时都在本地。这违背了视图持有者的全部目的 - 缓存找到的视图。
  • @EugenPechanec 谢谢你,这很有意义。
【解决方案2】:

在您的持有人中使用itemView.context 属性。

编辑: 你的onClick 不“工作”(未被调用)的原因是因为你还没有注册onClickListener,例如:

itemView.setOnClickListener(this)

在您的支架内initbind

【讨论】:

  • @user3338275 查看编辑后的答案。您尚未在任何地方设置 OnClickListener。
【解决方案3】:
 itemView.setOnClickListener {
                Toast.makeText(itemView.context, "Item is clicked $position", Toast.LENGTH_SHORT).show() }
        }

【讨论】:

    猜你喜欢
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 2018-08-09
    • 2022-01-13
    • 1970-01-01
    • 2019-08-19
    • 2017-10-23
    • 2021-02-18
    相关资源
    最近更新 更多