【FirstKotlinApp】使用Kotllin封装Adapter时ViewHolder的一点技巧

一路做一路学,感谢互联网给我们提供的资源

学习Android开发的小伙伴们都知道,List(列表)、Grid(表格)、RecycleView等使用的是适配器模式,即需要一个Adapter为视图提供数据。而在使用Adapter的时候,getView方法内含了复用机制,即子视图的对象会循环利用,以免造成OOM。

复用机制在早期的List等中,需要开发者手动实现,其示例如下:

此处的itemView是一个上图标下文字的布局。

    inner class GridItemViewHolder(val root: View) {
        var tvText: TextView
        var ivIcon: ImageView
        val views = SparseArray<View>()//Android推荐使用的集合框架,键值对形式存储

        init {//作为主构造函数的方法体,且一定是第一个调用
            tvText = getTextView(R.id.id_tv_grid_text)
            ivIcon = getImageView(R.id.id_iv_grid_icon)
        }

        /**使用泛型优化控件的获取**/
        fun <T : View> getView(id: Int): T {
            var view = views[id]
            if (view == null) {
                view = root.findViewById(id)
                views.put(id, view)
            }
            return view as T
        }
        //提供的附加方法
        fun getTextView(id: Int): TextView = getView(id)
        fun getImageView(id: Int): ImageView = getView(id)

        fun setText(id: Int, text: String) {
            getTextView(id).text = text
        }

        fun setIcon(id: Int, resId: Int) {
            getImageView(id).setImageResource(resId)
        }

    }

ViewHolder的存在是利用复用机制可循环使用对象,让其携带(通过tag属性)一个Holder对象,Holder持有子视图控件的引用,从而循环利用子视图的控件,避免重复创建对象,拉高内存占用。

但为什么需要一个外部Holder对象呢?还有没有别的方法?我想,这些肯定是有人思考过的。它的根本就在于,复用机制需要的是子视图View的对象,而这个对象本身是无法改变的,只得在其拥有的Field、方法等上下手,其tag属性终被程序员发现,这才有了ViewHolder。

这种做法显然有它的限制,那就是Java语法的限制,它规定了在语言中的一切行为,才使得无法在对象本身开刀。那么,Kotlin呢?对了,Kotlin有扩展方法,可以使我们可以忘记ViewHolder,让复用的视图自己包含缓存方法,简化代码,So Happy。

代码如下:

fun <T : View> View.findViewOften(viewId: Int): T {
    var viewHolder: SparseArray<View> = 
        tag as? SparseArray<View> ?: SparseArray()
    tag = viewHolder
    var childView: View? = viewHolder.get(viewId)
    if (null == childView) {
        childView = findViewById(viewId)
        viewHolder.put(viewId, childView)
    }
    return childView as T
}

给View扩展一个方法,内部实现与ViewHolder相同的缓存机制,这可能有人会问了,View本身如何获取?Kotlin的扩展方法需要指定接收者类型,使用this关键字即可在方法内获取,在convertView(复用的子View对象)调用该方法是,方法体便可通过this操作接收者对象,因为复用的是同一个对象,那么缓存机制便因此而实现了。

说到这里,另一个问题随之出现,Google自5.0之后推出的RecycleView自带ViewHolder的实现,且必须传入,那这个方法如何去做呢?

当然会有:

class MyViewHolder(val convertView: View) : RecyclerView.ViewHolder(convertView) {
        fun <T : View> findView(viewId: Int): T {
        return convertView.findViewOften(viewId)
    }
}

即使必须要ViewHolder,最终也是逃离不掉convertView的,通过ViewHolder下手即可。

欢迎提出更好的实现方案。

参考来源:http://www.jianshu.com/p/819ba04955a8




额外内容:

获取被声明在arrays数组中的资源ID
1.val typedArray = resources.obtainTypedArray(R.array.home_bar_icon)
2.val itemInfo = ItemInfo(stringArray[i], typedArray.getResourceId(i, 0))

通过对该数组获取typedArray,再获取resourceId

今天就写到这里,最近尽可能的在过好自己的生活,说到的要做到,不过放假难免有其他事,都做好互不影响并尽可能弥补自己吧
写于:2017年7月30日 20:55

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值