Android学习之路(19) ListView详解

一.ListView简介

在Android开发中,ListView是一个比较常用的控件。它以列表的形式 展示具体数据内容,并且能够根据数据的长度自适应屏幕显示。

二.ListView简单用法

代码部分

1.布局界面 activity_main.xml 代码:

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">
   <ListView
     android:id="@+id/list_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"/>
</LinearLayout>

2.类文件 MainActivity.java 代码:

复制代码
package com.example.listview1;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
//1、定义对象
     ListView listView;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         //2、绑定控件
         listView=(ListView) findViewById(R.id.list_view);
         //3、准备数据
         String[] data={"菠萝","芒果","石榴","葡萄", "苹果", "橙子", "西瓜","菠萝","芒果","石榴","葡萄", "苹果", "橙子", "西瓜","菠萝","芒果","石榴","葡萄", "苹果", "橙子", "西瓜"};
         //4、创建适配器 连接数据源和控件的桥梁
         //参数 1:当前的上下文环境
         //参数 2:当前列表项所加载的布局文件
         //(android.R.layout.simple_list_item_1)这里的布局文件是Android内置的,里面只有一个textview控件用来显示简单的文本内容
         //参数 3:数据源
         ArrayAdapter<String> adapter=new ArrayAdapter<>(MainActivity.this,android.R.layout.simple_list_item_1,data);
         //5、将适配器加载到控件中
         listView.setAdapter(adapter);
         //6、为列表中选中的项添加单击响应事件
         listView.setOnItemClickListener(new AdapterView.OnItemClickListener() 
{
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int i, long l) {
         String result=((TextView)view).getText().toString();
         Toast.makeText(MainActivity.this,"您选择的水果是:"+result,Toast.LENGTH_LONG).show();
         }
     });
   }
 }

代码解析

1.ArrayAdapter适配器

  • 1、ArrayAdapter适用亍数组或数据ArrayList(动态数组)。
  • 2、ArrayAdapter可以通过泛型来指定要适配的数据类型,然后在构造凼数中把要适配的数据传入。
  • 3、ArrayAdapter有多个构造函数的重载,可以根据实际情况选择最合适的一种。

2.点击事件响应

Parent: 指定哪个AdapterView(可能会有多个ListView,区分多个ListView)

View: 为你点击的Listview的某一项的内容,来源于adapter。如用((TextView)view).getText().toString(),可以取出点击的这一项的内容,转为string 类型。

Position: 指的是adapter的某一项的位置,如点击了listview第2项,而第2项对应 的是adapter的第2个数值,那此时position的值就为1了。注:这些数值都是从0开 始的。

Id:id的值为点击了Listview的哪一项对应的数值,点击了listview第2项,那id就等于1。一般和position相同。

三.定制 ListView 界面

只能显示一段文本的listview太单调了,我们现在就来对listview的界面进行定制,让其丰富内容。

代码部分

1.布局界面 activity_main.xml 代码:

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
     android:layout_height="match_parent"
     tools:context=".MainActivity">
     <ListView
         android:id="@+id/list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"/>
</LinearLayout>

2.类文件 MainActivity.java 代码:

复制代码
package com.example.listview2;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
     //第一步:定义对象
     ListView listView;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         //第二步:绑定控件
         listView = (ListView) findViewById(R.id.list_view);
         //第三步:准备数据
         List<Fruit> fruitlist = new ArrayList<>();
         for (int i = 0; i <2 ; i++) {
             Fruit pineapple=new Fruit(R.drawable.pineapple,"菠萝","¥16.9 元/KG");
             fruitlist.add(pineapple);
             Fruit mango = new Fruit(R.drawable.mango, "芒果","¥29.9 元/kg");
             fruitlist.add(mango);
             Fruit pomegranate = new Fruit(R.drawable.pomegranate, "石榴","¥15元/kg");
             fruitlist.add(pomegranate);
             Fruit grape = new Fruit(R.drawable.grape, "葡萄","¥19.9 元/kg");
             fruitlist.add(grape);
             Fruit apple = new Fruit(R.drawable.apple, "苹果","¥20 元/kg");
             fruitlist.add(apple);
             Fruit orange = new Fruit(R.drawable.orange, "橙子","¥18.8 元/kg");
             fruitlist.add(orange);
             Fruit watermelon = new Fruit(R.drawable.watermelon, "西瓜","¥28.8元/kg");
             fruitlist.add(watermelon);
         }
         //第四步:设计每一个列表项的子布局
         //第五步:定义适配器 控件 -桥梁-数据
         FruitAdapter adapter=new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitlist);
         listView.setAdapter(adapter);
   }
}

3.类文件 Fruit.java 代码:

复制代码
package com.example.listview2;
public class Fruit {
private int imageID;
private String name;
private String price;
     public int getImageID() {
         return imageID;
     }
     public String getName() {
         return name;
     }
     public String getPrice() {
         return price;
     }
     public Fruit(int imageID, String name, String price) {
         this.imageID = imageID;
         this.name = name;
         this.price = price;
     }
}

4.类文件 FruitAdapter.java 代码:

复制代码
package com.example.listview2;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
//用于将上下文、listview 子项布局的 id 和数据都传递过来
public class FruitAdapter extends ArrayAdapter<Fruit> {
 public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
     super(context, resource, objects);
 }
