diff options
author | Allan Wang <me@allanwang.ca> | 2019-10-20 00:09:20 -0700 |
---|---|---|
committer | Allan Wang <me@allanwang.ca> | 2019-10-20 00:09:20 -0700 |
commit | 2e13634663cb3c511e4aed556db06a20d8bff6f4 (patch) | |
tree | f583a09a7ab9df68fe7e7b9b21000916d7f07c38 | |
parent | c253ae07a96ba91fa4801039358a2bef52881c53 (diff) | |
download | kau-2e13634663cb3c511e4aed556db06a20d8bff6f4.tar.gz kau-2e13634663cb3c511e4aed556db06a20d8bff6f4.tar.bz2 kau-2e13634663cb3c511e4aed556db06a20d8bff6f4.zip |
Generify data binding to view binding
-rw-r--r-- | fastadapter-databinding/src/main/kotlin/ca/allanwang/fastadapter/databinding/BindingItem.kt | 123 | ||||
-rw-r--r-- | fastadapter-viewbinding/.gitignore (renamed from fastadapter-databinding/.gitignore) | 0 | ||||
-rw-r--r-- | fastadapter-viewbinding/build.gradle (renamed from fastadapter-databinding/build.gradle) | 3 | ||||
-rw-r--r-- | fastadapter-viewbinding/consumer-rules.pro (renamed from fastadapter-databinding/consumer-rules.pro) | 0 | ||||
-rw-r--r-- | fastadapter-viewbinding/proguard-rules.pro (renamed from fastadapter-databinding/proguard-rules.pro) | 0 | ||||
-rw-r--r-- | fastadapter-viewbinding/src/main/AndroidManifest.xml (renamed from fastadapter-databinding/src/main/AndroidManifest.xml) | 0 | ||||
-rw-r--r-- | fastadapter-viewbinding/src/main/kotlin/ca/allanwang/fastadapter/viewbinding/BindingItem.kt | 154 | ||||
-rw-r--r-- | fastadapter-viewbinding/src/main/res/values/ids.xml | 5 | ||||
-rw-r--r-- | sample/src/main/kotlin/ca/allanwang/kau/sample/SwipeActivity.kt | 2 | ||||
-rw-r--r-- | settings.gradle | 2 |
10 files changed, 165 insertions, 124 deletions
diff --git a/fastadapter-databinding/src/main/kotlin/ca/allanwang/fastadapter/databinding/BindingItem.kt b/fastadapter-databinding/src/main/kotlin/ca/allanwang/fastadapter/databinding/BindingItem.kt deleted file mode 100644 index b2b0f26..0000000 --- a/fastadapter-databinding/src/main/kotlin/ca/allanwang/fastadapter/databinding/BindingItem.kt +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.databinding - -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.recyclerview.widget.RecyclerView -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 -} - -abstract class BindingItem<Binding : ViewDataBinding>(open val data: Any?) : - AbstractItem<BindingItem.ViewHolder>(), - BindingLayout<Binding> { - - override val type: Int - get() = layoutRes - - override fun createView(ctx: Context, parent: ViewGroup?): View { - val binding: ViewDataBinding = DataBindingUtil.inflate( - LayoutInflater.from(ctx), - layoutRes, parent, - false - ) - return binding.root - } - - fun getBinding(holder: ViewHolder): Binding? = - DataBindingUtil.getBinding<Binding>(holder.itemView) - - final override fun bindView(holder: ViewHolder, payloads: MutableList<Any>) { - super.bindView(holder, payloads) - val binding = getBinding(holder) ?: return - binding.bindView(holder, payloads) - binding.executePendingBindings() - } - - abstract fun Binding.bindView(holder: ViewHolder, payloads: MutableList<Any>) - - final override fun unbindView(holder: ViewHolder) { - super.unbindView(holder) - val binding = DataBindingUtil.getBinding<Binding>(holder.itemView) ?: return - binding.unbindView(holder) - binding.unbind() - } - - open fun Binding.unbindView(holder: ViewHolder) {} - - final override fun getViewHolder(v: View): ViewHolder = ViewHolder(v, layoutRes) - - override fun failedToRecycle(holder: 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() - - class ViewHolder(itemView: View, internal val layoutRes: Int) : - RecyclerView.ViewHolder(itemView) -} - -interface BindingLayout<Binding : ViewDataBinding> { - val layoutRes: Int -} - -abstract class BindingClickEventHook<Binding : ViewDataBinding, Item : BindingItem<Binding>>(val identifier: BindingLayout<Binding>) : - ClickEventHook<Item>() { - - private fun RecyclerView.ViewHolder.binding(): Binding? { - val holder = this as? BindingItem.ViewHolder ?: return null - if (holder.layoutRes != identifier.layoutRes) { - return null - } - return DataBindingUtil.getBinding(itemView) - } - - final override fun onBind(viewHolder: RecyclerView.ViewHolder): View? = - viewHolder.binding()?.onBind(viewHolder) ?: super.onBind(viewHolder) - - open fun Binding.onBind(viewHolder: RecyclerView.ViewHolder): View? = super.onBind(viewHolder) - - final override fun onBindMany(viewHolder: RecyclerView.ViewHolder): List<View>? = - viewHolder.binding()?.onBindMany(viewHolder) ?: super.onBindMany(viewHolder) - - open fun Binding.onBindMany(viewHolder: RecyclerView.ViewHolder): List<View>? = - super.onBindMany(viewHolder) - - final override fun onClick(v: View, position: Int, fastAdapter: FastAdapter<Item>, item: Item) { - val binding: Binding = DataBindingUtil.findBinding(v) ?: return - binding.onClick(v, position, fastAdapter, item) - } - - abstract fun Binding.onClick(v: View, position: Int, fastAdapter: FastAdapter<Item>, item: Item) -} diff --git a/fastadapter-databinding/.gitignore b/fastadapter-viewbinding/.gitignore index 796b96d..796b96d 100644 --- a/fastadapter-databinding/.gitignore +++ b/fastadapter-viewbinding/.gitignore diff --git a/fastadapter-databinding/build.gradle b/fastadapter-viewbinding/build.gradle index aefb22e..d9de6d5 100644 --- a/fastadapter-databinding/build.gradle +++ b/fastadapter-viewbinding/build.gradle @@ -9,6 +9,9 @@ android { dataBinding { enabled = true } + viewBinding { + enabled = true + } } dependencies { diff --git a/fastadapter-databinding/consumer-rules.pro b/fastadapter-viewbinding/consumer-rules.pro index e69de29..e69de29 100644 --- a/fastadapter-databinding/consumer-rules.pro +++ b/fastadapter-viewbinding/consumer-rules.pro diff --git a/fastadapter-databinding/proguard-rules.pro b/fastadapter-viewbinding/proguard-rules.pro index f1b4245..f1b4245 100644 --- a/fastadapter-databinding/proguard-rules.pro +++ b/fastadapter-viewbinding/proguard-rules.pro diff --git a/fastadapter-databinding/src/main/AndroidManifest.xml b/fastadapter-viewbinding/src/main/AndroidManifest.xml index acd1012..acd1012 100644 --- a/fastadapter-databinding/src/main/AndroidManifest.xml +++ b/fastadapter-viewbinding/src/main/AndroidManifest.xml diff --git a/fastadapter-viewbinding/src/main/kotlin/ca/allanwang/fastadapter/viewbinding/BindingItem.kt b/fastadapter-viewbinding/src/main/kotlin/ca/allanwang/fastadapter/viewbinding/BindingItem.kt new file mode 100644 index 0000000..2d1834f --- /dev/null +++ b/fastadapter-viewbinding/src/main/kotlin/ca/allanwang/fastadapter/viewbinding/BindingItem.kt @@ -0,0 +1,154 @@ +/* + * 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.databinding.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<Binding : ViewBinding> { + val layoutRes: Int + fun createBinding(context: Context, parent: ViewGroup?): Binding + fun Binding.bindView(holder: ViewHolder, payloads: MutableList<Any>) + 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 <T> 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 <T> getBinding(view: View): T = view.getTag(R.id.kau_view_binding_model) as T + } +} + +abstract class BindingItem<Binding : ViewBinding>(open val data: Any?) : + AbstractItem<BindingLayout.ViewHolder>(), + BindingLayout<Binding> { + + 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<Any>) { + super.bindView(holder, payloads) + val binding = holder.getBinding<Binding>() + binding.bindView(holder, payloads) + } + + final override fun unbindView(holder: BindingLayout.ViewHolder) { + super.unbindView(holder) + val binding = holder.getBinding<Binding>() + 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<Binding : ViewBinding, Item : BindingItem<Binding>>(val identifier: BindingLayout<Binding>) : + ClickEventHook<Item>() { + + 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<View>? { + 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<View>? = + super.onBindMany(viewHolder) + + final override fun onClick(v: View, position: Int, fastAdapter: FastAdapter<Item>, item: Item) { + v.findBinding().onClick(v, position, fastAdapter, item) + } + + abstract fun Binding.onClick(v: View, position: Int, fastAdapter: FastAdapter<Item>, item: Item) +} diff --git a/fastadapter-viewbinding/src/main/res/values/ids.xml b/fastadapter-viewbinding/src/main/res/values/ids.xml new file mode 100644 index 0000000..550ae02 --- /dev/null +++ b/fastadapter-viewbinding/src/main/res/values/ids.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <item name="kau_view_binding_model" type="id" /> + <item name="kau_view_binding_root" type="id" /> +</resources>
\ No newline at end of file diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/SwipeActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/SwipeActivity.kt index 5107e18..14b67e2 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/SwipeActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/SwipeActivity.kt @@ -17,7 +17,9 @@ package ca.allanwang.kau.sample import android.app.Activity import android.os.Bundle +import androidx.viewbinding.ViewBinding import ca.allanwang.kau.internal.KauBaseActivity +import ca.allanwang.kau.sample.databinding.ActivitySwipeBinding import ca.allanwang.kau.swipe.SWIPE_EDGE_BOTTOM import ca.allanwang.kau.swipe.SWIPE_EDGE_LEFT import ca.allanwang.kau.swipe.SWIPE_EDGE_RIGHT diff --git a/settings.gradle b/settings.gradle index 0ec0606..43c5974 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,7 +4,7 @@ include ':core', ':about', ':adapter', ':fastadapter', - ':fastadapter-databinding', + ':fastadapter-viewbinding', ':colorpicker', ':mediapicker', ':kpref-activity', |