Skip to content

Commit

Permalink
Merge pull request #112 from AlanCheen/feature/layout-delegate
Browse files Browse the repository at this point in the history
Feature/layout delegate
  • Loading branch information
AlanCheen authored Sep 8, 2022
2 parents 7791536 + 036d057 commit 473f8e7
Show file tree
Hide file tree
Showing 15 changed files with 129 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,7 @@ class TestAllComponent(view: View) : Component<TestAllModel>(view) {

val logService = adapter.getAdapterService(TestService::class.java)
logService?.log("LogService Message")

val logService2 = adapter.getAdapterService<TestService>("LogService")

val result = logService2?.testResult()
messageTextView.text = result
messageTextView.text = logService?.testResult()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,6 @@ open class BaseTestcaseFragment : Fragment(), Scrollable {
}

adapter.registerAdapterService(TestService::class.java)
adapter.registerAdapterService("LogService", TestService::class.java)

initLayoutManagers()
initItemDecorations()
Expand Down
10 changes: 6 additions & 4 deletions app/src/main/java/me/yifeiyuan/flapdev/testcases/DSLTestcase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import android.widget.ImageView
import me.yifeiyuan.flap.Component
import me.yifeiyuan.flap.FlapAdapter
import me.yifeiyuan.flap.delegate.AdapterDelegate
import me.yifeiyuan.flap.delegate.LayoutAdapterDelegate
import me.yifeiyuan.flap.dsl.adapterDelegate
import me.yifeiyuan.flap.dsl.adapterHook
import me.yifeiyuan.flap.ext.bindButton
import me.yifeiyuan.flap.ext.bindTextView
import me.yifeiyuan.flap.ext.bindView
import me.yifeiyuan.flap.ext.*
import me.yifeiyuan.flap.hook.AdapterHook
import me.yifeiyuan.flapdev.R
import me.yifeiyuan.flapdev.components.SimpleImageModel
import me.yifeiyuan.flapdev.components.SimpleTextModel
import me.yifeiyuan.flapdev.components.TestAllModel
import me.yifeiyuan.flapdev.components.TestBinderModel
import me.yifeiyuan.flapdev.mockMultiTypeModels

private const val TAG = "DSLTestcase"
Expand Down Expand Up @@ -157,7 +157,9 @@ class DSLTestcase : BaseTestcaseFragment() {
}
}


// val layoutDelegate = LayoutAdapterDelegate(TestBinderModel::class.java, R.layout.flap_item_binder) {
// model: TestBinderModel, position: Int, payloads: List<Any>, adapter: FlapAdapter ->
// }

adapter.registerAdapterDelegates(simpleTextDelegate, simpleImageDelegate, testAllDelegate)