//每个子项被滚动到屏幕内的时候会被调用
     @NonNull
     @Override
     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
     Fruit fruit=getItem(position);//得到当前项的 Fruit 实例
     //为每一个子项加载设定的布局
     View view=LayoutInflater.from(getContext()).inflate(R.layout.fruit_item,parent,false);
     //分别获取 image view 和 textview 的实例
     ImageView fruitimage =view.findViewById(R.id.fruit_image);
     TextView fruitname =view.findViewById(R.id.fruit_name);
     TextView fruitprice=view.findViewById(R.id.fruit_price);
     // 设置要显示的图片和文字
     fruitimage.setImageResource(fruit.getImageID());
     fruitname.setText(fruit.getName());
     fruitprice.setText(fruit.getPrice());
     return view;
     }
}

5.布局界面 fruit_item.xml 代码:

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:orientation="horizontal"
     android:layout_height="wrap_content">
     <ImageView
         android:id="@+id/fruit_image"
         android:src="@drawable/apple"
         android:layout_width="100dp"
         android:layout_height="80dp"/>
     <TextView
         android:id="@+id/fruit_name"
         android:layout_gravity="center_vertical"
         android:textSize="30sp"
         android:textColor="#000000"
         android:text="name"
         android:layout_marginLeft="10dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
     <TextView
         android:id="@+id/fruit_price"
         android:layout_gravity="center_vertical"
         android:textColor="#ff0000"
         android:text="price"
         android:textSize="30sp"
         android:layout_marginLeft="10dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"/>
</LinearLayout>

代码解析

1.引入动态数组ArrayList

数组的缺点

(1)数组长度固定

(2)定义数组只能指定一种数据类型

ArrayList:可以动态增加和缩减的索引序列,它是基于数组实现的list类

List fruitlist = new ArrayList<>();

List泛型里面既包括图片又包含文本,因此我们要定义一个Fruit类

2.Fruit类

复制代码
public class Fruit {
private int imageID;
private String name;
private String price;
}

在里面添加图片的id,名称和价格

然后按下Alt+Insert键添加构造方法(Constructor)和Get方法(Getter)

3.自定义适配器 控件 -桥梁-数据

为什么要自定义适配器?

原因在于,当我们想用一些其他的展现方式,或者是本案例我们需要的图文混排的呈现方式,这就需要DIY了。

1.我们定义一个自定义适配器 FruitAdapter继承ArrayAdapter。

2.自定义适配器中常用的方法:getCount、getView、getItem、getItemId。

(1)创建好后需要添加泛型(也就是我们创建的Fruit类)

(2)按下键盘上的Alt+Enter键创建构造方法(倒数第二个list)

(3)重写getView方法

4.inflate()方法

inflate()方法的三个参数

inflate(R.layout.fruit_item,parent,false)

1、第一个参数是布局;(自己写的)

2、第二个参数是父容器控件;

3、第三个布尔值参数表明是否连接该布局和其父容器控件,在这里的情况设置 为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一 个View Group。

四.提升ListView的运行效率

目前我们ListView的运行效率是很低的,因为在FruitAdapter的 getView()方法中,每次都将布局重 新加载了一遍,将快速滚动的时候, 这将会成为性能的瓶颈。

getView()方法中的convertView参数,用于将之前加载好的布局进行缓存,以便之 后可以进行重用。

优化方法一:

优化方法二:

代码:

复制代码
package com.example.listview3;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
//用于将上下文、listview 子项布局的 id 和数据都传递过来
public class FruitAdapter extends ArrayAdapter<Fruit> {
     public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) {
         super(context, resource, objects);
     }
     @NonNull
     @Override
     public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
         Fruit fruit=getItem(position);//获取当前项的 Fruit 实例
         View view;
         //新增一个内部类 ViewHolder,用于对控件的实例进行缓存
         ViewHolder viewHolder;
         if (convertView==null){
             //为每一个子项加载设定的布局
             view= LayoutInflater.from(getContext()).inflate(R.layout.fruit_item,parent,false);                     
             viewHolder=new ViewHolder();
             //分别获取 imageview 和 textview 的实例
             viewHolder.fruitimage =view.findViewById(R.id.fruit_image);
             viewHolder.fruitname =view.findViewById(R.id.fruit_name);
             viewHolder.fruitprice=view.findViewById(R.id.fruit_price);
             view.setTag(viewHolder);//将 viewHolder 存储在 view 中
         }else {
             view=convertView;
             viewHolder= (ViewHolder) view.getTag();//重新获取 viewHolder
     }
         // 设置要显示的图片和文字
         viewHolder.fruitimage.setImageResource(fruit.getImageID());
         viewHolder.fruitname.setText(fruit.getName());
         viewHolder.fruitprice.setText(fruit.getPrice());
         return view;
     }
     private class ViewHolder {
         ImageView fruitimage;
         TextView fruitname;
         TextView fruitprice;
     }
}

五.ListView的点击事件

ListView的滚动毕竟只是满足 了我们视觉上的效果,下面我们来学习ListView如何才能响 应用户的点击事件。

代码:

复制代码
//第六步:listview 的点击事件
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
        Fruit fruit= fruitlist.get(position) ;
        Toast.makeText(MainActivity.this,"您选择的水果是:"+fruit.getName(),Toast.LENGTH_LONG).show();
    }
});

六.总结

相关推荐
阿巴斯甜12 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker13 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952714 小时前
Andorid Google 登录接入文档
android
黄林晴15 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android