Android Listview 自定义BaseAdapter的实现及Listview优化示例
创始人
2025-05-29 19:52:31

上一篇文章中我们讲了Android Listview SimpleAdapter的使用完整示例(实现用户列表)_左眼看成爱的博客-CSDN博客

本示例实现的效果图:

每个item中的checkbox选中事件实现单独监听处理: 

取消选中:

 整个item点击事件(共存):

 

     这篇文章我来讲一下 自定义BaseAdapter的实现示例及优势在哪里?   通过查看SimpleAdapter的实现源码,我们可以发现SimpleAdapter实现的view复用方式其实就是convertView复用。但并没有避免复用时重复的调用findViewById,所以SimpleAdapter很难实现给item中的按钮或Checkbox单独添加监听事件。而且使用 SimpleAdapter也无法解决Listview中带checkbox时滑动选中状态混乱的问题。

  

      所以通常我们都会自定义一个继承自BaseAdapter(已继承ListViewAdapter)来实现更加灵活强大的listview适配器,通过查看源码可以发现:ArrayAdapter(继承自BaseAdapter),SimpleAdapter(继承自BaseAdapter)的类,所以,这节我们讲一下如何重写BaseAdapter 重写getView()方法,如何使用ViewHolder优化findView次数来实现我们自己想要的功能。

基于 BaseAdapter 使用 ListView

添加 ListView 组件,存放数据,设置列表项的布局文件都和 SimpleAdapter 中的操作相同

创建一个 Adapter 继承 BaseAdapter,并实现抽象方法。

BaseAdapter 有 4 个抽象方法需要去实现:

int getCount(); //返回的是数据源对象的个数,即列表项数
Object getItem(int var1); //返回指定位置position上的列表项
long getItemId(int var1); //返回指定位置处的行ID
View getView(int var1, View var2, ViewGroup var3); //返回列表项对应的视图

继承 BaseAdapter 时需要去实现这 4 个抽象方法,这几个抽象方法都是 Adapter 接口中定义的方法。

前三个方法基本很简单不需要太多关注,重点在getView方法的实现

继承 BaseAdapter 基本样式:

package com.example.Listview_BaseAdapter;/*** @author wh445306* @version 1.0* @Description MyAdapter* @Date 2023-03-17 13:10*/public class MyAdapter extends BaseAdapter {List> list;LayoutInflater mInflater;Context context;// MyAdapter构造函数 建议把上下文context也传进来public MyAdapter(Context context, List> list){super();mInflater = LayoutInflater.from(context);this.list = list;this.context=context;}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {return null;}}

    public View getView(final int position, View convertView, ViewGroup parent) {

        return null;
    }

第一个参数: int position,一般BaseAdapter都是很多类型一样的数据展示在界面,
该属性是判断显示在界面上的是第几个,通过position在BaseAdapter自定义的数组或者集合中取值。并展示在界面上。

第二个参数: View converView View converView是展示在界面上的一个item。因为手机屏幕就那么大,所以一次展示给用户看见的内容是固定的,如果你List中有1000条数据,不应该new1000个converView,那样内存肯定不足,应该学会控件重用,滑出屏幕的converView就在下面新进来的item中重新使用,只是修改下他展示的值。这样能减去很多消耗。
不过有的时候我们不能对其进行重构 比如带CheckBox的item,如果你使用判断,在你选中某个item的CheckBox时滑动时会出现混乱,这时你就必须去掉判断对其进行重构。

第三个参数:ViewGroup parent 这个属性是加载xml视图时使用。

所以在下面的代码中我们为了提高性能会先判定converView是否为空,为空的话猜重新创建并且后面选择保存布局到缓存

下面我们上一个完整示例代码:

MainActivity单元:
package com.example.Listview_BaseAdapter;import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MainActivity extends AppCompatActivity {String[] names={"张三","李四","王五","听雨","若兰","海子","大眼","卡卡","小米","小恐龙"};String[] ids={"wh445306","jyw8886","bmw8899","xx8yzz","888yzx","776yy9","99zz9","ka8ka8","xiaoni8","xkl888"};String[] ages={"28岁","27岁","22岁","24岁","28岁","18岁","15岁","13岁","17岁","20岁"};//String[] tels={"18322228898","13922278898","13719780706","15322228898"};//int[] pics={R.drawable.userface1,R.drawable.userface2,R.drawable.userface3,R.drawable.userface4};List> list = new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);for (int i = 0; i < names.length; i++) {Map map= new HashMap<>();map.put("name",names[i]);map.put("id",ids[i]);map.put("age",ages[i]);map.put("tel","138"+(int)(Math.random()*90000000+10000000));int picID = getResources().getIdentifier("userface"+(i+1), "drawable", getPackageName());map.put("pic",picID);map.put("box",false);list.add(map);}ListView listView=findViewById(R.id.lvTest);/*    使用SimpleAdapterSimpleAdapter adapter = new SimpleAdapter(MainActivity.this,  list,R.layout.list_item,new String[] { "name", "id", "age","tel","pic" },new int[] { R.id.txtUserName, R.id.txtUserID, R.id.txtUserAge,R.id.txtUserTel,R.id.imgHead });*/// 使用BaseAdapterMyAdapter adapter =new MyAdapter(this,list);listView.setAdapter(adapter);//为 ListView 的列表项添加鼠标点击事件listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {/*** @param adapterView 发生单击事件的列表项 ListView* @param view        view是当前listview中的item的view的布局,就是可用这个view获取里面控件id后操作控件* @param i           在列表项中的位置 position* @param l           被单击列表项的行ID*/@Overridepublic void onItemClick(AdapterView adapterView, View view, int i, long l) {String Tag = "onItemClick======";Log.d(Tag, "position=" + i);Log.d(Tag, "行 ID" + l);
/*              HashMap map=(HashMap)adapterView.getItemAtPosition(i);String Text= map.get("name");String id= map.get("id");
*/String Text= list.get(i).get("name").toString();String id= list.get(i).get("id").toString();Toast.makeText(MainActivity.this, "您点击的行:Name:="+Text+" id:="+id, Toast.LENGTH_SHORT).show();}});}
}
BaseAdapter实现单元:
package com.example.Listview_BaseAdapter;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author wh445306* @version 1.0* @Description MyAdapter* @Date 2023-03-17 13:10*/public class MyAdapter extends BaseAdapter {List> list;LayoutInflater mInflater;Context context;// 用于记录listView中的复选框有哪些是被选中的HashMap state = new HashMap<>();// MyAdapter构造函数 建议把上下文context也传进来public MyAdapter(Context context, List> list){super();mInflater = LayoutInflater.from(context);this.list = list;this.context=context;}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView==null){// 首次convertView为null时才会加载item布局文件holder= new ViewHolder();convertView= mInflater.inflate(R.layout.list_item,null);// 通过ViewHolder持有view中的子控件holder.name=convertView.findViewById(R.id.txtUserName);holder.id=convertView.findViewById(R.id.txtUserID);holder.age=convertView.findViewById(R.id.txtUserAge);holder.tel=convertView.findViewById(R.id.txtUserTel);holder.pic=convertView.findViewById(R.id.imgHead);holder.box =convertView.findViewById(R.id.chkBox);// 再通过setTag的形式和view绑定convertView.setTag(holder);}else {holder= (ViewHolder) convertView.getTag();}// 使用ViewHolder实现View组件的缓存和重用,重用View时就不用通过findViewById重新寻找view组件Map map = list.get(position);//holder.name.setText((String)list.get(position).get("name"));holder.name.setText((String)map.get("name"));holder.id.setText((String)map.get("id"));holder.age.setText((String)map.get("age"));holder.tel.setText((String)map.get("tel"));holder.pic.setImageResource((int) map.get("pic"));//holder.box.setChecked((Boolean) map.get("box"));// 根据state设置复选框是否被选中// 但是由于setChecked方法会触发下面setOnCheckedChangeListener// 所以需要在setOnCheckedChangeListener中添加一句,判断是用户点击的才触发处理holder.box.setChecked(state.get(position));holder.box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {// 忽略非人为点击if(!buttonView.isPressed())return;// 记录那些复选框被选中,保存在List中if (isChecked) {state.put(position, isChecked);} else {state.remove(position);}// 在这里处理选中或取消选中的操作if (isChecked) {Toast.makeText(context, "您选中了:Name:="+ list.get(position).get("name"), Toast.LENGTH_SHORT).show();} else {Toast.makeText(context, "您取消了选中:Name:="+ list.get(position).get("name"), Toast.LENGTH_SHORT).show();}}});return convertView;}// 定义一个ViewHolder静态类来持有convertView的每一个子控件public static  class ViewHolder{public TextView name;public TextView id;public TextView age;public TextView tel;public ImageView pic;public CheckBox box;}}

item布局:


主布局文件 



相关内容

热门资讯

C++ Primer第五版_第... 文章目录练习4.11练习4.12练习4.13练习4.14练习4.15练习4.16练习4.17练习4....
【数据结构】千字深入浅出讲解队... 🚀write in front🚀 📝个人主页...
电子拣货标签3代系统简介 CK_Label_v3 一、产品参数  1. 电池供电版 产品型号 CK_Label_v3 尺...
2023新车上牌费用是多少?上... 今天给各位分享2023新车上牌费用是多少?上牌照需要多少钱的知识,其中也会对2022年上牌进行解释,...
荣事达竟然是美的集团旗下品牌?... 今天给各位分享荣事达竟然是美的集团旗下品牌?!的知识,其中也会对荣事达美的合并了吗进行解释,如果能碰...
火车站次查询(火车站查询车次)... 本篇文章极速百科给大家谈谈火车站次查询,以及火车站查询车次对应的知识点,希望对各位有所帮助,不要忘了...
分集水器的详细使用方法,学会供... 本篇文章极速百科给大家谈谈分集水器的详细使用方法,学会供暖不会再有办法,以及分集水器构造详图对应的知...
c++ error:cross... 最近在写代码的时候,碰到了 crosses initialization of ......
机器学习模型的性能评估方法 动动发财的小手,点个赞吧! 部署模型后,监控其性能对于确保...
俄罗斯土地面积多少平方公里(世... 本篇文章极速百科给大家谈谈俄罗斯土地面积多少平方公里,以及世界领土最大的三个国家对应的知识点,希望对...
ysl83是什么颜色 极速百科... ysl83是什么颜色目录ysl83是什么颜色ysl83是什么颜色圣罗兰纯魅唇膏86号适合黄皮女生素颜...
信用贷款怎么申请,个人信用贷款... 信用贷款怎么申请目录信用贷款怎么申请个人信用贷款怎么贷如何办理个人信用贷款?个人信用贷款怎么贷信用贷...
泾县有哪些景点,泾县十大必去景... 泾县有哪些景点目录泾县有哪些景点泾县十大必去景点泾县旅游景点泾县景点泾县有哪些景点 泾县位于安...
Leveraging Sali... Leveraging Saliency in Single-Stage Multi-Label Co...
ES-数据建模 数据模型是描述现实世界某种现象或者状态的物理抽象,比如我们之前用FSA来描述周老师的一...
进击的巨人大结局,进击的巨人漫... 进击的巨人大结局目录进击的巨人大结局进击的巨人漫画最终结局漫画《进击的巨人》大结局是什么?进击的巨人...
免单活动是什么意思 极速百科网... 免单活动是什么意思目录免单活动是什么意思免单活动是什么意思告诉我,免单是什么意思免单活动是什么意思免...
meld是手机里的什么,mel... meld是什么意思? meld是什么意思?作为全球唯一的标识,作用相当于我们每个人的。扩展资料:查找...
中华什么多奇志(中华什么多奇志... 本篇文章极速百科给大家谈谈中华什么多奇志,以及中华什么多奇志不爱红装爱武装英文对应的知识点,希望对各...
Linux C++实现进程间通... 基本知识 基本知识介绍参考:https://mp.weixin.qq.com/s/oS...
2023年ACM竞赛班 202...  目录 瞎编乱造第一题 瞎编乱造第二题 瞎编乱造第三题 瞎编乱造第四题 瞎编乱造第五题 不是很想编了...
windows安装包管理工具C... Chocolatey介绍Chocolatey 通过使用通用打包格式来管理 Windows 软件的各个...
解决:centos7如何解决网... 遇到此类问题可能会有多重解决方法,需要一个一个的去排除。 1、查看自己的网络设置是不是...
百克特1103是什么意思,请问... 百克特1103是什么意思目录百克特1103是什么意思请问,白克特1101和1103是什么病毒扫地机器...
无话不谈彼此陪伴什么意思,无话... 无话不谈彼此陪伴什么意思目录无话不谈彼此陪伴什么意思无话不谈是什么意思无话可说和无话不谈有什么区别?...
58同城企业认证的注册号是什么... 58同城企业认证的注册号是什么目录58同城企业认证的注册号是什么企业认证里的人注册号是什么我在58同...
草字头的字(草字头的字和什么有... 本篇文章极速百科给大家谈谈草字头的字,以及草字头的字和什么有关一年级对应的知识点,希望对各位有所帮助...
CentOS8提高篇2:Cen... 一、下载安装包 到wps官网下载linux版本的安装包, 根据自己的linux是 32...
这款开源管理文档管理系统兼容P... demo软件园每日更新资源,请看到最后就能获取你想要的: 1.《计算机网络实验教程》课后答案   ...
SpringBoot学习笔记(... 测试 加载测试专用属性 在启动测试环境时可以通过properties参数设置测试环境专用的属性 &#...