使用 Kotlin 的接口回调方法将数据从适配器传递到 activity

Pass data from adapter to activity using interface callback method with Kotlin

我已经通读了这个 并且正在尝试根据给定的答案选择选项 1。

我有一个不同级别的游戏。我使用 gridlayout

中的以下项目视图将所有级别发送到回收站视图
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingTop="6dp"
    android:paddingBottom="6dp"
    app:layout_constraintHorizontal_chainStyle="spread">

    <RatingBar
        android:id="@+id/ratingBar"
        style="@style/Widget.AppCompat.RatingBar.Small"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="2dp"
        android:layout_marginEnd="8dp"
        android:isIndicator="true"
        android:numStars="3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />

    <Button
        android:id="@+id/button"
        android:layout_width="90dp"
        android:layout_height="60dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:backgroundTint="@color/game_button_color"
        android:textColor="@color/game_button_text_color"
        app:autoSizeTextType="uniform"
        app:layout_constraintDimensionRatio="1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

我的适配器目前看起来像这样;

    package com.maxcell.sumitup

import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.RatingBar
import androidx.core.content.ContextCompat.startActivity
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView


class LevelsAdaptor(private val callbackInterface:CallbackInterface) : ListAdapter<Levels, LevelsAdaptor.LevelsViewHolder>(LevelsComparator()) {

    interface CallbackInterface {
        fun passDataCallback(main:Int,sub: Int,star: Int)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LevelsViewHolder {
        return LevelsViewHolder.create(parent)
    }

    override fun onBindViewHolder(holder: LevelsViewHolder, position: Int) {
        val current = getItem(position)
        holder.bind(
            current.id,
            current.mainLevel,
            current.subLevel,
            current.stars,
            current.unlocked
        )

    }


    class LevelsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {



        private val btn: Button = itemView.findViewById(R.id.button)
        private val rating: RatingBar = itemView.findViewById(R.id.ratingBar)
        private val myContext = itemView.context
        //private val myContext: Context = sumView.context
        fun bind(ID: Int, ML: Int, SL: Int, Stars: Int, Unlocked: Boolean) {

            btn.text = ID.toString()
            btn.setTag(R.id.mainLevel, ML)
            btn.setTag(R.id.subLevel, SL)
            btn.setTag(R.id.visualLevel, ID)
            btn.setTag(R.id.currentLevelStars, Stars)
            rating.rating = Stars.toFloat()
            if (Unlocked==true){btn.isEnabled=true; btn.isClickable=true}else{btn.isEnabled=false; btn.isClickable=false}

            itemView.setOnClickListener {
                //Set your codes about intent here
                CallbackInterface.passDataCallback(ML,SL,Stars)
            }

        }



        companion object {
            fun create(parent: ViewGroup): LevelsViewHolder {
                val view: View = LayoutInflater.from(parent.context)
                    .inflate(R.layout.rv_storymode_items, parent, false)
                return LevelsViewHolder(view)
            }
        }
    }

    class LevelsComparator : DiffUtil.ItemCallback<Levels>() {
        override fun areItemsTheSame(oldItem: Levels, newItem: Levels): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Levels, newItem: Levels): Boolean {
            return oldItem == newItem
        }
    }
}

我目前遇到的问题是我的伴随对象,因为我在我的 onbindviewholder 中包含了回调接口,它需要我在 return 中为它包含一个参数。我正在努力解决这个问题,并了解此时应该包含哪些参数

我也没有 100% 关注它。在 link 我分享的接口函数叫做 passDataCallback() 然后他们在 onBindViewHolder 中使用 passResultCallback()。如果我使用 passResultCallback,它不会解决,所以我不知道我是否做错了什么,或者它是否真的像我在上面的适配器中写的那样

我不会在我的 activity 中收集这些数据,因为我希望至少首先正确编写适配器,如果人们认为我需要完全重新考虑该方法,我会听取该建议。

这是保存回收站视图的 activity。这是我认为我应该为 result

启动新的 activity 的地方
package com.maxcell.sumitup

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.activity.viewModels
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentTransaction
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_story_levels.*

class StoryLevels : AppCompatActivity(), View.OnClickListener {

    private lateinit var storyView: RecyclerView
    val numbers = mutableListOf(1,2,3,4,5,6,7,8,9,10)
    lateinit var btn1: Button
    lateinit var btn2: Button
    lateinit var btn3: Button
    lateinit var btn4: Button
    lateinit var btn5: Button
    lateinit var btn6: Button
    lateinit var btn7: Button
    lateinit var btn8: Button
    lateinit var btn9: Button
    lateinit var btn10: Button

