Kotlin入门(22)适配器的简单优化

列表视图

为实现各种排列组合类的视图(包括但不限于Spinner、ListView、GridView等等),Android提供了五花八门的适配器用于组装某个规格的数据,常见的适配器有:数组适配器ArrayAdapter、简单适配器SimpleAdapter、基本适配器BaseAdapter、翻页适配器PagerAdapter。适配器的种类虽多,却个个都不好用,以数组适配器为例,它与Spinner配合实现下拉框效果,其实现代码纷复繁杂,一直为人所诟病。故而在下拉框一小节之中,干脆把ArrayAdapter连同Spinner一股脑都摒弃了,取而代之的是Kotlin扩展函数selector。
到了列表视图ListView这里,与之搭档的一般是基本适配器BaseAdapter,这个BaseAdapter更不简单,基于它的列表适配器得重写好几个方法,还有那个想让初学者撞墙的ViewHolder。总之,每当要实现类似新闻列表、商品列表之类的页面,一想到这个难缠的BaseAdapter,心里便发怵。譬如下图所示的六大行星的说明列表,左侧是图标,右边为文字说明,很普通的一个页面。

可是这个行星列表页面,倘若使用Java编码,就得书写下面一大段长长的代码:

public class PlanetJavaAdapter extends BaseAdapter  {
    private Context mContext;
    private ArrayList<Planet> mPlanetList;
    private int mBackground;

    public PlanetJavaAdapter(Context context, ArrayList<Planet> planet_list, int background) {
        mContext = context;
        mPlanetList = planet_list;
        mBackground = background;
    }

    @Override
    public int getCount() {
        return mPlanetList.size();
    }

    @Override
    public Object getItem(int arg0) {
        return mPlanetList.get(arg0);
    }

    @Override
    public long getItemId(int arg0) {
        return arg0;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_view, null);
            holder.ll_item = (LinearLayout) convertView.findViewById(R.id.ll_item);
            holder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);
            holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
            holder.tv_desc = (TextView) convertView.findViewById(R.id.tv_desc);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        Planet planet = mPlanetList.get(position);
        holder.ll_item.setBackgroundColor(mBackground);
        holder.iv_icon.setImageResource(planet.image);
        holder.tv_name.setText(planet.name);
        holder.tv_desc.setText(planet.desc);
        return convertView;
    }

    public final class ViewHolder {
        public LinearLayout ll_item;
        public ImageView iv_icon;
        public TextView tv_name;
        public TextView tv_desc;
    }
}

上面Java实现的适配器类PlanetJavaAdapter,果真又冗长又晦涩,然而这段代码模版基本上是列表视图的标配,只要用Java编码,就必须依样画瓢。如果用Kotlin实现这个适配器类会是怎样的呢?马上利用Android Studio把上述Java代码转换为Kotlin编码,转换后的Kotlin代码类似以下片段:

class PlanetKotlinAdapter(private val mContext: Context, private val mPlanetList: ArrayList<Planet>, private val mBackground: Int) : BaseAdapter() {

    override fun getCount(): Int {
        return mPlanetList.size
    }

    override fun getItem(arg0: Int): Any {
        return mPlanetList[arg0]
    }

    override fun getItemId(arg0: Int): Long {
        return arg0.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        var view = convertView
        var holder: ViewHolder?
        if (view == null) {
            holder = ViewHolder()
            view = LayoutInflater.from(mContext).inflate(R.layout.item_list_view, null)
            holder.ll_item = view.findViewById(R.id.ll_item) as LinearLayout
            holder.iv_icon = view.findViewById(R.id.iv_icon) as ImageView
            holder.tv_name = view.findViewById(R.id.tv_name) as TextView
            holder.tv_desc = view.findViewById(R.id.tv_desc) as TextView
            view.tag = holder
        } else {
            holder = view.tag as ViewHolder
        }
        val planet = mPlanetList[position]
        holder.ll_item!!.setBackgroundColor(mBackground)
        holder.iv_icon!!.setImageResource(planet.image)
        holder.tv_name!!.text = planet.name
        holder.tv_desc!!.text = planet.desc
        return view!!
    }

    inner class ViewHolder {
        var ll_item: LinearLayout? = null
        var iv_icon: ImageView? = null
        var tv_name: TextView? = null
        var tv_desc: TextView? = null
    }
}

相比之下,直接转换得来的Kotlin代码,最大的改进是把构造函数及初始化参数放到了第一行,其它地方未有明显优化。眼瞅着没多大改善,反而因为Kotlin的空安全机制,平白无故多了好些问号和双感叹号,可谓得不偿失。问题出在Kotlin要求每个变量都要初始化上面,视图持有者ViewHolder作为一个内部类,目前虽然无法直接对控件对象赋值,但是从代码逻辑可以看出先从布局文件获取控件,然后才会调用各种设置方法。这意味着,上面的控件对象必定是先获得实例,在它们被使用的时候肯定是非空的,因此完全可以告诉编译器,这些控件对象一定会在使用前赋值,编译器您老就高抬贵手,睁一只眼闭一只眼放行好了。

