问题
I have read through this Kotiln: pass data from adapter to activity and am attempting option 1 from the answer given.
I have a game with various levels. I send all levels to a recycler view using the the below itemview in 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>
My adapter currently looks like this;
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
}
}
}
The current issue I have is with my companion object, because I have included the callback interface in my onbindviewholder it needs me to include a parameter for it in the return. I am struggling to get my head round this and understand what parameter should be included at this point
Also I am not following it 100%. In the link I shared the interface function is called passDataCallback() and then they use passResultCallback() in the onBindViewHolder. If I use passResultCallback it doesnt resolve so I dont know if I am doing something wrong or if indeed it was meant to be as I have written it in my adapter above
I am not collecting this data in my activity yet as want the adapter to at least be written correctly first, if people think I need to re think the approach completely I will listen to that advice.
Here is the activity that holds the recyclerview. This is where I believe I should be launching the new activity for result
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) }}}
}
}
}
}
Expected Result
Recyclerview is filled with buttons which contain a ratingbar (1-3 stars depending on how well they did last time they played that level) - this works
When i click one of the buttons my activity should know the main level, sublevel and current stars for that level
I will then start a new activity for result passing that information into my minigame
Note: You will notice I set button tags. This was one approach I was following, this can be removed if using the interface works well and if people agree its the best approach
...............EDIT.................
Adapter code updated so that the interface is initialised at adapter level rather than within the onbindviewholder.
However, I still cannot get the following to work;
itemView.setOnClickListener {
//Set your codes about intent here
CallbackInterface.passDataCallback(ML,SL,Stars)
}
The specific problem is that 'passDataCallback' will not resolve i.e. Unresolved reference: passDataCallback. I believe it does now resemble the approach shared in the link at the top. I have to admit I am new to interfaces. I have only ever used them to date within a Dao
I have tried moving the interface into the LevelsViewHolder - This did not fix it.
回答1:
change your adapter to this
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
}
}
}
you initialize the adapter in your activity with
val adapter = LevelsAdaptor(){id->
//TODO do something with id passed from adapter, e.g. create new fragment
}
I would also recommend using Jetpack Navigation you could use the detail view as entering the level
来源:https://stackoverflow.com/questions/65666654/pass-data-from-adapter-to-activity-using-interface-callback-method-with-kotlin