Android之创建ListView自定义控件

最常用和最难用的控件----ListView

ListView绝对是称得上Android中最常用的控件之一。几乎所有应用程序都会用到它。
ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。
不过比起先前学的几种控件,ListView的用法也相对复杂了很多。

新建项目ListViewTest

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></ListView>
</LinearLayout>

一个ListView通常有两个职责。
(1)将数据填充到布局。
(2)处理用户的选择点击等操作。
一个ListView的创建需要三个元素
(1)ListView中的每一列的View
(2)填入View的数据或图片等。
(3)连接数据与ListView的适配器。
也就是说,要使用ListView,首先要了解什么是适配器。
适配器是一个连接数据和AdapterView(ListView就是一个典型的AdapterView)
的桥梁,通过它能有效地实现数据与AdaterView的分离设置,使AndroidView与数据的绑定更加简便,修改更加方便。
Android中提供了很多的Adapter

Adapter 含义

ArrayAdapter 用来绑定一个数组,支持泛型操作

SimpleAdapter 用来绑定在xml中定义的控件对应的数据

SimpleCursorAdapter 用来绑定游标得到的数据

BaseAdapter 通用的基础适配器

其实适配器还有很多,要注意的是,各种Adapter只不过是转换的方式和能力不一样而已。

package net.nyist.lenovo.listviewtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

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"};

@Override
protected void onCreate(Bundle savedInstanceState) {
    
    
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(
            MainActivity.this,android.R.layout.simple_list_item_1,data);
    ListView listView= (ListView)findViewById(R.id.list_view);
    listView.setAdapter(adapter);
}
}

既然ListView是用于展示大量数据的,那我们就应该先将数据提供好。这些数据可以是从网上下载的,也可以是从数据库中读取的,应该视具体的应用程序场景而定。这里我就使用了一个简单的data数组来测试,里面包含了许多水果的名称。
不过,数组中的数据时无法直接传递给ListView的,我们还需要借助适配器来完成。
ArrayAdpter有多个构造函数的重载,应该根据实际情况选择合适的一种。
由于我们提供的数据都是字符串,因此将ArrayAdapter的泛型指定为String,然后在ArrayAdapter的构造函数中一次传入当前上下文、LitView子项布局的id,以及要适配的数据。注意,我们使用了android.R.layout.simple_list_item_1作为ListView子项布局的id,这是一个Android内置的布局文件,里面只有一个TextView,可用于简单地显示一段文本。这样适配器对象就构建好了。
最后,还需要调用ListView的setAdapter()方法,将构建好的适配器对象传递进去,这样ListView和数据之间的关联就建立完成了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0I9U22qy-1587024879884)(http://i.imgur.com/xBKRLfV.png)]
#定制ListView的界面
只能显示一段文本的ListView实在是太单调了,我们现在就来对ListView的界面进行定制,让它可以显示更加丰富的内容。
首先需要准备好一组图片。
接着定义一个实体类,作为ListView适配器的适配类型。新建Fruit,代码如下:

package net.nyist.lenovo.listviewtest;
public class Fruit {
    
    
private String name;

private int imageId;

public Fruit(String name ,int imageId){
    
    
    this.name = name;
    this.imageId = imageId;
}

public int getImageId() {
    
    
    return imageId;
}

public String getName() {
    
    
    return name;
}
}

Fruit类中只有两个字段,name表示水果的名字,imageId表示水果对应图片的资源id。
然后需要为ListView的子项指定一个我们自定义的布局,在layout目录下新建fruit_item.xml.代码如下:

扫描二维码关注公众号,回复: 12397014 查看本文章
<?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">

<ImageView
    android:id="@+id/fruit_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

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

在这个布局中,我们定义了一个ImageView用于显示水果的图片,有定义了一个TextView用于显示水果的名称,并让TextView在垂直方向上居中显示。

接下来需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型指定为Fruit类。新建类FruitAdapter,代码如下所示:

package net.nyist.lenovo.listviewtest;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
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;

public class FruitAdapter extends ArrayAdapter<Fruit>{
    
    

private int resourceId;

public FruitAdapter(Context context, int textViewResourceId, List<Fruit>objects){
    
    
    super(context,textViewResourceId,objects);
    resourceId = textViewResourceId;
}


@Override
public View getView(int position,  View convertView,  ViewGroup parent) {
    
    
    Fruit fruit = getItem(position);//获取当前项的Fruit实例
    View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
    ImageView fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
    TextView fruitName = (TextView)view.findViewById(R.id.fruit_name);
    fruitImage.setImageResource(fruit.getImageId());
    fruitName.setText(fruit.getName());
    return view;
}
}

// 加载布局管理器,将xml布局转换为view对象
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
//利用view对象,找到布局中的组件
ImageView fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView)view.findViewById(R.id.fruit_name);
FruitAdapter
重写了父类的一组构造函数,用于将上下文,ListView子项布局的id和数据都传递进来。
另外又重写了getView()方法这个方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。在getView()方法中,首先通过getItem()方法得到当前项的Fruit实例,然后使用LayoutInflater来为这个子项加载我们传入的布局。
这里LayoutInflater的inflate()方法接收三个参数,前两个参数我们已经知道是什么意思了,第三个参数指定成false,表示只让我们在父布局中声明的layout属性生效,但不为这个View添加父布局,因为一旦View有了父布局之后,他就不能再添加到ListView中了。
接下来调用View的findViewById()方法分别获取到ImageView和TextView的实例。
并分别调用它们的setImageResource()和setText()方法来设置显示的图片和文字,,最后将布局返回,这样定义的适配器就完成了。
下面修改MainActivity中的代码,如下所示:

