使用View Binding配置RecyclerView失败问题求助
问题:使用View Binding时RecyclerView无法工作,findViewById则正常
使用findViewById可以正常让RecyclerView加载数据,但改用View Binding后,RecyclerView完全无响应——onCreateViewHolder和onBindViewHolder从未被调用,还会出现错误提示No adapter attached; skipping layout,界面上看不到任何数据。虽然findViewById可用,但想改用性能更优的View Binding,已经卡了好几天,求问题原因。
可用代码
val tdList: RecyclerView = findViewById(R.id.td_list); tdList.layoutManager = LinearLayoutManager(this) tdList.setHasFixedSize(true); tdList.adapter = reportAdapter
不可用代码
tdBinding.tdList.layoutManager = LinearLayoutManager(this) tdBinding.tdList.setHasFixedSize(true); tdBinding.tdList.adapter = reportAdapter
完整代码
DurationsReport.kt
package com.funkytwig.tasktimer import android.database.Cursor import android.os.Bundle import android.util.Log import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.funkytwig.tasktimer.databinding.ActivityDurationsReportBinding import com.funkytwig.tasktimer.databinding.TaskDurationsBinding import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch private const val TAG = "DurationsReportXX" enum class SortColumns { NAME, DESCRIPTION, START_DATE, DURATION } class DurationsReport : AppCompatActivity() { private val reportAdapter by lazy { DurationsRVAdapter(this, null) } var databaseCursor: Cursor? = null var sortOrder = SortColumns.NAME private val selection = "${DurationsContract.Columns.START_TIME} BETWEEN ? AND ?" private var selectionArgs = arrayOf("0", "1559347199") private lateinit var binding: ActivityDurationsReportBinding private lateinit var tdBinding: TaskDurationsBinding override fun onCreate(savedInstanceState: Bundle?) { val func = "onCreate" Log.d(TAG, func) super.onCreate(savedInstanceState) binding = ActivityDurationsReportBinding.inflate(layoutInflater) tdBinding = TaskDurationsBinding.inflate(layoutInflater) setContentView(binding.root) setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) Log.d(TAG, "$func: Setup adapter") val tdList: RecyclerView = findViewById(R.id.td_list); tdList.layoutManager = LinearLayoutManager(this) tdList.setHasFixedSize(true); tdList.adapter = reportAdapter // This does not work // tdBinding.tdList.layoutManager = LinearLayoutManager(this) // tdBinding.tdList.setHasFixedSize(true); // tdBinding.tdList.adapter = reportAdapter loadData() } private fun loadData() { val func = "loadData" Log.d(TAG, func) val order = when (sortOrder) { SortColumns.NAME -> DurationsContract.Columns.NAME SortColumns.DESCRIPTION -> DurationsContract.Columns.DESCRIPTION SortColumns.START_DATE -> DurationsContract.Columns.START_DATE SortColumns.DURATION -> DurationsContract.Columns.DURATION } Log.d(TAG, "order=$order") GlobalScope.launch { val cursor = application.contentResolver.query( DurationsContract.CONTENT_URI, null, selection, selectionArgs, order ) Log.d(TAG, "$func: cursor.count=${cursor?.count}") databaseCursor = cursor reportAdapter.swapCursor(cursor)?.close() } } }
DurationsRVAdapter.kt
package com.funkytwig.tasktimer import android.content.Context import android.database.Cursor import android.text.format.DateFormat import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.funkytwig.tasktimer.databinding.TaskDurationItemsBinding import java.util.Locale import java.lang.IllegalStateException private const val TAG = "DurationsRVAdapterXX" class DurationsRVAdapter(context: Context, private var cursor: Cursor?) : RecyclerView.Adapter<DurationsRVAdapter.DurationsViewHolder>() { inner class DurationsViewHolder(val bindings: TaskDurationItemsBinding) : RecyclerView.ViewHolder(bindings.root) private val dateFormat = DateFormat.getDateFormat(context) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DurationsViewHolder { Log.d(TAG, "onCreateViewHolder") val view = TaskDurationItemsBinding.inflate(LayoutInflater.from(parent.context), parent, false) return DurationsViewHolder(view) } override fun onBindViewHolder(holder: DurationsViewHolder, position: Int) { val func = "onBindViewHolder" Log.d(TAG, "$func: position = $position") val cursor = cursor if (cursor != null && cursor.count != 0) { if (!cursor.moveToPosition(position)) { throw IllegalStateException("Couldn't move cursor to position $position") } val name = cursor.getString(cursor.getColumnIndex(DurationsContract.Columns.NAME)) val description = cursor.getString(cursor.getColumnIndex(DurationsContract.Columns.DESCRIPTION)) val startTime = cursor.getLong(cursor.getColumnIndex(DurationsContract.Columns.START_TIME)) val totalDuration = cursor.getLong(cursor.getColumnIndex(DurationsContract.Columns.DURATION)) val userDate = dateFormat.format(startTime * 1000) // The database stores seconds, we need milliseconds val totalTime = formatDuration(totalDuration) holder.bindings.tdName.text = name holder.bindings.tdDescription?.text = description holder.bindings.tdStart.text = userDate holder.bindings.tdDuration.text = totalTime } } private fun formatDuration(duration: Long): String { // convert duration Long to hours:mins:secs String (can be > 24 hours so cant use dateFormat) val hours = duration / 3600 val remainder = duration - hours * 3600 val minutes = remainder / 60 val seconds = remainder % 60 return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds) } override fun getItemCount(): Int { val func = "getItemCount" val count = cursor?.count ?: 0 Log.d(TAG, "$func: count=$count") return count } fun swapCursor(newCursor: Cursor?): Cursor? { val func = "swapCursor" Log.d(TAG, func) if (newCursor === cursor) return null val numItems = itemCount val oldCursor = cursor cursor = newCursor Log.d(TAG, "$func: cursor.count=${cursor?.count}") Log.d(TAG, "$func newCursor.count=${newCursor?.count}, oldCursor.cont=${oldCursor?.count}") if (newCursor != null) { Log.d(TAG, "$func notify the observers about the new cursor") // notify the observers about the new cursor this.notifyDataSetChanged() Log.d(TAG, "$func: notifyDataSetChanged") } else { Log.d(TAG, "$func Notify observer about lack of dataset") // Notify observer about lack of dataset, all of it from 0 to newItems, // i.e. whole range of records has gone this.notifyItemRangeChanged(0, numItems) Log.d(TAG, "$func: notifyItemRangeChanged(0, $numItems)") } return oldCursor } override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { val func = "onAttachedToRecyclerView" Log.d(TAG, "$func: ${recyclerView.adapter.toString()}") super.onAttachedToRecyclerView(recyclerView) } override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { val func = "onDetachedFromRecyclerView" Log.d(TAG, "$func: ${recyclerView.adapter.toString()}") super.onDetachedFromRecyclerView(recyclerView) } }
task_durations.xml
<?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" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_constraintHorizontal_chainStyle="spread"> <TextView android:id="@+id/td_name_heading" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginTop="4dp" android:background="?attr/colorButtonNormal" android:padding="4dp" android:text="@string/td_text_name" android:textAlignment="viewStart" android:textStyle="bold" app:layout_constraintHorizontal_weight="2" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="spread" /> <TextView android:id="@+id/td_start_heading" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" android:background="?attr/colorButtonNormal" android:padding="4dp" android:text="@string/td_text_date" android:textAlignment="viewStart" android:textStyle="bold" app:layout_constraintBaseline_toBaselineOf="@+id/td_name_heading" app:layout_constraintEnd_toStartOf="@+id/td_duration_heading" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@+id/td_name_heading" /> <TextView android:id="@+id/td_duration_heading" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" android:background="?attr/colorButtonNormal" android:padding="4dp" android:text="@string/td_text_duration" android:textAlignment="viewStart" android:textStyle="bold" app:layout_constraintBaseline_toBaselineOf="@+id/td_start_heading" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@+id/td_start_heading" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/td_list" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginBottom="8dp" android:scrollbars="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/td_name_heading" tools:listitem="@layout/task_duration_items" /> </androidx.constraintlayout.widget.ConstraintLayout>
task_duration_items.xml
<?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="wrap_content" tools:showIn="@layout/task_durations"> <TextView android:id="@+id/td_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:padding="4dp" android:textAlignment="viewStart" app:layout_constraintHorizontal_weight="2" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="spread" tools:text="@string/td_text_name" /> <TextView android:id="@+id/td_start" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" android:padding="4dp" android:textAlignment="viewStart" app:layout_constraintBaseline_toBaselineOf="@+id/td_name" app:layout_constraintEnd_toStartOf="@+id/td_duration" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@+id/td_name" tools:text="@string/td_text_date" /> <TextView android:id="@+id/td_duration" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginEnd="4dp" android:padding="4dp" android:textAlignment="viewStart" app:layout_constraintBaseline_toBaselineOf="@+id/td_start" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toEndOf="@+id/td_start" tools:text="@string/td_text_duration" /> </androidx.constraintlayout.widget.ConstraintLayout>
内容的提问来源于stack exchange,提问作者Ben Edwards
相关产品推荐
相关产品推荐

