Android Listview 自定义BaseAdapter的实现及Listview优化示例

上一篇文章中我们讲了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<Map<String,Object>> list;
    LayoutInflater mInflater;
    Context context;


    // MyAdapter构造函数 建议把上下文context也传进来
    public MyAdapter(Context context, List<Map<String, Object>> list){
        super();
        mInflater = LayoutInflater.from(context);
        this.list = list;
        this.context=context;
    }

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

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

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

    @Override
    public 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<Map<String,Object>> list = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < names.length; i++) {
            Map<String,Object> 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);

/*    使用SimpleAdapter
       SimpleAdapter 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 });
 */
        // 使用BaseAdapter
        MyAdapter 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
             */
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String Tag = "onItemClick======";
                Log.d(Tag, "position=" + i);
                Log.d(Tag, "行 ID" + l);
/*              HashMap<String,String> map=(HashMap<String,String>)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<Map<String,Object>> list;
    LayoutInflater mInflater;
    Context context;

    // 用于记录listView中的复选框有哪些是被选中的
    HashMap<Integer, Boolean> state = new HashMap<>();

    // MyAdapter构造函数 建议把上下文context也传进来
    public MyAdapter(Context context, List<Map<String, Object>> list){
        super();
        mInflater = LayoutInflater.from(context);
        this.list = list;
        this.context=context;
    }

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

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

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

    @Override
    public 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<String,Object> 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() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // 忽略非人为点击
                if(!buttonView.isPressed())return;
                // 记录那些复选框被选中,保存在List<map>中
                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布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <!--顶部头像信息栏布局-->
    <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp">


        <androidx.cardview.widget.CardView
                android:id="@+id/imgCard"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                app:cardCornerRadius="5dp"
                app:cardElevation="5dp"
                android:layout_margin="10dp">

            <ImageView
                    android:id="@+id/imgHead"
                    android:layout_width="100dp"
                    android:layout_height="100dp"
                    android:src="@drawable/userface1"/>

        </androidx.cardview.widget.CardView>



        <LinearLayout
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="5dp">
            <TextView
                    android:id="@+id/txtUserName"
                    android:text="IT情深"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#000000"
                    android:textSize="18sp"/>

            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">

                <TextView
                        android:text="ID:"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_marginTop="5dp"/>

                <TextView
                        android:id="@+id/txtUserID"
                        android:text="wh445306"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp"
                        android:layout_marginTop="5dp"/>

            </LinearLayout>

            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                <TextView
                        android:text="年龄:"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content" />


                <TextView
                        android:id="@+id/txtUserAge"
                        android:text="28岁"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp" />



                <CheckBox
                        android:id="@+id/chkBox"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:focusable="false"
                        android:focusableInTouchMode="false"
                        />



            </LinearLayout>

            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">

                <TextView
                        android:text="手机号码:"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        />

                <TextView
                        android:id="@+id/txtUserTel"
                        android:text="13318780706"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="5dp"
                        />
            </LinearLayout>


            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content">


                <TextView
                        android:text="最后登录时间:"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"/>


                <TextView
                        android:id="@+id/txtLastIP"
                        android:text="2023-03-11 14:56"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"/>


            </LinearLayout>
        </LinearLayout>
    </LinearLayout>


</LinearLayout>

主布局文件 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">


    <ListView
            android:id="@+id/lvTest"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</LinearLayout>

猜你喜欢

转载自blog.csdn.net/wh445306/article/details/129624249