package net.nyist.lenovo.listviewtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    
    

private List<Fruit>fruitList = new ArrayList<>();



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);
    initFruits();//初始化水果数据
    FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
    ListView listView= (ListView)findViewById(R.id.list_view);
    listView.setAdapter(adapter);
}
private void initFruits(){
    
    
    for (int i  = 0;i < 2;i++){
    
    
        Fruit apple = new Fruit("Apple",R.drawable.apple_pic);
        fruitList.add(apple);
        Fruit banana = new Fruit("Banana",R.drawable.banana_pic);
        fruitList.add(banana);
        Fruit orange = new Fruit("Orange",R.drawable.orange_pic);
        fruitList.add(orange);
        Fruit watermelon = new Fruit("Watermelon",R.drawable.watermelon_pic);
        fruitList.add(watermelon);
        Fruit pear = new Fruit("Pear",R.drawable.pear_pic);
        fruitList.add(pear);
        Fruit grape = new Fruit("Grape",R.drawable.grape_pic);
        fruitList.add(grape);
        Fruit pineapple = new Fruit("Pineapple",R.drawable.pineapple_pic);
        fruitList.add(pineapple);
        Fruit strawberry = new Fruit("Strawberry",R.drawable.strawberry_pic);
        fruitList.add(strawberry);
        Fruit cherry = new Fruit("Cherry",R.drawable.cherry_pic);
        fruitList.add(cherry);
        Fruit mango = new Fruit("Mango",R.drawable.mango_pic);
        fruitList.add(mango);

    }
}
}

这里添加一个initFruits()方法,用于初始化所有的水果数据。在Fruit类的构造函数中将水果的名字和对应的图片id传入,然后把创建好的对象添加到水果列表中,另外我们使用了一个for循环将所有的水果数据添加了两遍,这是因为如果只添加一遍的话,数据量还不足以充满整个屏幕,接着在onCreate()方法中创建了FruitAdpter对象,并将FruitAdapter作为适配器传递给ListView,这样定制ListView界面的任务就做好了。

这里写图片描述

#ListView的点击事件
话说回来,ListView的滚动毕竟只是满足了我们视觉上的效果,可是如果ListView中的子项不能点击的话,这个控件就没有什么实际的用途了。因此,本小节我们就来学习一下ListView如何才能响应用户的点击事件。
修改MainActivity中的代码,如下所示:

public class MainActivity extends AppCompatActivity {
    
    

private List<Fruit>fruitList = new ArrayList<>();
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);
    initFruits();//初始化水果数据
    FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
    ListView listView= (ListView)findViewById(R.id.list_view);
    listView.setAdapter(adapter);
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
    
    
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    
    
            Fruit fruit = fruitList.get(position);
            Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
        }
    });
}

可以看到,我们使用setOnItemClickListener()方法为ListView注册了一个监听器,当用户点击了ListView中的任何一个子项时,就会回调onItemClick()方法。在这个方法中可以通过position参数判断出用户点击的是哪一个子项,然后获取到响应的水果,并通过Toast将水果的名字显示出来。

猜你喜欢

转载自blog.csdn.net/i_nclude/article/details/75263636