ViewBinding与Kotlin委托

2023-05-16

接上篇幅

自定义属性委托的用处很多,例如组合替代继承,给个ViewBindingFragment中的使用的例子:

委托:

/**
 * 自定义属性委托。
 *
 * <p>......。</p>
 * <ul><li></li></ul>
 * <br>
 * <strong>Time</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020/5/18 14:54<br>
 * <strong>CopyRight</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020, tt.reducto<br>
 *
 * @version  : 1.0.0
 * @author   : TT
 */

fun <T> Fragment.viewBindingByLazy(initialise: () -> T): ReadOnlyProperty<Fragment, T> =
    object : ReadOnlyProperty<Fragment, T>, DefaultLifecycleObserver {

        private var binding: T? = null

        private var viewLifecycleOwner: LifecycleOwner? = null
        private val mainHandler = Handler(Looper.getMainLooper())


        init {
            this@viewBindingByLazy
                .viewLifecycleOwnerLiveData
                .observe(this@viewBindingByLazy, Observer { newLifecycleOwner ->
                    viewLifecycleOwner
                        ?.lifecycle
                        ?.removeObserver(this)

                    viewLifecycleOwner = newLifecycleOwner.also {
                        it.lifecycle.addObserver(this)
                    }
                })
        }
        @MainThread
        override fun onDestroy(owner: LifecycleOwner) {
            super.onDestroy(owner)
            mainHandler.post {
                binding = null
            }
        }
        @MainThread
        override fun getValue(
            thisRef: Fragment,
            property: KProperty<*>
        ): T {

            return this.binding ?: initialise().also {
                this.binding = it
            }
        }
    }

使用:

class MultipleFragment : Fragment(R.layout.fragment_multiple) {
    .....
    private val binding: FragmentMultipleBinding by viewBindingByLazy {
        FragmentMultipleBinding.bind(
            requireView()
        )
    }
    .....
}

注意添加依赖:

 implementation  "androidx.lifecycle:lifecycle-common-java8:2.2.0"

那我们这里同样利用自定义属性委托让ViewHolder可以存储和检索任何类型的任何属性

by Map委托

/**
 *
 * @receiver View
 * @param id Int
 * @param initializer Function0<T>
 * @return T 存储容器-> map
 */
private inline fun <reified T> View.getOrPutTag(@IdRes id: Int, initializer: () -> T) =
    getTag(id) as? T ?: initializer().also {
        setTag(id, it)
    }
    
    
 /**
 *  
 */
private inline val BindingViewHolder<*>.propertyByMap
    get() = itemView.getOrPutTag<MutableMap<String, Any?>>(
        R.id.recyclerview_view_binding_map,
        ::mutableMapOf
    )

避免重名


/**
 * 用于ViewBinding的通用ViewHolder。
 *  
 * <p>对外提供数据与视图的匹配。</p>
 * <ul><li></li></ul>
 * <br>
 * <strong>Time</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020/5/18 12:38<br>
 * <strong>CopyRight</strong>&nbsp;&nbsp;&nbsp;&nbsp;2020, tt.reducto<br>
 *
 * @version  : 1.0.0
 * @author   : TT
 */