毋庸置疑,该想法合情合理,Kotlin正好提供了这种后门,它便是关键字lateinit。lateinit的意思是延迟初始化,它放在var或者val前面,表示被修饰的变量属于延迟初始化属性,即使没有初始化也仍然是非空的。如此一来,这些控件在声明之时无需赋空值,在使用的时候也不必画蛇添足加上两个感叹号了。根据新来的lateinit修改前面的Kotlin适配器,改写后的Kotlin代码如下所示:

class PlanetListAdapter(private val context: Context, private val planetList: MutableList<Planet>, private val background: Int) : BaseAdapter() {

    override fun getCount(): Int = planetList.size

    override fun getItem(position: Int): Any = planetList[position]

    override fun getItemId(position: Int): Long = position.toLong()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        var view = convertView
        val holder: ViewHolder
        if (convertView == null) {
            view = LayoutInflater.from(context).inflate(R.layout.item_list_view, null)
            holder = ViewHolder()
            //先声明视图持有者的实例,再依次获取内部的各个控件对象
            holder.ll_item = view.findViewById(R.id.ll_item) as LinearLayout
            holder.iv_icon = view.findViewById(R.id.iv_icon) as ImageView
            holder.tv_name = view.findViewById(R.id.tv_name) as TextView
            holder.tv_desc = view.findViewById(R.id.tv_desc) as TextView
            view.tag = holder
        } else {
            holder = view.tag as ViewHolder
        }
        val planet = planetList[position]
        holder.ll_item.setBackgroundColor(background)
        holder.iv_icon.setImageResource(planet.image)
        holder.tv_name.text = planet.name
        holder.tv_desc.text = planet.desc
        return view!!
    }

    //ViewHolder中的属性使用关键字lateinit延迟初始化
    inner class ViewHolder {
        lateinit var ll_item: LinearLayout
        lateinit var iv_icon: ImageView
        lateinit var tv_name: TextView
        lateinit var tv_desc: TextView
    }
}

以上的Kotlin代码总算有点模样了,虽然总体代码还不够精简,但是至少清晰明了,其中主要运用了Kotlin的以下三项技术:

1、构造函数和初始化参数放在类定义的首行,无需单独构造,也无需手工初始化;
2、像getCount、getItem、getItemId这三个函数,仅仅返回简单运算的数值,可以直接用等号取代大括号;
3、对于视图持有者的内部控件,在变量名称前面添加lateinit,表示该属性为延迟初始化属性;

网格视图

在前面的列表视图一小节中,给出了Kotlin改写后的适配器类,通过关键字lateinit固然避免了麻烦的空校验,可是控件对象迟早要初始化的呀,晚赋值不如早赋值。翻到前面PlanetListAdapter的实现代码,认真观察发现控件对象的获取其实依赖于布局文件的视图对象view,既然如此,不妨把该视图对象作为ViewHolder的构造参数传过去,使得视图持有者在构造之时便能一块初始化内部控件。据此改写后的Kotlin适配器代码如下所示:

class PlanetGridAdapter(private val context: Context, private val planetList: MutableList<Planet>, private val background: Int) : BaseAdapter() {

    override fun getCount(): Int = planetList.size

    override fun getItem(position: Int): Any = planetList[position]

    override fun getItemId(position: Int): Long = position.toLong()

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        var view = convertView
        val holder: ViewHolder
        if (view == null) {
            view = LayoutInflater.from(context).inflate(R.layout.item_grid_view, null)
            holder = ViewHolder(view)
            //视图持有者的内部控件对象已经在构造时一并初始化了,故这里无需再做赋值
            view.tag = holder
        } else {
            holder = view.tag as ViewHolder
        }
        val planet = planetList[position]
        holder.ll_item.setBackgroundColor(background)
        holder.iv_icon.setImageResource(planet.image)
        holder.tv_name.text = planet.name
        holder.tv_desc.text = planet.desc
        return view!!
    }

    //ViewHolder中的属性在构造时初始化
    inner class ViewHolder(val view: View) {
        val ll_item: LinearLayout = view.findViewById(R.id.ll_item) as LinearLayout
        val iv_icon: ImageView = view.findViewById(R.id.iv_icon) as ImageView
        val tv_name: TextView = view.findViewById(R.id.tv_name) as TextView
        val tv_desc: TextView = view.findViewById(R.id.tv_desc) as TextView
    }
}

利用该适配器运行测试应用,得到的网格效果如下图所示,可见与Java代码的运行结果完全一致。

至此基于BaseAdapter的Kotlin列表适配器告一段落,上述的适配器代码模版,同时适用于列表视图ListView与网格视图GridView。

原文地址:https://www.cnblogs.com/aqi00/p/9747494.html

时间: 2024-10-13 01:18:53

Kotlin入门(22)适配器的简单优化的相关文章

Kotlin入门(23)适配器的进阶表达

