首页 > 解决方案 > 试图影响片段内的视图的问题

问题描述

所以,我目前正在使用 Android Studio 构建一个 Android 应用程序。我正在使用 Kotlin 构建它,关于该应用程序的具体要点是:

我目前坚持RecyclerView使用GridLayout.

我已经准备好用户界面,并RecyclerView用我的适配器内的列表中的静态数据填充RecyclerView.

我做的下一件事是使用从我的 API 获取我的 JSON 数据OkHTTP,然后使用GSON. 到目前为止一切正常,JSON 格式正确并获得。当我想将它传递给适配器时,麻烦就开始了。

在此之前我在访问东西时遇到了一些麻烦,因为我是 Kotlin 和 Android 的新手,我无法直接从片段中访问东西,但后来我发现我可以将片段的主视图分配给一个变量并参考从那里开始。那部分部分解决了我的问题。所以,我的代码是这样的:

MesFragment.kt - 这是我的片段的 Kotlin 文件,如您所见,我将主视图分配给mesView变量,我必须在主函数之外创建它,因为当尝试在fetchMes()函数中分配适配器时它没有访问视图

class MesFragment : Fragment() {

    var mesView : View? = null

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        mesView = inflater.inflate(R.layout.fragment_mes, container, false)
        //mesView.calendario_mes.adapter = MesAdapter()

        fetchInfoMes("201905")

        return mesView

    }


    fun fetchInfoMes(mes: String) {
        val url = "https://www.myoriginalsite.com/api/mes/"+ mes +"/-0500/norte"

        val request = Request.Builder().url(url).build()

        val client = OkHttpClient()
        client.newCall(request).enqueue(object: Callback {

            override fun onResponse(call: Call, response: Response) {
                var body = response.body()?.string()

                val gson = GsonBuilder().create()

                val mesFeed : List<FechaCompleta> = gson.fromJson(body, object: TypeToken<List<FechaCompleta>>() {}.type)

                mesView?.calendario_mes?.adapter = MesAdapter(mesFeed)
            }

            override fun onFailure(call: Call, e: IOException) {
                println("Error")
            }

        })
    }

}

class FechaCompleta(val vacia: Boolean, val imagen_signo: String, val imagen_luna: String, val fecha: String, val dia: String, val id_entrada: Int)

MesAdapter.kt - 这是我的适配器,如您所见,我已经评论了我的静态列表,我想要做的唯一 UI 更改是mes_fecha.text

class MesAdapter(val mesFeed: List<FechaCompleta>): RecyclerView.Adapter<MesViewHolder>() {

    //val list = listOf<String>("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31")

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MesViewHolder {
        val layoutInflater = LayoutInflater.from(parent?.context)
        val cell = layoutInflater.inflate(R.layout.celda_mes_layout, parent, false)

        return MesViewHolder(cell)
    }

    override fun getItemCount(): Int {
        return mesFeed.size
    }

    override fun onBindViewHolder(holder: MesViewHolder, position: Int) {
        holder?.view?.mes_fecha.text = mesFeed[position].dia
    }

}

class MesViewHolder(val view: View): RecyclerView.ViewHolder(view) {

}

一旦我打开片段,应用程序就会关闭,我得到这个错误:

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.testing.fragmentapp, PID: 8148
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

标签: androidandroid-fragmentskotlinandroid-recyclerview

解决方案


OkHttpClient 在后台线程上调用回调。

所以你应该:

1.) 改用 Retrofit,它会在 UI 线程上调用你的回调

2.) 使用 Handler 从后台线程返回 UI 线程

private val handler = Handler(Looper.getMainLooper())

call.enqueue(object: Callback {
        override fun onResponse(call: Call, response: Response) {
            var body = response.body()?.string()

            val gson = GsonBuilder().create()

            val mesFeed : List<FechaCompleta> = gson.fromJson(body, object: TypeToken<List<FechaCompleta>>() {}.type)

            handler.post {
                mesView?.calendario_mes?.adapter = MesAdapter(mesFeed)
            }
        }

        override fun onFailure(call: Call, e: IOException) {
            println("Error")
        }
})

推荐阅读