Android学习之ListView的使用

ListView是Android中最常用的控件之一,虽然现在推荐使用RecycleView,但是作为初学者还是有必要了解ListView的基本用法的。

ListView的简单用法

简单地在ListView中单一显示文本还是比较简单的,首先在activity_main.xml中添加

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

在MainActivity.java中准备好String[]数据用于展示,创建String类型的适配器并传入数据data。再调用findViewById(int id)方法获取listView实例,并为这个listView实例设置适配器

	private	String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango","Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
	    setContentView(R.layout.activity_main);
	    ArrayAdapter<String> adapter=new ArrayAdapter<>(MainActivity.this,android.R.layout.simple_list_item_1,data);
	    ListView listView=findViewById(R.id.list_view);
	    listView.setAdapter(adapter);
	}


单纯只有文本显示有点乏味,我们可以为ListView自定义View,为每个文本增加图片显示

带图标的ListView

首先创建一个我们自己的类,名为MyItem

package com.example.listviewdemo;

public class MyItem {
    private String name;
    private int imageId;
    public MyItem(String name, int imageId){
        this.name=name;
        this.imageId=imageId;
    }

    public String getName(){
        return name;
    }
    public int getImageId(){
        return imageId;
    }
}

再为每一个item创建独立的layout文件,myitem.xml(不可出现大写),其中ImageView限制了图片的尺寸

<?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="wrap_content">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="64dp"
        android:layout_height="64dp">
    </ImageView>

    <TextView
        android:id="@+id/item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp">
    </TextView>
    
</LinearLayout>

随后,为我们自定义的类创建一个独特的适配器,重写其中的getView()方法。创建类MyItemAdapter.java

package com.example.listviewdemo;

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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.List;

public class MyItemAdapter extends ArrayAdapter<MyItem> {
    private int resourceId;

    /**
     * 将上下文、ListView子项布局的id和数据传递进来
     * @param context 上下文
     * @param textViewResourceId 每一个item使用的布局文件id
     * @param objects 数据
     */
    public MyItemAdapter(Context context, int textViewResourceId, List<MyItem> objects){
        super(context,textViewResourceId,objects);
        resourceId=textViewResourceId;
    }
    /**
     * 每个子项被滚动到屏幕内时会被调用
     * @param position item的position
     * @param convertView
     * @param parent
     * @return
     */
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        //获取当前item实例
        MyItem myItem = getItem(position);
        //使用LayoutInflater为这个子项加载传入的布局
        //第三个参数为false表示只让我们在父布局中声明的layout属性生效,但不为这个View添加父布局
        //因为一旦View有了父布局,它就不能再添加到ListView中了
        View view= LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        //调用View的findViewById方法分别获取到ImageView和TextView的实例
        ImageView itemImage = view.findViewById(R.id.item_image);
        TextView itemName = view.findViewById(R.id.item_name);
        //分别调用方法来设置显示的图片和文字
        itemImage.setImageResource(myItem.getImageId());
        itemName.setText(myItem.getName());
        //最后将布局返回,这样自定义适配器就完成了
        return view;
    }
}

修改我们的MainActivity.java文件,方法与普通ListView类似

package com.example.listviewdemo;

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 {
    private String[] data={"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango","Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
    private List<MyItem> myItemList =new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /*ArrayAdapter<String> adapter=new ArrayAdapter<>(MainActivity.this,android.R.layout.simple_list_item_1,data);
        ListView listView=findViewById(R.id.list_view);
        listView.setAdapter(adapter);*/
        initItem();
        MyItemAdapter myItemAdapter = new MyItemAdapter(MainActivity.this,R.layout.myitem, myItemList);
        ListView listView = findViewById(R.id.list_view);
        listView.setAdapter(myItemAdapter);
    }
    private void initItem(){
        for (int i = 0; i < 30; i++) {
            MyItem item1=new MyItem("item1",R.drawable.icon);
            myItemList.add(item1);
            MyItem item2=new MyItem("item2",R.drawable.java);
            myItemList.add(item2);
        }
    }
}

效果

由此可见,我们只要修改myitem.xml,就能定制各种复杂的界面
但是呢,这么写还是不够完美,因为getView方法每次都将布局重新加载了一遍

