I’m currently working on a Wordle clone with Kotlin. So far I’ve finished building the keyboard with RecyclerView, but there are some issues with the onClick event listener, it’s not very responsive. I have to click the key multiple times to make it work, sometimes it just doesn’t respond to click at all. I wonder what I did wrong here.
Here’s my code for MainActivity
class MainActivity : AppCompatActivity() {
private var layoutManager: RecyclerView.LayoutManager? = null;
private var adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>? = null;
private var row1KeyList = listOf<String>("Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P");
private var row2KeyList = listOf<String>("A", "S", "D", "F", "G", "H", "J", "K", "L");
private var row3KeyList = listOf<String>("Z", "X", "C", "V", "B", "N", "M");
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val recyclerView2 = findViewById<RecyclerView>(R.id.recyclerView2)
val recyclerView3 = findViewById<RecyclerView>(R.id.recyclerView3)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerView2.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
recyclerView3.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
var rowAdapter1 = RecyclerAdapter(row1KeyList)
recyclerView.adapter = rowAdapter1
rowAdapter1.setOnItemClickListener(object : RecyclerAdapter.onItemClickListener{
override fun onItemClick(position: Int){
// Toast.makeText(this@MainActivity, row1KeyList[position], Toast.LENGTH_SHORT).show()
Log.d("letter: ", row1KeyList[position])
Log.d("position", position.toString())
val tile = findViewById<TextView>(R.id.tile1)
tile.text = row1KeyList[position]
}
})
recyclerView2.adapter= RecyclerAdapter(row2KeyList)
recyclerView3.adapter= RecyclerAdapter(row3KeyList)
}
}
Here’s the code of RecyclerAdapter
class RecyclerAdapter(val keyList: List<String>): RecyclerView.Adapter <RecyclerAdapter.ViewHolder>() {
// private var keyList = listOf<String>("Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P");
private var mListener: onItemClickListener? = null
interface onItemClickListener{
fun onItemClick(position: Int)
}
fun setOnItemClickListener(listener: onItemClickListener){
mListener = listener
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.key_list, parent, false)
return ViewHolder(v, mListener)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemKey.text = keyList[position]
}
override fun getItemCount(): Int {
return keyList.size;
}
inner class ViewHolder(itemView: View, listener: onItemClickListener?): RecyclerView.ViewHolder(itemView) {
var itemKey: AppCompatButton
init{
itemView.setOnClickListener {
listener?.onItemClick(position)
}
itemKey = itemView.findViewById(R.id.letterKey)
}
}
}
In case, I also attached the code of the layout file of the key list and the main layout
// key_list.xml defined how each key looks like
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/letterKey"
android:layout_width="24dp"
android:layout_height="36dp"
android:background="#D3D6DA"
android:layout_marginRight="6dp"
android:textColor="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
here’s the layout for main activity
<?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="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.6"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.7"
/>
<TextView
android:id="@+id/tile1"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:textSize="10pt"
android:gravity="center"
android:background="@drawable/tile_border"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_bias="0.1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3
Answers
I have figured out a solution inspired by Jon's answer. Basically, I just create a listener in the main activity and pass it to the adapter
Here's the updated main activity code:
here's the updated RecyclerAdapter code:
TRY THIS:
adapter:
Your new MainActivity:
You haven’t posted your click listener code for the other two rows in the question, so we don’t know what’s happening there and if there’s a problem – but your solution you posted is treating every click listener value as coming from row 1. You need to do something like this:
And then your Adapter needs to handle that function we’re passing in:
You could set up a click listener in
onBindViewHolder
instead, but it’s more efficient to set it up once perViewHolder
so you’re not constantly creating listener objects (in general, doesn’t matter here with your fixed layout!)That should make what you’re doing work – your other option is to just pass the key character itself, i.e. make the listener function a
(String) -> Unit
(you should be working withChar
s really) and then the listener can just use the character instead of having to look it up by position.The other thing I’d say is this isn’t really something you should use a
RecyclerView
for – it’s meant for scrolling lists of items, and it reusesViewHolder
s to make that efficient instead of creating one for every single item. You have everything displayed on the screen at once, you’re not getting any benefit for the extra work it involves!There are lots of ways to do what you’re doing – the simplest would just be a
ConstraintLayout
with all the keyTextView
s (they should beButton
s really, especially for accessibility reasons) added to it in rows, using apacked
chain to squish them all together in the centre. They could all use the sameOnClickListener
, which passes theView
that was clicked as a parameter, so you can grab the text contents (or atag
) and work out which key was pressed. Just an idea! You’ve already done the work here so no need to reinvent it