I just started learning Android apps in Android Studio using Kotlin Programming. When I want to create a simple RecyclerView example. I encounter bugs when using the recycler view adapter.
I have followed few tutorials online but the bug still exists in the code. I think the bug comes from the GameFragment.kt where it needs to set the layout manager (the below is the code related to initializing the recycler view).
val adapter = GameAdapter()
binding.recyclerView.adapter = adapter
val linearLayoutManager:LinearLayoutManager = LinearLayoutManager(activity)
linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
binding.recyclerView.layoutManager = linearLayoutManager
I tried to print log messages to see which method is executed, the result shows that the adapter is initialized twice and onCreateViewHolder is executed once, but onBindViewHolder never executed. Inside LinearLayoutManager, I tried to put "this", "activity", "RequiredActivity", "this as context", "activity.context", but none of the above works. Does anyone know how I can fix this problem?
The error log generated
java.lang.ClassCastException: androidx.constraintlayout.widget.ConstraintLayout cannot be cast to android.widget.TextView
at com.jasonpwy.simplecountdowntimer.game.GameAdapter.onCreateViewHolder(GameAdapter.kt:31)
at com.jasonpwy.simplecountdowntimer.game.GameAdapter.onCreateViewHolder(GameAdapter.kt:12)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7078)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6235)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1855)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:530)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:789)
at android.view.View.layout(View.java:21946)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3086)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2596)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1727)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7612)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1029)
at android.view.Choreographer.doCallbacks(Choreographer.java:852)
at android.view.Choreographer.doFrame(Choreographer.java:787)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1014)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:359)
at android.app.ActivityThread.main(ActivityThread.java:7407)
game_fragment.xaml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="gameViewModel"
type="com.example.simplecountdowntimer.game.GameViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/activity_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:ems="10"
android:hint="@string/activity_name"
android:inputType="textPersonName"
app:layout_constraintBaseline_toBaselineOf="@+id/timer_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/submit_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="20dp"
android:text="@string/submit"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/activity_name" />
<TextView
android:id="@+id/timer_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:fontFamily="@font/roboto"
android:text="@{gameViewModel.currentTimeString}"
android:textSize="28sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/activity_name"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/submit_btn"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
GameFragment.kt
package com.example.simplecountdowntimer.game
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.jasonpwy.simplecountdowntimer.R
import com.jasonpwy.simplecountdowntimer.databinding.GameFragmentBinding
class GameFragment : Fragment() {
private lateinit var viewModel : GameViewModel
private lateinit var binding: GameFragmentBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate view and obtain an instance of the binding class
binding = DataBindingUtil.inflate(
inflater,
R.layout.game_fragment,
container,
false
)
viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
binding.lifecycleOwner = this
binding.gameViewModel = viewModel
val adapter = GameAdapter()
binding.recyclerView.adapter = adapter
binding.submitBtn.setOnClickListener {
Log.i("Test", "It runs")
viewModel.addText(binding.activityName.text.toString())
viewModel.CountDown()
}
val linearLayoutManager:LinearLayoutManager = LinearLayoutManager(activity)
linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
binding.recyclerView.layoutManager = linearLayoutManager
viewModel.ListData.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})
return binding.root
}
}
GameAdapter.kt
package com.jasonpwy.simplecountdowntimer.game
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.simplecountdowntimer.R
class GameAdapter() : RecyclerView.Adapter<GameAdapter.TextItemViewHolder>() {
init{
Log.i("Temp", "Created")
}
var data = listOf<String>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
val item = data[position]
holder.view.text = item
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
val textView = LayoutInflater.from(parent.context).inflate(R.layout.game_fragment, parent, false)
return TextItemViewHolder(textView as TextView)
}
override fun getItemCount(): Int {
return data.size
}
class TextItemViewHolder(val view: TextView): RecyclerView.ViewHolder(view)
}
GameViewModel.kt
package com.example.simplecountdowntimer.game
import android.os.CountDownTimer
import android.text.format.DateUtils
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
class GameViewModel : ViewModel(){
companion object {
// These represent different important times
// This is when the game is over
const val DONE = 0L
// This is the number of milliseconds in a second
const val ONE_SECOND = 1000L
// This is the total time of the game
const val COUNTDOWN_TIME = 10000L
}
private lateinit var timer : CountDownTimer
private var _currentTime = MutableLiveData<Long>()
val currentTime : LiveData<Long>
get() = _currentTime
val currentTimeString = Transformations.map(currentTime) { time ->
DateUtils.formatElapsedTime(time)
}
private val _storedText = MutableLiveData<String>()
val storeText : LiveData<String>
get() = _storedText;
private val _ListData = MutableLiveData<List<String>>()
val ListData : LiveData<List<String>>
get() = _ListData
init{
_storedText.value = ""
_ListData.value = listOf()
_ListData.value = _ListData.value?.plus("Testing only")
}
fun CountDown(){
Log.i("Test", "Count Down Start")
_currentTime.value = COUNTDOWN_TIME
timer = object : CountDownTimer(COUNTDOWN_TIME, ONE_SECOND) {
override fun onTick(millisUntilFinished: Long) {
_currentTime.value = (millisUntilFinished/ONE_SECOND)
}
override fun onFinish() {
_currentTime.value = DONE
}
}
timer.start()
}
fun addText(message: String){
_storedText.value += message
_storedText.value += "\n"
_storedText.value?.let { Log.i("Test", it) }
_ListData.value = _ListData.value?.plus("Testing only")
}
}
Your error message is pretty self explanatory. A TextView
cannot be cast to a ConstraintLayout
.
When you're inflating your view in onCreateViewHolder
you get a View
which is the root of the layout you're inflating ( game_fragment
), and the root is a ConstraintLayout
. So when you write return TextItemViewHolder(textView as TextView)
you get an error because a ConstraintLayout
isn't a TextView
.
You'll have to use findViewById
to get the TextView
from the view that was inflated, something like the code below
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.game_fragment, parent, false)
val textView: TextView = view.findViewById(R.id.<id of your text view>)
return TextItemViewHolder(textView as TextView)
}
I found out that I misunderstood the meaning of the recycler view. I finally can solve the problem by changing the recycler view adapter and add one more layout to the project.
I uploaded the project to github:https://github.com/Jason627592/SimpleAndroidRecyclerViewExample
Updated version of recyclerview adapter:
package com.example.simplecountdowntimer.game
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.jasonpwy.simplecountdowntimer.R
import kotlinx.android.synthetic.main.recyclerview_row.view.*
class TextItemViewHolder(view: View): RecyclerView.ViewHolder(view){
var content: TextView = view.stored_message
}
class GameAdapter() : RecyclerView.Adapter<TextItemViewHolder>() {
var data = listOf<String>()
set(value) {
field = value
notifyDataSetChanged()
}
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
val item = data[position]
holder.content.text = item
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_row, parent, false)
return TextItemViewHolder(view)
}
override fun getItemCount(): Int {
return data.size
}
}
recyclerview_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="@+id/stored_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
tools:text="Testing" />
</LinearLayout>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.