    lateinit var popupLayout: ConstraintLayout
    lateinit var popupBTNstart:Button
    lateinit var popupBTNexit:Button




    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_story_levels)

        storyView = findViewById(R.id.rv_storymode)

        btn1 = findViewById(R.id.button2)
        btn2 = findViewById(R.id.button3)
        btn3 = findViewById(R.id.button4)
        btn4 = findViewById(R.id.button5)
        btn5 = findViewById(R.id.button6)
        btn6 = findViewById(R.id.button7)
        btn7 = findViewById(R.id.button8)
        btn8 = findViewById(R.id.button9)
        btn9 = findViewById(R.id.button10)
        btn10 = findViewById(R.id.button11)

        btn1.text = numbers[0].toString()
        btn2.text = numbers[1].toString()
        btn3.text = numbers[2].toString()
        btn4.text = numbers[3].toString()
        btn5.text = numbers[4].toString()
        btn6.text = numbers[5].toString()
        btn7.text = numbers[6].toString()
        btn8.text = numbers[7].toString()
        btn9.text = numbers[8].toString()
        btn10.text = numbers[9].toString()

        btn1.setOnClickListener(this)
        btn2.setOnClickListener(this)
        btn3.setOnClickListener(this)
        btn4.setOnClickListener(this)
        btn5.setOnClickListener(this)
        btn6.setOnClickListener(this)
        btn7.setOnClickListener(this)
        btn8.setOnClickListener(this)
        btn9.setOnClickListener(this)
        btn10.setOnClickListener(this)

        popupLayout = findViewById(R.id.layout_pregame_intro)
        popupLayout.isVisible = false
        popupBTNexit = findViewById(R.id.popup_btn_exit)

        val levelsViewModel: LevelsViewModel by viewModels {
            LevelsViewModelFactory((application as MyApplication).repository2)}

        val adapter = LevelsAdaptor()
        rv_storymode.adapter = adapter
        rv_storymode.layoutManager = GridLayoutManager(applicationContext, 4);

        levelsViewModel.level1.observe(this) { newValue ->
            // Update the cached copy of the words in the adapter.
            newValue.let { adapter.submitList(it) }
        }
    }

    fun testMessage(){
        Toast.makeText(applicationContext,"this is toast message", Toast.LENGTH_SHORT).show()
    }
    override fun onClick(v: View?) {

        val levelsViewModel: LevelsViewModel by viewModels {
            LevelsViewModelFactory((application as MyApplication).repository2)}

        val adapter = LevelsAdaptor()
        rv_storymode.adapter = adapter
        rv_storymode.layoutManager = GridLayoutManager(applicationContext, 4);

        if (v != null) {
            when (v.id){
                R.id.button2->{
                    levelsViewModel.level1.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}
                }
                R.id.button3->{
                    levelsViewModel.level2.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button4->{
                    levelsViewModel.level3.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button5->{
                    levelsViewModel.level4.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button6->{
                    levelsViewModel.level5.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button7->{
                    levelsViewModel.level6.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button8->{
                    levelsViewModel.level7.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button9->{
                    levelsViewModel.level8.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button10->{
                    levelsViewModel.level9.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
                R.id.button11->{
                    levelsViewModel.level10.observe(this) { newValue ->
                        // Update the cached copy of the words in the adapter.
                        newValue.let { adapter.submitList(it) }}}
            }
        }
    }
}

预期结果

Recyclerview 充满了包含评级栏的按钮(1-3 星取决于他们上次玩该级别时的表现)- 这有效

当我点击其中一个按钮时,我的 activity 应该知道该关卡的主关卡、子关卡和当前星星

然后我将开始一个新的 activity 结果,并将该信息传递到我的小游戏中

注意:您会注意到我设置了按钮标签。这是我遵循的一种方法,如果使用界面效果很好并且人们同意它是最好的方法,则可以将其删除

.......编辑.................

更新了适配器代码,以便在适配器级别而不是在 onbindviewholder 中初始化接口。

但是,我仍然无法使以下内容正常工作;

itemView.setOnClickListener {
                //Set your codes about intent here
                CallbackInterface.passDataCallback(ML,SL,Stars)
            }

具体问题是 'passDataCallback' 无法解决,即未解决的参考:passDataCallback。我相信它现在确实类似于顶部 link 中共享的方法。我不得不承认我是接口的新手。我只用过它们在 Dao 中约会

我已经尝试将界面移动到 LevelsViewHolder - 这没有解决问题。

将您的适配器更改为此

class LevelsAdaptor(private val onClick:(id:Int)->Unit) : ListAdapter<Levels, LevelsAdaptor.LevelsViewHolder>(LevelsComparator()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LevelsViewHolder {
        val view: View = LayoutInflater.from(parent.context)
            .inflate(R.layout.rv_storymode_items, parent, false)
        return LevelsViewHolder(view)
    }

    override fun onBindViewHolder(holder: LevelsViewHolder, position: Int) {
        val current = getItem(position)
        holder.bind(
            current.id,
            current.mainLevel,
            current.subLevel,
            current.stars,
            current.unlocked
        )
    }
    
    inner class LevelsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val btn: Button = itemView.findViewById(R.id.button)
        private val rating: RatingBar = itemView.findViewById(R.id.ratingBar)
        private val myContext = itemView.context
        //private val myContext: Context = sumView.context
        fun bind(ID: Int, ML: Int, SL: Int, Stars: Int, Unlocked: Boolean) {
            btn.text = ID.toString()
            btn.setTag(R.id.mainLevel, ML)
            btn.setTag(R.id.subLevel, SL)
            btn.setTag(R.id.visualLevel, ID)
            btn.setTag(R.id.currentLevelStars, Stars)
            rating.rating = Stars.toFloat()
            if (Unlocked==true){btn.isEnabled=true; btn.isClickable=true}else{btn.isEnabled=false; btn.isClickable=false}
            itemView.setOnClickListener {
                //Set your codes about intent here
                onClick(ID)
            }
        }
    }

    class LevelsComparator : DiffUtil.ItemCallback<Levels>() {
        override fun areItemsTheSame(oldItem: Levels, newItem: Levels): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Levels, newItem: Levels): Boolean {
            return oldItem == newItem
        }
    }
}

您使用

在 activity 中初始化适配器
val adapter = LevelsAdaptor(){id->
    //TODO do something with id passed from adapter, e.g. create new fragment
}

我也建议使用 Jetpack Navigation 你可以使用详细视图作为进入关卡