android ListView包含Checkbox滑动时状态改变

小编 2026-07-02 阅读:1578 评论:0
题外话:在xamarin android的开发中基本上所有人都会遇到这个小小的坎,的确有点麻烦,...

题外话:

在xamarin android的开发中基本上所有人都会遇到这个小小的坎,的确有点麻烦,当时我也折腾了好一半天,如果你能看到这篇博客,说明你和我当初也是一样的焦灼,如果你想解决掉这个小小的坎,那么不要着急,一步一步来。我之前写过一篇Xamarin Android ListView简单的例子,例子入门级别的,Xamarin Android ListView简单用法,你也可以下载这个Demo ListView Demo下载结合下面的代码,那么你的问题马上就能解决。

Adapter中的GetView没有经过优化的代码:

为了使问题更直观,更便于理解,我们先来看看adapter中的重写方法GetView并没有经过优化的代码。我也看过网上很多博客,都是直接ListView优化后的代码来介绍解决这个问题,我觉得新手并不一定能够很懂易就能理解。
<pre name="code" class="csharp">using System;using System.Collections.Generic;using System.Linq;using System.Text;using Android.App;using Android.Content;using Android.OS;using Android.Runtime;using Android.Views;using Android.Widget;using Java.Lang;using DeBug = System.Diagnostics.Debug;namespace DrawerLayout.Adapter{    public class News {        public int Pv { get; set; }        public string Title { get; set; }        public News(string  title,int Pv)        {            this.Title = title;            this.Pv = Pv;        }    }    public class NewsAdapter : BaseAdapter    {        private List<News> data;        private Context context;        public override int Count        {            get            {                return data.Count;            }        }        public NewsAdapter(List<News> data,Context context)        {            this.data = data;            this.context = context;        }        public override Java.Lang.Object GetItem(int position)        {            return null;        }        public override long GetItemId(int position)        {            return position;        }        private int count;        public override View GetView(int position, View convertView, ViewGroup parent)        {            convertView = LayoutInflater.From(context).Inflate(Resource.Layout.lv_test,parent,false);            TextView title = convertView.FindViewById<TextView>(Resource.Id.tv_title);            TextView pv = convertView.FindViewById<TextView>(Resource.Id.tv_pv);            CheckBox chk = convertView.FindViewById<CheckBox>(Resource.Id.chk_test);            pv.Text = data[position].Pv.ToString();            title.Text = data[position].Title;            DeBug.Write($"执行GetView第{position}次");//DeBug.Write直接在输出里面看到到底发生了什么            DeBug.Write(dictChk[position]);            return convertView;        }    }}

Xamarin  android中ListView中的CheckBox在滑动的时候失去状态的根本原因:

遇到这个问题,我们首先要知道原因。我们就这个没有优化的代码很容易会发现每一次轻轻的滑动,都会触发GetView方法。滑动一个Item的距离就触发两次,依次类推。反正要知道的是滑动的时候会触发GetView方法就行了,在执行GetView方法,代码一目了然,重新实例化CheckBox,选中的状态并没有维持住。我们现在原因找到,解决的办法就简单多了。直接上代码,用一个Dictionary保存每一次单击Checkbox时状态,就ok,不会这么简单,就是这么简单,直接上代码了.
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Android.App;using Android.Content;using Android.OS;using Android.Runtime;using Android.Views;using Android.Widget;using Java.Lang;using DeBug = System.Diagnostics.Debug;namespace DrawerLayout.Adapter{    public class News {        public int Pv { get; set; }        public string Title { get; set; }        public News(string  title,int Pv)        {            this.Title = title;            this.Pv = Pv;        }    }    public class NewsAdapter : BaseAdapter    {        private List<News> data;        private Context context;        private Dictionary<int, bool> dictChk = new Dictionary<int, bool>();        public override int Count        {            get            {                return data.Count;            }        }        public NewsAdapter(List<News> data,Context context)        {            this.data = data;            this.context = context;            for (int i = 0; i < data.Count; i++)            {                dictChk.Add(i,false);            }        }        public override Java.Lang.Object GetItem(int position)        {            return null;        }        public override long GetItemId(int position)        {            return position;        }        private int count;        public override View GetView(int position, View convertView, ViewGroup parent)        {            convertView = LayoutInflater.From(context).Inflate(Resource.Layout.lv_test,parent,false);            TextView title = convertView.FindViewById<TextView>(Resource.Id.tv_title);            TextView pv = convertView.FindViewById<TextView>(Resource.Id.tv_pv);            CheckBox chk = convertView.FindViewById<CheckBox>(Resource.Id.chk_test);            pv.Text = data[position].Pv.ToString();            title.Text = data[position].Title;            chk.Checked = dictChk[position];//每一个Checkbox是否选中是直接根据dicChk的key(position)获取是否选中            chk.CheckedChange += (s, e) =>            {                dictChk[position] = e.IsChecked;//每一次单击CheckBox,dictChk都会保存单击哪一个(position)的状态            };            DeBug.Write($"执行GetView第{position}次");//DeBug.Write直接在输出里面看到到底发生了什么            DeBug.Write(dictChk[position]);            return convertView;        }    }}
效果图:
android ListView包含Checkbox滑动时状态改变

这样肯定不行啊!!一般ListView中的控件都会加一个类 ViewHolder来优化啊.

这样肯定不行的,的确这样肯定没有达到你想要的结果,那么经过ListView优化后的怎样来解决这个问题呢?好的,我们先来看一下,listview优化后的代码一般都会是这样的
        public override View GetView(int position, View convertView, ViewGroup parent)        {            ViewHolder holder = null;            if (convertView == null)            {                convertView = LayoutInflater.From(context).Inflate(Resource.Layout.lv_test, parent, false);                holder = new ViewHolder();                holder.tv_title = convertView.FindViewById<TextView>(Resource.Id.tv_title);                holder.tv_pv = convertView.FindViewById<TextView>(Resource.Id.tv_pv);                holder.chk_status = convertView.FindViewById<CheckBox>(Resource.Id.chk_test);                convertView.Tag = holder;            }            else {                holder = (ViewHolder)convertView.Tag;            }            holder.tv_pv.Text = data[position].Pv.ToString();            holder.tv_title.Text = data[position].Title;            DeBug.Write($"执行GetView第{position}次");            DeBug.Write(dictChk[position]);            return convertView;        }
以上代码所出现的问题是在listview滑动的时候,比如你手机能够看到6项, 选中第一项之后,滑动之后第七项就选中了,再次反复滑动第1,7项一直都是选中的状态那么原因出在哪里呢?调试的时候我们会很容易看到发生的清空。原因我引用别人博客的一段话,个人觉得解释还是蛮清楚的:
Getview()方法是baseadapter里面一个重要的方法,它是在android显示listview里面的内容的时候回调的一个方法。下面就讲讲这个方法的工作机制(个人观点,如有不对,望指出)现在假设我们有10项内容要显示,但是一屏只能显示5项,那么android会首先创建6项对应的itemview的实例并缓存起来,当滑动屏幕,第一项还未移除屏幕,然后第6项已经进入了屏幕的时候,就会把多余的那个view实例用来显示第6项的内容,只有在第7项已经进入屏幕,但是第1项还未移除屏幕的时候才会新建一个view来显示同时缓存起来,此后就将移除屏幕的view用来显示新进入屏幕的item
        public override View GetView(int position, View convertView, ViewGroup parent)        {            ViewHolder holder = null;            News item = data[position];            if (convertView == null)            {                convertView = LayoutInflater.From(context).Inflate(Resource.Layout.lv_test, parent, false);                holder = new ViewHolder();                holder.tv_title = convertView.FindViewById<TextView>(Resource.Id.tv_title);                holder.tv_pv = convertView.FindViewById<TextView>(Resource.Id.tv_pv);                holder.chk_status = convertView.FindViewById<CheckBox>(Resource.Id.chk_test);                convertView.Tag = holder;            }            else            {                holder = (ViewHolder)convertView.Tag;            }            holder.chk_status.Tag = position;            holder.tv_pv.Text = data[position].Pv.ToString();            holder.tv_title.Text = data[position].Title;                       <span style="color:#FF0000;">holder.chk_status.Checked = dictChk[(int)holder.chk_status.Tag];</span>            holder.chk_status.CheckedChange += (s, e) =>            {                DeBug.Write((int)holder.chk_status.Tag);                               <span style="color:#FF0000;">dictChk[(int)holder.chk_status.Tag] = e.IsChecked;</span>            };            DeBug.Write($"执行GetView第{position}次");            DeBug.Write(dictChk[position]);            return convertView;        }

position参数的误区:

加上上面两行红色的代码就可以完全解决listView滑动时失去ChecxBox状态的bug了,刚开始学的时候不能理解这个bug是因为这个GetView中的position参数,当你刚学会用这个listview的时候你一定以为position不就是有多少条数据就多大吗?但是实际却是完全相反的,你手机能显示6条数据。position一直都是0-5,当你滑动到2-7的数据时,position还是0-5。还有CheckBox的状态也可以用用实体字段来保存.

Android中Tag是什么

既然不能position来做标识,那就用Tag。这样问题似乎简单多了,好像并没有想象中的复杂啊。简单点说,Tag的作用是和Id的作用是一样的,程序中调用对应的控件用(findViewById(R.tag.chk),findViewByTag(R.tag.chk))!不过和使用tag相比,使用Id进行查找!效率更快!但是在xamarin android中好像没有Tag这种查找控件的方式.

所以我们就要用Tag来保存这个每一个CheckBox的状态

  <span style="color:#FF0000;">holder.chk_status.Checked = dictChk[(int)holder.chk_status.Tag];</span>
看到这里是不是觉得很简单啊!!!!嘻嘻。


版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表