修改我们的getView方法

	/**
     * 每个子项被滚动到屏幕内时会被调用
     * @param position item的position
     * @param convertView 
     * @param parent
     * @return
     */
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        //获取当前item实例
        MyItem myItem = getItem(position);
        //若convertView不等于null,则直接传入,避免重复获取自定义布局
        View view;
        if(convertView==null){
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        }else{
            view = convertView;
        }
        //调用View的findViewById方法分别获取到ImageView和TextView的实例
        ImageView itemImage = view.findViewById(R.id.item_image);
        TextView itemName = view.findViewById(R.id.item_name);
        //分别调用方法来设置显示的图片和文字
        itemImage.setImageResource(myItem.getImageId());
        itemName.setText(myItem.getName());
        //最后将布局返回,这样自定义适配器就完成了
        return view;
    }

如此修改,直接重用了convertView,避免了重复加载布局,大大提高了ListView的运行效率,在快速滚动的时候会体现出更好的性能。但是这段代码仍然可以继续优化,比如在getView方法中仍然会重复findViewById来获取实例,因此我们可以借助一个ViewHolder来进行优化
创建ViewHolder内部类用于存储单个item其自身的ImageView和TextView,代码如下

package com.example.listviewdemo;

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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class MyItemAdapter extends ArrayAdapter<MyItem> {
    private int resourceId;
	class ViewHolder{
        ImageView itemImage;
        TextView itemName;
    }
    /**
     * 将上下文、ListView子项布局的id和数据传递进来
     * @param context 上下文
     * @param textViewResourceId 每一个item使用的布局文件id
     * @param objects 数据
     */
    public MyItemAdapter(Context context, int textViewResourceId, List<MyItem> objects){
        super(context,textViewResourceId,objects);
        resourceId=textViewResourceId;
    }
    /**
     * 每个子项被滚动到屏幕内时会被调用
     * @param position item的position
     * @param convertView
     * @param parent
     * @return
     */
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        //获取当前item实例
        MyItem myItem = getItem(position);
        //若convertView不等于null,则直接传入,避免重复获取自定义布局
        View view;
        ViewHolder viewHolder;
        if(convertView==null){
            //第一次加载
            view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.itemImage=view.findViewById(R.id.item_image);
            viewHolder.itemName=view.findViewById(R.id.item_name);
            //将viewHolder存储在View中
            view.setTag(viewHolder);
        }else{
            view = convertView;
            //重新获取ViewHolder,当中要强转
            viewHolder=(ViewHolder) view.getTag();
        }
        //调用View的findViewById方法分别获取到ImageView和TextView的实例
        //分别调用方法来设置显示的图片和文字
        viewHolder.itemImage.setImageResource(myItem.getImageId());
        viewHolder.itemName.setText(myItem.getName());
        //最后将布局返回,这样自定义适配器就完成了
        return view;
    }
}

经过上述两步的优化之后,ListView滚动 已经变得十分丝滑,运行效率上升不少。

为ListView添加项目点击事件

修改MainActivity.java中的代码

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initItem();
        MyItemAdapter myItemAdapter = new MyItemAdapter(MainActivity.this,R.layout.myitem, myItemList);
        final ListView listView = findViewById(R.id.list_view);
        listView.setAdapter(myItemAdapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                MyItem item=myItemList.get(position);
                Toast.makeText(MainActivity.this,item.getName(),Toast.LENGTH_SHORT).show();
            }
        });
    }
效果展示

ListView的一些常用属性

分割线

设置颜色必须配合设置高度使用!
部分颜色码参考:https://www.jianshu.com/p/ef2ef0f35cf9

android:dividerHeight="1dp" <!-- 设置分割线高度 或者调用listView.setDividerHeight(0);
android:divider="#00000000" <!--设置分割线颜色 注意,必须要同时设置好分割线高度,否则不显示
android:divider="@drawable/listView_driver" <!--设置分割线的图片资源
android:divider="@drawable/@null" <!--不显示分割线
滚动条
android:scrollbars="none"	<!--隐藏listView的滚动条,或listView.setVerticalScrollBarEnabled(true);
android:fadeScrollbars="true" <!--设置为true可以实现滚动条的自动隐藏和显示
快速滚动条
android:fastScrollEnabled="true" 

或者调用方法listView.setFastScrollEnabled(true)来控制启用,参数为false则隐藏

背景图设置
android:background="@drawable/background"
缓冲颜色

如果发现ListView在滚动的过程中,item会变黑,则在xml中指定属性

android:cacheColorHint="#00000000" <!-- 指定缓冲颜色为透明色

部分资料参考自《第一行代码》作者:郭霖
由于本人才疏学浅,所以本文仅用于记录及分享相关用法,并将持续更新更多的用法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值