idanatz / OneAdapter

A Viewholderless Adapter for RecyclerView, who supports builtin diffing, states (paging, empty...), events (clicking, swiping...), and more.
MIT License
470 stars 45 forks source link

View Binding with Kotlin 1.6.0 not working #46

Closed vsxed closed 2 years ago

vsxed commented 2 years ago

Hey there,

as of today i tried migrating an older app and now i am required to use Kotlins Android View Binding with kotlin 1.6.0.

I followed the example in the readme but i cant make it to work. This is how my code looks:

class DiscoveredDeviceModule(
    val context: Context,
    private val callback: DeviceModuleCallbackListener?
) : ItemModule<DiffableDevice>() {
    init {
        config {
            layoutResource = R.layout.component_discover_bt_list_item

            firstBindAnimation = AnimatorInflater.loadAnimator(
                context,
                R.animator.animator_new_bt_device_discovered
            )
        }

        onBind { item, viewBinder, _ ->
            viewBinder.bindings(ComponentDiscoverBtListItemBinding::bind).run {
                btListItemTitle.text = item.getReadableName(context.resources)
                // PushDownAnim.setPushDownAnimTo(btListItemWrapper)

                btListItemWrapper.setOnClickListener {
                    btListItemProgress.visibility = View.VISIBLE
                    callback?.onClick(item)
                }
            }
        }
    }

    interface DeviceModuleCallbackListener {
        fun onClick(device: DiffableDevice) {}
    }
}

Everytime the onBind should have been called, nothing appears in the view. Any help?

The version i'm using is 2.1.1

idanatz commented 2 years ago

Hey, onBind should be called regardless of viewbindings. it means that the item is not rendering in your RecycerView.

  1. Please add the XMLs where the RecyclerView is defined and R.layout.component_discover_bt_list_item
  2. have you tried it without the the onBindAnimation?
  3. does your RecyclerView has LayoutManager attached?
vsxed commented 2 years ago

Hey, thanks for the fast reply!

onBind is called correctly, since the breakpoint stops at the root of onBind { .. }

R.layout.component_discover_bt_list_item.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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/bt_list_item__wrapper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <View
            android:id="@+id/bt_list_item__prefix"
            android:layout_width="4dp"
            android:layout_height="4dp"
            android:layout_marginStart="@dimen/component_discover_bt_horizontal_spacing"
            android:layout_marginEnd="24dp"
            android:background="@drawable/ic_dot"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/bt_list_item__title"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/bt_list_item__title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:paddingStart="0dp"
            android:paddingTop="12dp"
            android:paddingEnd="64dp"
            android:paddingBottom="12dp"
            android:singleLine="true"
            android:textColor="@color/colorLightGrey"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/bt_list_item__prefix"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Device name" />

        <ProgressBar
            android:id="@+id/bt_list_item__progress"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_marginEnd="@dimen/component_discover_bt_horizontal_spacing"
            android:indeterminateTint="@color/colorLightGrey"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@id/bt_list_item__title"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@drawable/component_list_item_divider"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

The actual RecyclerView layout

<?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/discover_bt__device_list__devices"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:isScrollContainer="false"
            android:nestedScrollingEnabled="false"
            android:orientation="vertical"
            android:paddingBottom="32dp"

            <!-- LayoutManager is set here -->
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"

            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/discover_bt__device_list__status"
            tools:itemCount="24"
            tools:listitem="@layout/component_discover_bt_list_item" />
...
</androidx.constraintlayout.widget.ConstraintLayout>

And heres the relevant Fragment code where it is actucally attached:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
adapter = OneAdapter(discover_bt__device_list__devices!!) {
            itemModules += DiscoveredDeviceModule(
                requireContext(),
                object : DiscoveredDeviceModule.DeviceModuleCallbackListener {
                    override fun onClick(device: DiffableDevice) {
                        viewModel.apply {
                            viewModel.stopScanningForDevices()

                            binding?.let {
                                // add extras to tell the fragment to create a shared transition
                                val navExtraInfo = FragmentNavigatorExtras(
                                    it.discoverBtContentImage to "discover_image",
                                    it.discoverBtContentInfo to "discover_descr_text"
                                )

                                findNavController().navigate(
                                    DiscoverBluetoothFragmentDirections.routeFromDiscoverBluetoothToDiscoveryPersonalization(
                                        deviceMacAddress = device.uuid,
                                        suggestedName = device.getReadableName(resources),
                                        deviceQrCode = null,
                                    ),
                                    navExtraInfo
                                )
                            }
                        }
                    }
                }
            )
        }

        adapter.setItems(
            btViewModel.discoveredDevices.value?.map { device -> DiffableDevice(device) }
                ?: listOf()
        )
...
}
  1. Removing the animation makes no difference.
  2. as you can see above in the xml: it has. But also doing it programmatically before attaching the adapter, it does not seem to work.

Before switching to View Binding, everything worked fine.

idanatz commented 2 years ago

I've updated the sample project to Kotlin 1.6.0 and the ViewBindings example works without any special issues 🤷🏻‍♂️

Have you verified your implementation against the sample project by any chance? https://github.com/ironSource/OneAdapter/blob/develop/sample/app/src/main/java/com/idanatz/sample/examples/binding/view_binding/ViewBindingActivity.kt

Its very simple example but I've used this feature in bigger and more complex scenarios with no issues

vsxed commented 2 years ago

Thanks so far. Will take a look at it and report back.

vsxed commented 2 years ago

Okay, this issue can be closed. Seems like it was an underlying View Binding issue.

Inside the Fragment i got an

...
  <include  layout="@layout/component_discover_device_list" />
...

without an id, so the View Binding could not reference it (and it inner children) properly. This resulted in the above error, since all child views may appear in the debugging tools as valid views, but it was not possible to change them programmatically.

The fix was to give the include also a reference

...
        <include
            android:id="@+id/innerReference"
            layout="@layout/component_discover_device_list" />
...

and then use them like this

binding?.innereReference?.btListItemTitle?.text = "anything"

Now it seems to work fine.