经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Kotlin入门(22)适配器的简单优化
来源:cnblogs  作者:aqi00  时间:2018/10/8 9:04:40  对本文有异议

列表视图

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

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

  1. public class PlanetJavaAdapter extends BaseAdapter {
  2. private Context mContext;
  3. private ArrayList<Planet> mPlanetList;
  4. private int mBackground;
  5.  
  6. public PlanetJavaAdapter(Context context, ArrayList<Planet> planet_list, int background) {
  7. mContext = context;
  8. mPlanetList = planet_list;
  9. mBackground = background;
  10. }
  11.  
  12. @Override
  13. public int getCount() {
  14. return mPlanetList.size();
  15. }
  16.  
  17. @Override
  18. public Object getItem(int arg0) {
  19. return mPlanetList.get(arg0);
  20. }
  21.  
  22. @Override
  23. public long getItemId(int arg0) {
  24. return arg0;
  25. }
  26.  
  27. @Override
  28. public View getView(final int position, View convertView, ViewGroup parent) {
  29. ViewHolder holder = null;
  30. if (convertView == null) {
  31. holder = new ViewHolder();
  32. convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_view, null);
  33. holder.ll_item = (LinearLayout) convertView.findViewById(R.id.ll_item);
  34. holder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);
  35. holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
  36. holder.tv_desc = (TextView) convertView.findViewById(R.id.tv_desc);
  37. convertView.setTag(holder);
  38. } else {
  39. holder = (ViewHolder) convertView.getTag();
  40. }
  41. Planet planet = mPlanetList.get(position);
  42. holder.ll_item.setBackgroundColor(mBackground);
  43. holder.iv_icon.setImageResource(planet.image);
  44. holder.tv_name.setText(planet.name);
  45. holder.tv_desc.setText(planet.desc);
  46. return convertView;
  47. }
  48.  
  49. public final class ViewHolder {
  50. public LinearLayout ll_item;
  51. public ImageView iv_icon;
  52. public TextView tv_name;
  53. public TextView tv_desc;
  54. }
  55. }

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

  1. class PlanetKotlinAdapter(private val mContext: Context, private val mPlanetList: ArrayList<Planet>, private val mBackground: Int) : BaseAdapter() {
  2.  
  3. override fun getCount(): Int {
  4. return mPlanetList.size
  5. }
  6.  
  7. override fun getItem(arg0: Int): Any {
  8. return mPlanetList[arg0]
  9. }
  10.  
  11. override fun getItemId(arg0: Int): Long {
  12. return arg0.toLong()
  13. }
  14.  
  15. override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
  16. var view = convertView
  17. var holder: ViewHolder?
  18. if (view == null) {
  19. holder = ViewHolder()
  20. view = LayoutInflater.from(mContext).inflate(R.layout.item_list_view, null)
  21. holder.ll_item = view.findViewById(R.id.ll_item) as LinearLayout
  22. holder.iv_icon = view.findViewById(R.id.iv_icon) as ImageView
  23. holder.tv_name = view.findViewById(R.id.tv_name) as TextView
  24. holder.tv_desc = view.findViewById(R.id.tv_desc) as TextView
  25. view.tag = holder
  26. } else {
  27. holder = view.tag as ViewHolder
  28. }
  29. val planet = mPlanetList[position]
  30. holder.ll_item!!.setBackgroundColor(mBackground)
  31. holder.iv_icon!!.setImageResource(planet.image)
  32. holder.tv_name!!.text = planet.name
  33. holder.tv_desc!!.text = planet.desc
  34. return view!!
  35. }
  36.  
  37. inner class ViewHolder {
  38. var ll_item: LinearLayout? = null
  39. var iv_icon: ImageView? = null
  40. var tv_name: TextView? = null
  41. var tv_desc: TextView? = null
  42. }
  43. }

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

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

  1. class PlanetListAdapter(private val context: Context, private val planetList: MutableList<Planet>, private val background: Int) : BaseAdapter() {
  2.  
  3. override fun getCount(): Int = planetList.size
  4.  
  5. override fun getItem(position: Int): Any = planetList[position]
  6.  
  7. override fun getItemId(position: Int): Long = position.toLong()
  8.  
  9. override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
  10. var view = convertView
  11. val holder: ViewHolder
  12. if (convertView == null) {
  13. view = LayoutInflater.from(context).inflate(R.layout.item_list_view, null)
  14. holder = ViewHolder()
  15. //先声明视图持有者的实例,再依次获取内部的各个控件对象
  16. holder.ll_item = view.findViewById(R.id.ll_item) as LinearLayout
  17. holder.iv_icon = view.findViewById(R.id.iv_icon) as ImageView
  18. holder.tv_name = view.findViewById(R.id.tv_name) as TextView
  19. holder.tv_desc = view.findViewById(R.id.tv_desc) as TextView
  20. view.tag = holder
  21. } else {
  22. holder = view.tag as ViewHolder
  23. }
  24. val planet = planetList[position]
  25. holder.ll_item.setBackgroundColor(background)
  26. holder.iv_icon.setImageResource(planet.image)
  27. holder.tv_name.text = planet.name
  28. holder.tv_desc.text = planet.desc
  29. return view!!
  30. }
  31.  
  32. //ViewHolder中的属性使用关键字lateinit延迟初始化
  33. inner class ViewHolder {
  34. lateinit var ll_item: LinearLayout
  35. lateinit var iv_icon: ImageView
  36. lateinit var tv_name: TextView
  37. lateinit var tv_desc: TextView
  38. }
  39. }

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

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


网格视图

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

  1. class PlanetGridAdapter(private val context: Context, private val planetList: MutableList<Planet>, private val background: Int) : BaseAdapter() {
  2.  
  3. override fun getCount(): Int = planetList.size
  4.  
  5. override fun getItem(position: Int): Any = planetList[position]
  6.  
  7. override fun getItemId(position: Int): Long = position.toLong()
  8.  
  9. override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
  10. var view = convertView
  11. val holder: ViewHolder
  12. if (view == null) {
  13. view = LayoutInflater.from(context).inflate(R.layout.item_grid_view, null)
  14. holder = ViewHolder(view)
  15. //视图持有者的内部控件对象已经在构造时一并初始化了,故这里无需再做赋值
  16. view.tag = holder
  17. } else {
  18. holder = view.tag as ViewHolder
  19. }
  20. val planet = planetList[position]
  21. holder.ll_item.setBackgroundColor(background)
  22. holder.iv_icon.setImageResource(planet.image)
  23. holder.tv_name.text = planet.name
  24. holder.tv_desc.text = planet.desc
  25. return view!!
  26. }
  27.  
  28. //ViewHolder中的属性在构造时初始化
  29. inner class ViewHolder(val view: View) {
  30. val ll_item: LinearLayout = view.findViewById(R.id.ll_item) as LinearLayout
  31. val iv_icon: ImageView = view.findViewById(R.id.iv_icon) as ImageView
  32. val tv_name: TextView = view.findViewById(R.id.tv_name) as TextView
  33. val tv_desc: TextView = view.findViewById(R.id.tv_desc) as TextView
  34. }
  35. }

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

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号