open class BindingViewHolder<T : ViewBinding> private constructor(val binding: T) :
    RecyclerView.ViewHolder(binding.root) {
    constructor(
        parent: ViewGroup,
        creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T
    ) : this(
        creator(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
    )

    /**
     *  委托
     *
     * @param T itemView适配数据。
     */
    class BindingDataLazy<T> : ReadWriteProperty<BindingViewHolder<*>, T> {
        override fun getValue(thisRef: BindingViewHolder<*>, property: KProperty<*>): T {
            @Suppress("UNCHECKED_CAST")
            return thisRef.propertyByMap[property.name] as T
        }

        override fun setValue(thisRef: BindingViewHolder<*>, property: KProperty<*>, value: T) {
            thisRef.propertyByMap[property.name] = value
        }
    }
}

/**
 *
 * @receiver ViewGroup
 * @param creator 布局生成器
 * @return BindingViewHolder<T>
 */
fun <T : ViewBinding> ViewGroup.getViewHolder(
    creator: (inflater: LayoutInflater, root: ViewGroup, attachToRoot: Boolean) -> T
): BindingViewHolder<T> =
    BindingViewHolder(this, creator)

/**
 *
 */
private inline val BindingViewHolder<*>.propertyByMap
    get() = itemView.getOrPutTag<MutableMap<String, Any?>>(
        R.id.recyclerview_view_binding_map,
        ::mutableMapOf
    )

/**
/**
 * getTag()内部使用 { <code>private SparseArray<Object> mKeyedTags;</code> } 进行查找
 *
 * @receiver View
 * @param id Int 保证ID唯一
 * @param initializer 容器初始化
 * @return T 存储容器-> map
 */
private inline fun <reified T> View.getOrPutTag(@IdRes id: Int, initializer: () -> T) =
    getTag(id) as? T ?: initializer().also {
        setTag(id, it)
    }

ViewPage2的Adapter写个简单item_multiple.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:card_view="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.cardview.widget.CardView
        android:id="@+id/cd_item_multi"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/pageMarginAndOffset"
        android:layout_marginRight="@dimen/pageMarginAndOffset"
        android:background="@color/cardview_light_background"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        app:cardUseCompatPadding="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        card_view:cardCornerRadius="6dp">

        <ImageView
            android:id="@+id/img_item_multi"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:scaleType="fitXY" />
    </androidx.cardview.widget.CardView>

    <TextView
        android:id="@+id/tv_item_multi"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="1111"
        android:textColor="@color/colorPrimaryDark"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/cd_item_multi" />

    <CheckBox
        android:id="@+id/ck_item_multi"
        app:layout_constraintTop_toBottomOf="@id/cd_item_multi"
        app:layout_constraintEnd_toStartOf="@+id/tv_item_multi"
        android:layout_width="48dp"
        android:layout_height="48dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

数据源:

data class Category(val name: String,val id:Int)

Fragment或者Activity中:

private var BindingViewHolder<ItemMultipleBinding>.result by BindingViewHolder.BindingDataLazy<Category>()

fun BindingViewHolder<ItemMultipleBinding>.bindView(data: Category) {
    result = data
    binding.tvItemMulti.text = result.name
    binding.imgItemMulti.load(result.id)
    binding.ckItemMulti.setOnCheckedChangeListener { buttonView, isChecked ->

    }
}

如何与Adapter结合?我们利用闭包写个简单的Adapter:

class BindingAdapter <ItemT : Any?, VH : RecyclerView.ViewHolder>(
    private val itemsData: () -> List<ItemT>,
    private val viewHolderCreator: (ViewGroup, Int) -> VH,
    private val viewHolderBinder: (holder: VH, item: ItemT, position: Int) -> Unit,
    private val itemIdFunction: ((ItemT) -> Long)? = null
) : RecyclerView.Adapter<VH>() {

    init {
        setHasStableIds(itemIdFunction != null)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH = viewHolderCreator(parent, viewType)

    override fun onBindViewHolder(holder: VH, position: Int) = viewHolderBinder(holder, itemsData()[position], position)

    override fun getItemCount(): Int = itemsData().size


    override fun getItemId(position: Int): Long =
        itemIdFunction?.invoke(itemsData()[position]) ?: super.getItemId(position)


}

fun <ItemT, VH : RecyclerView.ViewHolder> adapterByBinding(
    itemsData: () -> List<ItemT>,
    viewHolderCreator: (ViewGroup, Int) -> VH,
    viewHolderBinder: (holder: VH, item: ItemT, position: Int) -> Unit,
    itemIdFunction: ((ItemT) -> Long)? = null
): RecyclerView.Adapter<VH> =
    BindingAdapter(
        itemsData,
        viewHolderCreator,
        viewHolderBinder,
        itemIdFunction
    )

如何使用?

 binding.vpMultiple.apply {
            // 横向滑动
            orientation = ViewPager2.ORIENTATION_HORIZONTAL
            //
            clipToPadding = false
            //
            clipChildren = false
            // 可见页面数
            offscreenPageLimit = 3
                        
            adapter = adapterByBinding(
                itemsData = Test()::mCategoryList,
                viewHolderCreator = { viewGroup, _ ->
                    viewGroup.getViewHolder(ItemMultipleBinding::inflate).apply {
                        itemView.setOnClickListener {
                            toast("${this.adapterPosition}")
                        }
                    }
                },
                viewHolderBinder = { viewHolder, data, _ ->
                    viewHolder.bindView(data)
                } 
            )
	
      .......     
  }
  
  
private var BindingViewHolder<ItemMultipleBinding>.result by BindingViewHolder.BindingDataLazy<Category>()

fun BindingViewHolder<ItemMultipleBinding>.bindView(data: Category) {
    result = data
    binding.tvItemMulti.text = result.name
    binding.imgItemMulti.load(result.id)
    binding.ckItemMulti.setOnCheckedChangeListener { buttonView, isChecked ->

    }
}

class Test {

    val mCategoryList: MutableList<Category> = mutableListOf()
    private val ids = arrayListOf(
        R.mipmap.image1,
        R.mipmap.image2,
        R.mipmap.image3,
        R.mipmap.image4,
        R.mipmap.image5
    )

    init {
        for (i in ids.indices) {
            mCategoryList.add(i, Category("page_${i + 1}", ids[i]))
        }

    }
}

如果需要支持DiffUtil或者多ItemType等等其他也可以加上 ,不过有了MergeAdapter更方便

参考:

ViewBindingPropertyDelegate

view-binding-internals

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ViewBinding与Kotlin委托 的相关文章

  • 如何从屏幕上的任意位置拖动底页?

    我正在尝试实现类似 YouTube 的体验 并允许用户从屏幕中的任何位置拖动底部工作表 我尝试了很多解决方案没有任何帮助 我终于从这个解决方案中找到了解决方案在外部区域达到阈值后允许 BottomSheet 向上滑动 https stack
  • 第一次调用后 LiveData 未观察到

    我实现了 LiveData 和 ViewModel 来模仿 AsyncTaskLoader 我从 DCIM 中的相机目录加载文件名 然后附加一个 fileObserver 来观察删除文件 图片 时的情况 然后回调告诉 LiveData 在发
  • 在 Kotlin 中将 Dp 转换为 Px - 这种转换永远不会成功

    我在使用 Kotlin 编码时遇到了问题 我复制粘贴了一个java代码示例 https stackoverflow com a 9685690 6818446将 DP 转换为像素 以便将其作为以编程方式设置填充的参数 我原本期望 IDE 能
  • Kotlin 中是否可以为 mutableStateOf() 提供自定义设置器

    我想在每次设置某个状态后两秒进行一些操作 viewModel 内的代码 var isLoading mutableStateOf LoadingState NONE set value Timber d Custom Setter Not
  • 在 Fragment 和 Activity 之外通过 Hilt 进行现场注入

    我想知道是否可以在片段或活动之外使用字段注入 我知道我可以使用构造函数注入 但是我想知道是否也可以使用字段注入 我认为 Dagger 是可能的 当我尝试用注入的东西做某事时yclass字段我收到此错误 lateinit property y
  • Typealias - 在 Kotlin 中组合多个接口

    我对 Kotlin 中的协议组合有点生疏 我只想通过声明自定义来组合多个接口typealias This doesn t work typealias MyType ReadableInterface WritableInterface 有
  • 如何在 IntelliJ 中快速文档中换行文本?

    Ctrl Q 快速文档 后我看不到所有文本 我必须水平滚动才能看到所有内容 我无法使编辑器适合窗口 更新 问题只是符号定义 即 第一行 描述 块文本 正确换行 实际上 我不想调整此窗口的大小 因为我更喜欢将其保留在相同的位置以便快速参考 我
  • LiveData无法观察到变化

    我正在更新一个ViewModel 中 DialogFragment 的 LiveData 值 但无法获取Fragment中的值 视图模型 class OtpViewModel private val otpUseCase OtpUseCas
  • @Service 中带有 Kotlin 的 Spring Boot @Autowired 始终为 null

    目前 我尝试使用 Kotlin 重写我的 Java Spring Boot 应用程序 我遇到了一个问题 在我所有的类中都用 Service依赖注入无法正常工作 所有实例都null 这是一个例子 Service Transactional o
  • 如何使用 Kotlin 在 ListAdapter 中使用 Filterable?

    我会用一个SearchView过滤我的RecyclerView 在 stackoverflow 和其他网站上我发现只是使用的示例Filterable与 Java 和RecyclerView Adapter当我使用时ListAdapter 所
  • 如何从 Java 类调用 Kotlin 类

    我需要将意图从 java 活动传递到 Kotlin 活动 Java活动ProfileActivity class Intent selectGameIntent new Intent ProfileActivity this kotlin
  • Kotlin 协程阻塞 Android 中的主线程

    我是 Kotlin 和协程的新手 我有一个fun在我的活动及其内部 检查User用户名和密码 如果为真 则返回Users object 一切都好 但是当我按下按钮时 我的活动被阻止并等待响应Users login 我用这个有趣的 priva
  • Kotlin 中的单例类

    我想知道如何在 Kotlin 中创建一个单例类 以便我的 Util 类在每次应用程序执行时仅实例化一次 但是 当我将 Java 类转换为 kotlin 时 生成了以下代码 它是否正确 companion object private var
  • 从 DecorView@2da7146[MyActivity] 中找不到 ViewTreeLifecycleOwner

    从撰写更新后alpha 11 to alpha 12 or beta 01 每当我打开具有撰写视图的活动或片段时 我都会遇到此崩溃 我在用AppCompatActivity它实现了LifecycleOwner 所以这非常奇怪 java la
  • 为什么线程比协程表现出更好的性能?

    我编写了 3 个简单的程序来测试协程相对于线程的性能优势 每个程序都会执行许多常见的简单计算 所有程序都彼此分开运行 除了执行时间之外 我还通过以下方式测量了 CPU 使用率Visual VMIDE 插件 第一个程序使用以下方法进行所有计算
  • 在 Kotlin 中创建 Spring 的 ParameterizedTypeReference 实例

    我正在尝试学习 Kotlin 并测试它如何与 Spring Boot 配合使用 我的应用程序使用 mongo 数据库来存储数据 并且我有用于检索数据的 Jersey 资源 我正在使用它进行测试spring boot test and Res
  • Moshi 无法解析 nullable

    你好 希望你能帮助我 使用 kotlin Retrofit2 moshi 我从 https api spacexdata com v3 launches 获取数据并解析它 一切都很顺利 我得到的属性如下 flight number miss
  • 用于 Kotlin 中单元测试的 BuildConfigField 模拟

    我正在尝试尽可能多地涵盖Kotlin Android 库我遇到了有关自定义 BuildConfig 变量的问题 更广为人知的是buildConfigField 我想模拟这个变量来测试两者true and false values 从 Gra
  • Jetpack Compose:制作全屏(绝对定位)组件

    我怎样才能在全屏渲染树的深处制作一个可组合的 类似于Dialog可组合作品 例如 当用户单击图像时 它会显示该图像的全屏预览 而无需更改当前路线 我可以用 CSS 来做到这一点position absolute or position fi
  • Kotlin 和惯用的书写方式,基于可变值“如果不为空,则...”

    假设我们有这样的代码 class QuickExample fun function argument SomeOtherClass if argument mutableProperty null doSomething argument

随机推荐

  • 如何利用大数据进行价值兑现才是正经事(虎嗅网)

    如果有一天你可以预测未来 xff0c 你要做的第一件事情是什么 xff1f 买彩票 xff1f 第二件 第三件事情呢 xff1f 先卖个关子 xff0c 我们后面再说这件事情 大数据是个产业 xff0c 广义上指的是在这个信息过载时代围绕着
  • 络达开发---串口日志&日志过滤

    平台 xff1a AB1565M SDK版本 xff1a V2 11 0 开发环境 xff1a windows10 采用官方ATK中的日志工具可以通过硬件物理串口来实时查看芯片中软件的运行日志 如下图所示 xff0c 其实该ATK工具为一若
  • 多图震撼!数字的未来,2013报告(虎嗅网)

    新媒体 完爆 旧媒体 从市值上来看 xff0c 以苹果 谷歌 亚马逊 Facebook 雅虎等为首的新媒体公司市值已超过1万亿美元 xff0c 而以迪斯尼 Comcast 时代华纳 Viacom CBS 新闻集团 21世纪福克斯等为首的旧媒
  • 网页采集器-八爪鱼采集器

    八爪鱼采集器下载地址 xff1a http www bazhuayu cc download 八爪鱼采集器的注册地址 xff1a http www bazhuayu cc signup id 61 0e492e9c 6d80 4c2a a2
  • 考研书单与技巧

    书尽量在网上搞活动时买正版的 xff0c 这样也不贵 每科研究透一到两本书 xff0c 不要贪多 xff01 1 英语 xff1a xff08 积累的过程 xff0c 可以现在开始 xff0c 正好把六级过了 xff09 xff08 看好所
  • Tomcat 9安装配置教程

    首先 xff0c 先去这个网址下载Tomcat 9 http tomcat apache org 然后根据自己的电脑系统版本去下载相对应的文件 xff01 我的系统版本是 Windows 10 64位 xff0c 所以我选择 34 64 b
  • Ef Core 使用Entity方式配置外键

    一 Ef Core 使用Entry方式配置外键 当一个表中有多个外键指向同一个表时候 xff0c 需要使用Entity方式执行具体外键约束名称 xff0c 使用方法如下 xff1a protected override void OnMod
  • Python安装后目录在哪儿_如何查看Python的安装目录

    一 Python的安装录 当前安装版本为 xff1a python 3 10 4 1 在安装python的时候可以看到安装目录 xff0c 可以修改安装目录 xff1a 2 windows系统下64位安装目录如下 xff1a 跟其他软件不太
  • linux下完全删除mysql

    linux下完全删除mysql 查询所有mysql的服务并停止所有mysql服务 查询自启服务列表 span class token function chkconfig span list 执行结果 mysqld 0 关闭 1 关闭 2
  • linux安装mysql-8.0.19-最全讲解

    linux离线方式安装mysql 8 0 19 下载mysql包 注意 在MySQL Server 8 0 12中 xff0c 压缩算法从Gzip更改为XZ xff1b 并且通用二进制文件的文件扩展名从 tar gz更改为 tar xz 安
  • Windows环境下给oracle打补丁详细教程

    环境检查 1 检查oracle数据库版本 xff0c 安装前检查 xff1a 确保Oracle数据库安装与您正在应用此修补程序的版本相同 C WINDOWS system32 span class token operator gt spa
  • CentOS7安装docker

    安装docker docker官网 xff1a http www docker com docker中文网站 xff1a https www docker com 仓库 Docker Hub官网 https hub docker com 官
  • springCloud---替换注册中心eureka为nacos后 @Value 获取不到值

    在替换为nacos后 xff0c 启动时出现如下错误 xff1a 64 Value 获取不到值 xff0c 无法解析 test 占位符 此时就会进行各种百度 xff0c google xff0c 查文档 xff01 而我遇到的问题出现在 x
  • Linux防火墙及端口策略设置(iptables&firewalld)

    防火墙设置 service firewalld stop service firewalld start service firewall restart service firewalld status 开机禁用 xff1a system
  • windows环境下安装MySQL8.0.19

    安装过程中可能提示缺少xx dll文件 xff0c 建议首先安装微软常用运行库集合 下载地址 1 下载MySQL8压缩包 xff0c 进行解压 xff0c 在根目录下创建data文件夹 xff0c 创建my ini配置文件 2 在配置文件中
  • 解决多个tomcat端口冲突

    我在一台PC机上安装了两个tomcat xff0c 需要同时启动 xff0c 每个tomcat上跑一个程序 xff0c 但是现在提示端口号冲突 xff0c 需要手动更改 需要修改三个地方 xff1a 1 首先 xff1a 在Tomcat的根
  • Android JsonArray移除里面的一个对象

    remove是在 API level 19 时加入的 xff0c 在低版本调用时会出现错误 这里用反射实现了兼容老版本的方法 public void Remove int positon throws Exception if positi
  • libgtk2.0-dev 安装broken packages问题解决方法

    在安装opencv的过程中 xff0c 需要安装到 libgtk2 0 dev xff0c 安装过程中可能会出现broken packages的问题 输出信息如下 xff1a apt get install libgtk2 0 dev Re
  • vue 代码格式化(VS code)

    1 安装了vetur xff08 Vue tooling for VS Code xff09 扩展插件 在扩展中搜索vetur xff0c 然后点击安装 2 直接 xff08 或者 选中你想格式化的代码 xff09 xff0c 右键 xff
  • ViewBinding与Kotlin委托

    接上篇幅 自定义属性委托的用处很多 xff0c 例如组合替代继承 xff0c 给个ViewBinding在Fragment中的使用的例子 xff1a 委托 自定义属性委托 lt p gt lt p gt lt ul gt lt li gt