前面在介绍列表视图和网格视图时,它们的适配器代码都存在视图持有者ViewHolder,因为Android对列表类视图提供了回收机制,如果某些列表项在屏幕上看不到了,则系统会自动回收相应的视图对象.随着用户的下拉或者上拉手势,已经被回收的列表项要重新加载到界面上,倘若每次加载都得从头创建视图对象,势必增加了系统的资源开销.所以ViewHolder便应运而生,它在列表项首次初始化时,就将其视图对象保存起来,后面再次加载该视图时,即可直接从持有者处获得先前的视图对象,从而减少了系统开销,提高了系统的运

linux小白-基础篇-系统简单优化

作为一个刚刚接触Linux的小白,通过一段时间的学习,将我自己的部分笔记整理后拿出来,请求大家指点:因为是"0"基础学起很多地方了解的都不够深入,希望各位前辈能够指点一下:予人玫瑰,手留余香,谢谢! 因为条件有限环境是用VM搭建起来的,下文主要是根据"老男孩教育视频"中的内容整理出来的系统简单优化,希望各位前辈看过以后可以给出建议: 已学习进步为目的,不喜勿喷!谢谢! 系统简单优化    命令 一.关闭selinux                         

【Android UI】ListView的使用和简单优化

ListView是每个app中都要使用的,所以今天我来总结下ListView的使用和一些简单的优化. 先看下运行效果: 一.创建数据库 为了模拟数据,这里将数据保存数据库中,顺便复习一下SQLite的知识,将数据保存到数据库的好处就是很容易模拟网络请求的延迟. 1.创建数据库打开帮助类BlackNumberDBOpenHelper,它继承自SQLiteOpenHelper package com.yzx.listviewdemo.db; import android.content.Contex

Kotlin入门(32)网络接口访问

手机上的资源毕竟有限,为了获取更丰富的信息,就得到辽阔的互联网大海上冲浪.对于App自身,也要经常与服务器交互,以便获取最新的数据显示到界面上.这个客户端与服务端之间的信息交互,基本使用HTTP协议进行通信,即App访问服务器的HTTP接口来传输数据.HTTP接口调用在Java代码中可不是一个轻松的活,开发者若用最基础的HttpURLConnection来编码的话,至少要考虑以下场景的处理:1.HTTP的请求方式是什么,是GET还是POST还是PUT还是DELETE?2.HTTP的连接超时时间是

编译安装nginx及简单优化配置

一.背景 使用源码包安装lnmp架构及简单的优化配置 二.实验环境 rhel6.5 三.安装过程 1.nginx(提前装好gcc等编译器) (1) 下载源码包  http://nginx.org/ (2) tar -zxf nginx-1.8.1.tar.gz (3) cd nginx-1.8.1 vim auto/cc/gcc 修改第179行 (将本行注释,意为取消debug模式,) (4) ./configure --prefix=/usr/local/lnmp/nginx \ --with

python网络爬虫入门(一)——简单的博客爬虫

最近,为了微信公众号的图文,上网疯狂的收集和看了一些有深度的新闻和有趣的引人深思的文字评论,并选择了几篇极品发布出去.但感觉一篇一篇的看实在是麻烦死了.想找一个简单的解决办法,看能不能自动把网上的资料收集起来,然后自己用统一筛选.不巧,最近准备学习下网络爬虫相关知识,于是按照网上的教程自己学着试写了一个小小的爬虫,嘻嘻,是用来爬韩寒博客的. 先把完整的代码贴上来,如果朋友们需要试验下,请先安装python相关环境,然后在复制粘贴保存,再按F5运行. #导入urllib库,python访问网页必须

完整构建LNMP,简单优化实现超高并发访问!

LNMP简介 1.什么是LNMP? 也许大家对LAMP比较熟悉,LAMP代表Linux下Apache.MySQL.PHP这种网站服务器架构:同上LNMP代表的就是Linux下Nginx.MySQL.PHP这种网站服务器架构. 2.我们为什么要采用LNMP这种架构呢? 使用Linux.PHP外加MySQL的优点我就无需多说相信大家也都很明了. Nginx是一个轻量级且高效的Linux下的Web服务器软件,最初是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的反向代

C# 实现寻峰算法的简单优化(包含边峰,最小峰值,峰距)

原文:C# 实现寻峰算法的简单优化(包含边峰,最小峰值,峰距) 核心寻峰算法的原理参考Ronny,链接:投影曲线的波峰查找, C#翻译原理代码参考sowhat4999,链接:C#翻译Matlab中findpeaks方法 前人种树,后人乘凉.感谢原作者详细的解释说明. 这里先把翻译代码贴一下(略微的修改了sowhat4999代码中的几个参数) //调用方法 List<double> data = new List<double>{25, 8, 15, 5, 6, 10, 10, 3,

mysql的简单优化【简单易学】

1.选取最适用的字段属性: 表字段尽量设小,不要给数据库增加没必要的空间:如:值为'01'.'02',给char(2)即可: 2.使用连接(JOIN)来代替子查询(Sub-Queries): 使用join是因为MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作. 3.建立索引: 一般用在JOIN,WHERE判断,ORDERBY排序字段上. 4.避免使用select * from 表名: 需要什么字段就写什么字段. 5.尽量避免在WHERE中!=或者<>,否则放弃使用索引