Android ListView

Android ListView 详解

介绍

“List view” 是一种用户界面设计中的布局方式,它通过列表的形式展示信息,是一种将信息组织为条目(通常是行)的视图形式,每一项条目都是列表中的一行,可能包含文本、图像或其他元素。

基本使用

xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

activity

public class MainActivity extends AppCompatActivity {

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

        List<String> fruits =new ArrayList<>();

        fruits.add("苹果");
        fruits.add("香蕉");
        fruits.add("普通");
        fruits.add("西瓜");
        //为listview绑定控件
        listView =findViewById(R.id.listview);
        //为listview绑定适配器
        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,fruits));
    }
}

以上是最基本的使用方法

自定义适配器

现在使用listview来显示学生列表

entity

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    String name;
    String age;
    String sex;
    String image;
}
/*
@Data
@AllArgsConstructor
@NoArgsConstructor
这些注释需要添加依赖

    implementation 'org.projectlombok:lombok:1.18.20'
    annotationProcessor 'org.projectlombok:lombok:1.18.20'

如果不添加依赖自己写get、set方法也可以
*/

xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

为listview绘制适配界面

在layout目录下创建layout_student_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/name"
        android:layout_marginHorizontal="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/sex"
        android:layout_marginHorizontal="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/age"
        android:layout_marginHorizontal="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

为listview创建适配器

public class MyListAdapter extends BaseAdapter {
    Context context;
    List<Student> students;

    public MyListAdapter(Context context, List<Student> students){
        //初始化
        this.context=context;
        this.students=students;
    }
    @Override
    public int getCount() {
        //返回list数量
        return students.size();
    }

    @Override
    public Object getItem(int position) {
        //获取每个item
        return students.get(position);
    }

    @Override
    public long getItemId(int position) {
        //获取下标
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 使用 LayoutInflater 将 layout_student_list 布局文件转换为视图对象。
        View viewStudent = LayoutInflater.from(context).inflate(R.layout.layout_student_list, parent, false);
        //获得学生对象
        Student student = students.get(position);
        TextView name = viewStudent.findViewById(R.id.name);
        TextView age = viewStudent.findViewById(R.id.age);
        TextView sex = viewStudent.findViewById(R.id.sex);
        //将数据一一添加到布局中。
        name.setText(student.getName());
        age.setText(String.valueOf(student.getAge()));
        /*
       		age.setText();
       		TextView使用setText方法要使用字符串,非字符串会引发android.content.res.Resources$NotFoundException: String resource ID 异常
       		非字符串类型请先转为字符串
        */
        
        sex.setText(student.getSex());
        return viewStudent ;
    }
}

缺点

  1. 性能开销大:每次 getView 被调用时,findViewById 方法都会被调用,这会导致性能开销增加,特别是在长列表中。
  2. 视图查找开销:每次 getView 被调用时,都会查找视图组件,增加了 CPU 和内存开销。

使用ViewHolder 优化

使用ViewHolder 对ListView进行优化时只需要修改适配器MyListAdapter代码即可

public class MyListAdapter extends BaseAdapter {
    Context context;
    List<Student> students;

    public MyListAdapter(Context context, List<Student> students){
        this.context=context;
        this.students=students;
    }
    @Override
    public int getCount() {
        return students.size();
    }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
         // 创建一个 ViewHolder 对象,用于缓存视图组件。
        ViewHolder viewHolder = null;
        //获得Item位置上的数据。
        Student student = students.get(position);
        if(convertView == null){
            convertView = LayoutInflater.from(context).inflate(R.layout.layout_student_list, parent, false);
        	// 创建一个新的 ViewHolder 实例,并将其与 convertView 关联。
            viewHolder = new ViewHolder(convertView);
        	// 将 ViewHolder 对象设置为 convertView 的标签,以便下次重用。
            convertView.setTag(viewHolder);
        }else{
        	// 如果 convertView 已经存在,则从 convertView 的标签中获取 ViewHolder 对象。
            viewHolder = (ViewHolder) convertView.getTag();
        }
        // 将当前学生的数据设置到视图中的相应组件。
        viewHolder.name.setText(student.getName());
        viewHolder.age.setText(student.getAge());
        viewHolder.sex.setText(student.getSex());
		// 返回最终的视图对象。
        return convertView;
    }

    static class ViewHolder{
    	// 定义用于缓存视图组件的变量。
        private final TextView name;
        private final TextView age;
        private final TextView sex;

        public ViewHolder(View itemView){ 
            // 在 itemView 中查找并初始化视图组件。
            name = itemView.findViewById(R.id.name);
            age = itemView.findViewById(R.id.age);
            sex = itemView.findViewById(R.id.sex);
        }
    }
}

优点:

  1. 性能优化:通过缓存视图组件,减少了频繁调用 findViewById 的次数,从而降低了 CPU 和内存开销。
  2. 更清晰的代码:代码更加简洁易懂,减少了在 getView 方法中重复的视图查找逻辑。
  3. 流畅的滚动体验:提高了 ListView 的滚动性能,使列表滚动更加流畅。

为listview添加常用事件

	listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
	//为listview添加item的点击事件
             @Override
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 //通过下标获取点击的元素
                 String selectedItem = students.get(position).getName();
                 Toast.makeText(MainActivity.this, selectedItem, Toast.LENGTH_SHORT).show();
             }
	});
	listView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
        //设置一个创建上下文菜单的监听器。
            @Override
            public void onCreateContextMenu(ContextMenu arg0, View view, ContextMenu.ContextMenuInfo arg1) {
                /*
                	 onCreateContextMenu 方法会在用户长按 ListView 中的某一项时调用。
					arg0.setHeaderTitle("选择操作"):设置上下文菜单的标题。
					arg0.add(0, 4, 0, "移出"):添加一个菜单项,ID 为 4,标题为“移出”。
                */
                arg0.setHeaderTitle("选择操作");
                arg0.add(0, 4, 0, "移出");
            }
	});
        
	List<Student> students;

    public boolean onContextItemSelected(@NonNull MenuItem item) {
        //AdapterView.AdapterContextMenuInfo:获取上下文菜单的项的相关信息。
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        if (item.getItemId() == 4) {
            //item.getItemId() == 4:检查用户选择的菜单项 ID 是否为 4。
            Student student = students.get((int)info.id);
        	//students.get((int)info.id):根据菜单项的 ID(也就是 info.id)获取对应的学生。
            Toast.makeText(this, "移出了姓名为"+student.getName()+"的学生", Toast.LENGTH_SHORT).show();
            students.remove(student);
            listView.setAdapter(new MyListAdapter(this,students));
            //listView.setAdapter(new MyListAdapter(this, students)):更新 ListView 的适配器,以反映数据的变化。
            return true;
        }

        return super.onContextItemSelected(item);
    }

总结

ListView 是 Android 中一个非常常用的控件,通过适配器将数据与视图绑定,展示列表项。基本的用法包括简单的数组适配器和自定义适配器。在进行性能优化时,ViewHolder 模式可以显著提高列表的滚动效率。结合点击事件和上下文菜单,可以实现更丰富的用户交互功能。

代码地址

https://gitee.com/lxj_dear/my-list-view

posted @ 2024-07-30 11:12  疾风不问归途  阅读(56)  评论(0)    收藏  举报