Expand Down
5 changes: 3 additions & 2 deletions flap/src/main/java/me/yifeiyuan/flap/Component.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import me.yifeiyuan.flap.delegate.AdapterDelegate
* @since 2020/9/22
* @since 3.0.0
*/
abstract class Component<T>(itemView: View) : RecyclerView.ViewHolder(itemView), LifecycleObserver, ComponentConfig {
open class Component<T>(itemView: View) : RecyclerView.ViewHolder(itemView), LifecycleObserver, ComponentConfig {

/**
* 默认情况下 context 是 Activity Context ;
Expand All @@ -55,6 +55,7 @@ abstract class Component<T>(itemView: View) : RecyclerView.ViewHolder(itemView),
}

/**
* 执行数据绑定,处理业务逻辑
*
* @param model The model that you need to bind.
* @param position position
Expand All @@ -76,7 +77,7 @@ abstract class Component<T>(itemView: View) : RecyclerView.ViewHolder(itemView),
*
* @see onBind
*/
abstract fun onBind(model: T)
open fun onBind(model: T) {}

protected fun <V : View> findViewById(@IdRes viewId: Int): V {
return itemView.findViewById<View>(viewId) as V
Expand Down
1 change: 0 additions & 1 deletion flap/src/main/java/me/yifeiyuan/flap/Flap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import me.yifeiyuan.flap.delegate.AdapterDelegate
import me.yifeiyuan.flap.delegate.AdapterDelegateManager
import me.yifeiyuan.flap.delegate.FallbackAdapterDelegate
import me.yifeiyuan.flap.delegate.IAdapterDelegateManager
import me.yifeiyuan.flap.hook.AdapterHook
import me.yifeiyuan.flap.hook.AdapterHookManager
import me.yifeiyuan.flap.hook.IAdapterHookManager
import me.yifeiyuan.flap.pool.ComponentPool
Expand Down
1 change: 0 additions & 1 deletion flap/src/main/java/me/yifeiyuan/flap/FlapAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ open class FlapAdapter : RecyclerView.Adapter<Component<*>>(), IAdapterHookManag
adapterHooks.addAll(Flap.adapterHooks)
adapterDelegates.addAll(Flap.adapterDelegates)
adapterServices.putAll(Flap.adapterServices)
namedAdapterServices.putAll(Flap.namedAdapterServices)

Flap.globalDefaultAdapterDelegate?.let {
defaultAdapterDelegate = it
Expand Down
29 changes: 0 additions & 29 deletions flap/src/main/java/me/yifeiyuan/flap/IRegistry.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,15 @@ import java.lang.reflect.ParameterizedType

/**
*
* A delegate class that delegates some methods of FlapAdapter.
* AdapterDelegate 是一个代理类,负责代理 Adapter 的部分方法。
*
* AdapterDelegate 是 FlapAdapter 的代理类,负责代理 Adapter 的部分方法。
* 当一个 AdapterDelegate.delegate(model) 返回 true 表示它负责接手这个数据模型。
* 每个 model 都应该有一个对应的 AdapterDelegate,当一个 AdapterDelegate.delegate(model) 返回 true 表示它负责代理这个 model。
* 一般一个 AdapterDelegate 代理一个类型的 Model,是一对一的关系,这样更加解耦。
* 这样可以解耦 Adapter 对不同类型的 Model 的处理。
*
* 如果一个 model 没有任何 AdapterDelegate 代理,那么 FallbackAdapterDelegate 会兜底处理
* @see FallbackAdapterDelegate
*
*
* Created by 程序亦非猿 on 2021/9/22.
*
* Flap Github: <a>https://github.com/AlanCheen/Flap</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,41 @@ package me.yifeiyuan.flap.delegate
import androidx.annotation.NonNull

/**
* Created by 程序亦非猿 on 2022/9/5.
* AdapterDelegate 管理者的抽象
*
* @see AdapterDelegateManager
*
* Created by 程序亦非猿 on 2022/9/5.
* @since 3.1.0
*/
interface IAdapterDelegateManager {

val adapterDelegates: MutableList<AdapterDelegate<*,*>>

/**
* 注册单个 AdapterDelegate
*/
fun registerAdapterDelegate(@NonNull adapterDelegate: AdapterDelegate<*,*>){
this.adapterDelegates.add(adapterDelegate)
}

/**
* 注册多个 AdapterDelegate
*/
fun registerAdapterDelegates(vararg adapterDelegates: AdapterDelegate<*, *>){
this.adapterDelegates.addAll(adapterDelegates)
}

/**
* 注销单个 AdapterDelegate
*/
fun unregisterAdapterDelegate(@NonNull adapterDelegate: AdapterDelegate<*,*>){
this.adapterDelegates.remove(adapterDelegate)
}

/**
* 注销所有 AdapterDelegate
*/
fun clearAdapterDelegates(){
adapterDelegates.clear()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package me.yifeiyuan.flap.delegate

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import me.yifeiyuan.flap.Component
import me.yifeiyuan.flap.FlapAdapter

/**
*
* LayoutAdapterDelegate 是为了降低创建 Component 和 自定义 AdapterDelegate 带来的使用成本而创建的。
*
* 使用 LayoutAdapterDelegate 只适用于:
* - layoutId 可以当做 itemViewType 直接用
* - 只关心 onBind 处理逻辑
*
* LayoutAdapterDelegate 的使用体验介于 自定义 AdapterDelegate 和 DSL 中间,看情况选择。
*
* @see me.yifeiyuan.flap.dsl.adapterDelegate
*
* Created by 程序亦非猿 on 2021/10/27.
* @since 3.0.0
*/
class LayoutAdapterDelegate<T>(
private var modelClass: Class<T>?,
@LayoutRes
private var layoutId: Int,
private var itemId: Long = RecyclerView.NO_ID,
private var isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == modelClass },
private var onBind: Component<T>.(model: T, position: Int, payloads: List<Any>, adapter: FlapAdapter) -> Unit,
) : AdapterDelegate<T, Component<T>> {

override fun delegate(model: Any): Boolean {
return isDelegateFor.invoke(model)
}

override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): Component<T> {
val view = inflater.inflate(viewType, parent, false)
return Component(view)
}

override fun onBindViewHolder(component: Component<*>, data: Any, position: Int, payloads: List<Any>, adapter: FlapAdapter) {
onBind.invoke(component as Component<T>, data as T, position, payloads, adapter)
}

override fun getItemViewType(model: Any): Int {
return layoutId
}

override fun getItemId(model: Any, position: Int): Long {
return itemId
}
}
35 changes: 24 additions & 11 deletions flap/src/main/java/me/yifeiyuan/flap/dsl/AdapterDelegateDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,29 @@ import me.yifeiyuan.flap.delegate.AdapterDelegate
/**
* 支持 AdapterDelegate DSL 功能
*
* @param layoutId 资源 id
* @param isDelegateFor 代理关系
* @param itemId
* @param componentInitializer 初始化 DslComponent 设置
*
* @since 3.0.9
*/
inline fun <reified T> adapterDelegate(@LayoutRes layoutId: Int, noinline isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == T::class.java }, noinline componentInitializer: DslComponent<T>.() -> Unit): DslAdapterDelegate<T> {
return DslAdapterDelegate(T::class.java, layoutId, isDelegateFor = isDelegateFor, block = componentInitializer)
inline fun <reified T> adapterDelegate(
@LayoutRes layoutId: Int,
noinline isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == T::class.java },
itemId: Long = RecyclerView.NO_ID,
noinline componentInitializer: DslComponent<T>.() -> Unit): DslAdapterDelegate<T> {
return DslAdapterDelegate(T::class.java, layoutId, itemId, isDelegateFor = isDelegateFor, block = componentInitializer)
}

/**
* Created by 程序亦非猿 on 2022/9/1.
* 支持 DSL 化的 AdapterDelegate
*
* Created by 程序亦非猿 on 2022/9/1.
* @since 3.0.9
*/
class DslAdapterDelegate<T>(
private var modelClass: Class<T>?,
private var modelClass: Class<T>,
@LayoutRes private var layoutId: Int,
private var itemId: Long = RecyclerView.NO_ID,
private var isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == modelClass },
Expand Down Expand Up @@ -103,26 +113,29 @@ class DslComponent<T>(view: View) : Component<T>(view) {
onBind?.invoke(model)
}

if (onClickListener != null && isClickable()) {
if (onClickListener != null) {
itemView.setOnClickListener {
onClickListener?.invoke(model, position, adapter)
if (isClickable()) {
onClickListener?.invoke(model, position, adapter)
}
}
} else {
itemView.setOnClickListener(null)
}

if (onLongClickListener != null && isLongClickable()) {
if (onLongClickListener != null) {
itemView.setOnLongClickListener {
onLongClickListener!!.invoke(model, position, adapter)
if (isLongClickable()) {
onLongClickListener!!.invoke(model, position, adapter)
} else {
false
}
}
} else {
itemView.setOnLongClickListener(null)
}
}

override fun onBind(model: T) {
}

override fun onViewAttachedToWindow(flapAdapter: FlapAdapter) {
super.onViewAttachedToWindow(flapAdapter)
onViewAttachedToWindow?.invoke()
Expand Down
32 changes: 10 additions & 22 deletions flap/src/main/java/me/yifeiyuan/flap/ext/ItemClicksHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,39 +34,27 @@ internal class ItemClicksHelper : RecyclerView.OnChildAttachStateChangeListener

private val internalOnClickListener = View.OnClickListener { v ->
val holder: RecyclerView.ViewHolder = recyclerView.getChildViewHolder(v)
onItemClickListener?.invoke(recyclerView, v, holder.position)
if (holder is ComponentConfig && holder.isClickable()) {
onItemClickListener?.invoke(recyclerView, v, holder.position)
}
}

private val internalOnLongClickListener = View.OnLongClickListener { v ->
val holder: RecyclerView.ViewHolder = recyclerView.getChildViewHolder(v)
onItemLongClickListener?.invoke(recyclerView, v, holder.position) ?: false
if (holder is ComponentConfig && holder.isLongClickable()) {
onItemLongClickListener?.invoke(recyclerView, v, holder.position) ?: false
} else {
false
}
}

override fun onChildViewAttachedToWindow(view: View) {
val holder: RecyclerView.ViewHolder = recyclerView.getChildViewHolder(view)

onItemClickListener?.let {
if (holder is ComponentConfig) {
if (holder.isClickable()) {
view.setOnClickListener(internalOnClickListener)
} else {
view.setOnClickListener(null)
}
} else {
view.setOnClickListener(internalOnClickListener)
}
view.setOnClickListener(internalOnClickListener)
}

onItemLongClickListener?.let {
if (holder is ComponentConfig) {
if (holder.isLongClickable()) {
view.setOnLongClickListener(internalOnLongClickListener)
} else {
view.setOnLongClickListener(null)
}
} else {
view.setOnLongClickListener(internalOnLongClickListener)
}
view.setOnLongClickListener(internalOnLongClickListener)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package me.yifeiyuan.flap.hook
import androidx.annotation.NonNull

/**
* AdapterHook 管理者的抽象
* @see AdapterHookManager
*
* Created by 程序亦非猿 on 2022/9/5.
* @since 3.1.0
*/
Expand Down
Loading

0 comments on commit 473f8e7

Please sign in to comment.