RecyclerView Adapter methods are not being called

  android, android-recyclerview, kotlin

I know this question has been posed a hundred times, which only makes it more frustrating for me.

I basically took this code from Camera2Basic in https://github.com/android/camera-samples.

Here’s what I’ve built on top from that project so far:

<!-- selector_fragment.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">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/camera_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        />
</androidx.constraintlayout.widget.ConstraintLayout>
class SelectorFragment : Fragment() {
    private var binding: SelectorFragmentBinding? = null

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        binding = SelectorFragmentBinding.inflate(inflater, container, false)
        return binding!!.root
    }

    @SuppressLint("MissingPermission")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val cameraManager =
                requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
        val configList = enumerateCameras(cameraManager)
        val layoutId = android.R.layout.simple_list_item_1
        val genericAdapter = GenericListAdapter(configList, itemLayoutId = layoutId) { innerView, item, _ ->
            innerView.findViewById<TextView>(android.R.id.text1).text = item.title
            innerView.setOnClickListener {
                Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
                        .navigate(SelectorFragmentDirections.actionSelectorToCamera(
                                item.cameraId, item.format))
            }
        }
        val cameraList: RecyclerView = binding!!.cameraList
        cameraList.adapter = genericAdapter
        val items = cameraList.adapter!!.itemCount
        cameraList.adapter!!.notifyDataSetChanged()
    }
    ...
}

/** Type helper used for the callback triggered once our view has been bound */
typealias BindCallback<T> = (view: View, data: T, position: Int) -> Unit

/** List adapter for generic types, intended used for small-medium lists of data */
class GenericListAdapter<T>(
        private val dataset: List<T>,
        private val itemLayoutId: Int? = null,
        private val itemViewFactory: (() -> View)? = null,
        private val onBind: BindCallback<T>
) : RecyclerView.Adapter<GenericListAdapter.GenericListViewHolder>() {

    class GenericListViewHolder(val view: View) : RecyclerView.ViewHolder(view)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = GenericListViewHolder(when {
        itemViewFactory != null -> itemViewFactory.invoke()
        itemLayoutId != null -> {
            LayoutInflater.from(parent.context)
                    .inflate(itemLayoutId, parent, false)
        }
        else -> {
            throw IllegalStateException(
                    "Either the layout ID or the view factory need to be non-null")
        }
    })

    override fun onBindViewHolder(holder: GenericListViewHolder, position: Int) {
        if (position < 0 || position > dataset.size) return
        onBind(holder.view, dataset[position], position)
    }

    override fun getItemCount() = dataset.size
}

When I go into the debugger, none of the Adapter methods are being called.
However, when I turn the screen the Adapter is called, and the app starts behaving as expected.

The problem also occurs when I use the old bindings — i.e. view.camera_list.adapter = genericAdapter.

There shouldn’t be any async happening, and enumerateCameras (omitted for brevity) returns as expected. Calling adapter.getItemCount directly returns as expected.

Adding the cameraList.adapter.notifyDataSetChanged() method seems to have no effect.

I’ve tried removing app:layoutManager and setting cameraList.layoutManager = LinearLayoutManager(requireContext()) directly; in fact, I’ve tried both with the same results.

Changing to a FrameLayout from ConstraintLayout makes no difference.

I’ve updated all my androidx dependencies and all other relevant dependencies.

Again, turning the device and triggering a relayout causes the list to populate as expected. I’m not sure what mechanism is calling adapter methods.

I’m going to try to roll my own Adapter in the mean time instead of using this one.

Any leads are appreciated.

Source: Android Questions

LEAVE A COMMENT