/* * Copyright 2019 Allan Wang * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.allanwang.fastadapter.viewbinding import android.content.Context import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding import ca.allanwang.kau.fastadapter.viewbinding.R import ca.allanwang.kau.logging.KL import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.GenericItem import com.mikepenz.fastadapter.items.AbstractItem import com.mikepenz.fastadapter.listeners.ClickEventHook interface VhModel { fun vh(): GenericItem } interface BindingLayout { val layoutRes: Int fun createBinding(context: Context, parent: ViewGroup?): Binding fun Binding.bindView(holder: ViewHolder, payloads: MutableList) fun Binding.unbindView(holder: ViewHolder) class ViewHolder(itemView: View, internal val layoutRes: Int) : RecyclerView.ViewHolder(itemView) { /** * Retrieves a binding. * * It is assumed that the binding is set on view holder creation, * and that its type matches the supplied generic. */ fun getBinding(): T = getBinding(itemView) } companion object { fun setBinding(view: View, binding: Any) { view.setTag(R.id.kau_view_binding_model, binding) } @Suppress("UNCHECKED_CAST") fun getBinding(view: View): T = view.getTag(R.id.kau_view_binding_model) as T } } abstract class BindingItem(open val data: Any?) : AbstractItem(), BindingLayout { override val type: Int get() = layoutRes override fun createView(ctx: Context, parent: ViewGroup?): View { val binding = createBinding(ctx, parent) BindingLayout.setBinding(binding.root, binding) return binding.root } final override fun bindView(holder: BindingLayout.ViewHolder, payloads: MutableList) { super.bindView(holder, payloads) val binding = holder.getBinding() binding.bindView(holder, payloads) } final override fun unbindView(holder: BindingLayout.ViewHolder) { super.unbindView(holder) val binding = holder.getBinding() binding.unbindView(holder) } override fun Binding.unbindView(holder: BindingLayout.ViewHolder) {} final override fun getViewHolder(v: View): BindingLayout.ViewHolder = BindingLayout.ViewHolder(v, layoutRes) override fun failedToRecycle(holder: BindingLayout.ViewHolder): Boolean { KL.e { "Failed to recycle" } return super.failedToRecycle(holder) } override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is BindingItem<*>) return false return identifier == other.identifier && data == other.data } override fun hashCode(): Int = data.hashCode() } abstract class BindingClickEventHook>(val identifier: BindingLayout) : ClickEventHook() { private fun RecyclerView.ViewHolder.binding(): Binding? { val holder = this as? BindingLayout.ViewHolder ?: return null if (holder.layoutRes != identifier.layoutRes) { return null } return getBinding() } /** * All bound views must set the view root, which will be used to find the binding. * We avoid attaching the binding directly */ private fun View.setRoot(root: View) { setTag(R.id.kau_view_binding_root, root) } private fun View.findBinding(): Binding { val root = getTag(R.id.kau_view_binding_root) as View return BindingLayout.getBinding(root) } final override fun onBind(viewHolder: RecyclerView.ViewHolder): View? { val binding = viewHolder.binding() ?: return super.onBind(viewHolder) val view = binding.onBind(viewHolder) ?: return super.onBind(viewHolder) view.setRoot(binding.root) return view } open fun Binding.onBind(viewHolder: RecyclerView.ViewHolder): View? = super.onBind(viewHolder) final override fun onBindMany(viewHolder: RecyclerView.ViewHolder): List? { val binding = viewHolder.binding() ?: return super.onBindMany(viewHolder) val views = binding.onBindMany(viewHolder) ?: return super.onBindMany(viewHolder) views.forEach { it.setRoot(binding.root) } return views } open fun Binding.onBindMany(viewHolder: RecyclerView.ViewHolder): List? = super.onBindMany(viewHolder) final override fun onClick(v: View, position: Int, fastAdapter: FastAdapter, item: Item) { v.findBinding().onClick(v, position, fastAdapter, item) } abstract fun Binding.onClick(v: View, position: Int, fastAdapter: FastAdapter, item: Item) }