Android中ListView等使用的Adapter简介

1.介绍:

Adapter连接后端数据前端显示的适配器接口,是数据和UIView)之间一个重要的纽带。很多常见的View(如:ListView,SpinnerGridView)都需要用到Adapter。如下图直观的表达了DataAdapterView三者的关系:

Android中ListView等使用的Adapter简介_第1张图片

注意:

(1)适配器用来将不同的数据映射View上。不同的数据对应不同的适配器,如ArrayAdapterCursorAdapter, SimpleAdapter等, 他们能够将数组游标Map对象列表等数据映射到View上。

(2)正是由于适配器的存在,使得ListView的使用相当灵活,经过适配器的处理后,在 view看来所有的数据映射过来都是一样的。

(3)所有的数据和资源要显示到ListView上都须通过适配器来完成。

(4)系统已有的适配器可以将基本的数据显示到ListView上,但是在实际开发中这些系统已实现的适配器,有时不能满足我们的需求,实现复杂的ListView可以通过1继承ListView并重写相应的方法来完成,更一般的做法是(2)通过继承BaseAdapter来实现


2.比较常用的Adapter有四类:

(1)ArrayAdapter支持泛型操作,操作最为简单,只能展示一行字符

(2)SimpleAdapter有很好的可扩充性,可以自定义出各种效果

(3)SimpleCursorAdapter可以适用于简单的纯文字型ListView,它需要将Cursor的字段和UI显示的id对应起来。可以看作SimpleAdapter和数据库的简单结合,能够方便地把数据库的内容以列表的形式展示出来。

(4)BaseAdapter是一个抽象类,继承它需要实现较多的方法,所以也就具有较高的灵活性。是用于ListView(实现指定的ListAdapter接口)Spinner(实现指定的SpinnerAdapter接口)的共同实现的一个公共基类适配器。

注意:ArrayAdapterCursorAdapter, SimpleAdapter都继承于BaseAdapter


3.使用:

ArrayAdapter的使用方法:

(1)写代码定义列表中要显示的底层数据结构,如List,并向底层数据结构中填充具体数据信息,例如:

//定义List变量(提供数据)--ArrayList是List的具体实现
List list = new ArrayList();
//添加数据内容
list.add("测试数据--1");
list.add("测试数据--2");
list.add("测试数据--3");
list.add("测试数据--4");
list.add("测试数据--5");
list.add("测试数据--6");
list.add("测试数据--7");
list.add("测试数据--8");
list.add("测试数据--9");

(2)

创建ArrayAdapter对象

参数确定ListView每行的布局和要显示的底层数据

//定义ArrayAdapter
//参数-----上下文环境, ListView的每一行的布局,  底层数据
ArrayAdapter adapter=new ArrayAdapter(this,android.R.layout.simple_list_item_1, list);
     

使用自定义布局必须给出TextViewID—可以包含复杂的布局,但要求布局中必须有一个TextView用于显示字符串

//如果要使用自定义的布局,必须指明TextView的ID--布局中也可以包含除TextView之外的其它控件
ArrayAdapter adapter=new ArrayAdapter(this, R.layout.test, R.id._id, list);

(3)调用ListView对象的setAdapter()方法将刚创建的ArrayAdapter对象与ListView对象结合起来。

  //设置ListView的Adapter对象
 listview.setAdapter(adapter);

(4)需要的话,再利用setOnItemClickListener()为ListView对象添加“点击行”的监听器

listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                //parent--ListView
                // view--一行
                //position--适配器中的序号
                // id--row id, ListView中的序号,从0开始编号
                //大部分情况下(如:ArrayAdapter 和 SimpleAdapter 中,两者是一样的),position和id相同
                //但是,有些情况(如SimpleCursorAdapter中),row id是数据库中的_id字段的值,与position不同
                String info=list.get(position) + "  was clicked!"; //取出点击的行的内容
                Toast.makeText(ArrayAdapterActivity.this, info+"--"+id, Toast.LENGTH_SHORT).show();
            }
 });

自己写的一个简单模版(布局文件太简单不贴了):

package cn.edu.qtech.csc.lcb.listviewdemo;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

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

public class ArrayAdapterActivity extends Activity {

    //定义ListView对象变量---View
    private ListView listview;

    //存放数据的List对象---Model
    private List list;


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

        //获取ListView对象
        listview=(ListView)findViewById(R.id.simpleListViewControll);

        //定义List变量(提供数据)--ArrayList是List的具体实现
        list=new ArrayList();
        //添加数据内容
        list.add("测试数据--1");
        list.add("测试数据--2");
        list.add("测试数据--3");
        list.add("测试数据--4");
        list.add("测试数据--5");
        list.add("测试数据--6");
        list.add("测试数据--7");
        list.add("测试数据--8");
        list.add("测试数据--9");

        //定义ArrayAdapter,衔接ListView和List---Controller
        //参数-----上下文环境, ListView的每一行的布局,  List对象
        //如果要使用自定义的布局,必须指明TextView的ID--布局中也可以包含除TextView之外的其它控件
        //ArrayAdapter adapter=new ArrayAdapter(this, android.R.layout.simple_list_item_1, list);
        ArrayAdapter adapter=new ArrayAdapter(this, R.layout.test, R.id._id, list);

        //设置ListView的Adapter对象
        listview.setAdapter(adapter);

        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                //parent--ListView
                // view--一行
                //position--适配器中的序号
                // id--row id, ListView中的序号,从0开始编号
                //大部分情况下(如:ArrayAdapter 和 SimpleAdapter 中,两者是一样的),position和id相同
                //但是,有些情况(如SimpleCursorAdapter中),row id是数据库中的_id字段的值,与position不同
                String info=list.get(position) + "  was clicked!"; //取出点击的行的内容
                Toast.makeText(ArrayAdapterActivity.this, info+"--"+id, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

 

SimpleCursorAdapter的使用方法 (已过期):

(1)通过数据库查询操作得到查询结果 --〉Cursor对象

(2)通过startManagingCursor(cursor)使得Cursor对象的生命周期能够和Activity自动同步

(3)创建SimpleCursorAdapter对象(新版替换为LoaderManager CursorLoader)

参数指定:每行的布局,Cursor对象,Cursor中的列名数组,每行布局中的控件ID--每行布局中多个控件可单独操作

(4)调用ListView对象的setAdapter()方法将刚创建的SimpleCursorAdapter对象与ListView对象结合起来。

(5)需要的话,再利用setOnItemClickListener()为ListView对象添加“点击行”的监听器。

下面的代码我解释一下,我这查的是手机中的联系人,这种高危操作肯定要申请权限了,所以我在配置文件中写了如下代码:

 这还不能获得权限(想想APP点击一个按钮后可以获取你这台手机的全部联系人,好可怕啊),还要在经过下面代码前面一部分的权限申请判断过程(不但如此,运行的时候你点击按钮后它还弹出危险提示框,问你是否允许给它这个权限,你点击allow就是允许,这样才能显示)。

package cn.edu.qtech.csc.lcb.listviewdemo;


import android.Manifest;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;


public class SimpleCursorAdapterActivity extends AppCompatActivity {

    //定义ListView对象
    private ListView listview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //在当前Activity中动态创建ListView对象
        listview=new ListView(this);

        //设置Activity的显示界面为ListView--控件ListView充满整个Activity
        setContentView(listview);

        //判断是否已经取得读联系人的权限
        //ContextCompat.checkSelfPermission()检测权限的返回值是PERMISSION_GRANTED和PERMISSION_DENIED
        if(ContextCompat.checkSelfPermission(SimpleCursorAdapterActivity.this,
                Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(SimpleCursorAdapterActivity.this,   //如果没有权限,则动态申请权限
                    new String[]{Manifest.permission.READ_CONTACTS},1);
        }
        //获取读"联系人"数据库的cursor
        Cursor cursor=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        //设置Cursor对象的生命周期和Activity自动同步
        startManagingCursor(cursor);

        //创建Adapter对象--必须指出布局及布局中的TextView的ID与Cursor中的列名的对应关系
        //SimpleCursorAdapter已经过时,建议使用 LoaderManager 和 CursorLoader
        ListAdapter adapter=new SimpleCursorAdapter(this, //上下文环境

 //               android.R.layout.simple_expandable_list_item_1, //单行的布局--使用系统定义的布局
                R.layout.test,                       //单行的布局--使用自定义的布局

                cursor,                                                         //游标对象

                new String[]{ContactsContract.Contacts.DISPLAY_NAME},

//                new int[]{android.R.id.text1});                   //显示用的TextView的ID--系统布局中定义
                new int[]{R.id._id});                   //显示用的TextView的ID--自定义布局中定义

        //设置ListView对象的Adapter
        listview.setAdapter(adapter);

        //设置点击行的监听器
        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {
                String info;
 //              info=((TextView)view).getText().toString();//取出当前行的内容显示, 若每行不是一个简单的TextView,则不能如此操作
                info=((TextView)((LinearLayout)view).findViewById(R.id._id)).getText().toString();  //自定义View区内容
                Toast.makeText(SimpleCursorAdapterActivity.this,
                        info,
                        Toast.LENGTH_SHORT).show();

            }
        });

    }

    //权限申请结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //ToDo:已授权
        } else {
            //ToDo:用户拒绝
            Toast.makeText(SimpleCursorAdapterActivity.this,"无权读取联系人信息",Toast.LENGTH_SHORT).show();
        }

    }
}

看一下我的截图:

我随便加了两个联系人,点击第二个按钮(第一次点击会有弹出权限申请框,之后就没了)后:

Android中ListView等使用的Adapter简介_第2张图片 

Android中ListView等使用的Adapter简介_第3张图片

 

SimpleAdapter的使用方法(适合每行布局复杂的列表):

(1)定义并填充存储底层数据的数据结构,:List>, Map集合类用于存储元素对(称作“键”和“值”),其中每个键映射到一个值适合于用来存储复杂数据。

(2)创建SimpleAdapter对象

参数:每行的布局,List对象,Map对象中的的键名数组,每行布局中的控件ID

(3)设置Adapter

(4)需要的话,可以添加点击行监听器

废话不多直接上代码:

package cn.edu.qtech.csc.lcb.listviewdemo;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//使用ListActivity--内部仅含有一个ListView
public class SimpleAdapterActivity extends ListActivity {

    //数据线性表--List实际上是一个线性表的接口
    List> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //创建Adapter对象
        SimpleAdapter adapter = new SimpleAdapter(this, //上下文对象
                getData(),               //存放数据的List对象
                R.layout.item,         //每行的布局
                new String[]{"title","info","img"},  //数据对象Map中的列名--键
                new int[]{R.id.title,R.id.info,R.id.img});  //列内容--Listview中的控件ID,对应控件用于显示Map对象中的值

        //为ListActivity中的ListView设置Adapter
        setListAdapter(adapter);
    }

    //获取List数据对象
    private List> getData(){
        //建立List对象--具体的ArrayList对象
        list=new ArrayList>();

        //List中存放的Map对象,由多个<键,值>对构成--一个Map对象对应ListView中的一行
        Map map;

        map=new HashMap();
        map.put("title", "牛");
        map.put("info", "食草动物,家畜");
        map.put("img", R.drawable.cow);
        list.add(map);

        map=new HashMap();
        map.put("title", "孔雀");
        map.put("info", "鸟类,开屏很好看");
        map.put("img", R.drawable.peacock);
        list.add(map);

        map=new HashMap();
        map.put("title", "熊猫");
        map.put("info", "珍稀,国宝");
        map.put("img", R.drawable.panda);
        list.add(map);

        map=new HashMap();
        map.put("title", "恐龙");
        map.put("info", "爬行类,已灭绝");
        map.put("img", R.drawable.dinosaur);
        list.add(map);

        map=new HashMap();
        map.put("title", "神龙");
        map.put("info", "神话中的动物");
        map.put("img", R.drawable.dragon);
        list.add(map);

        map=new HashMap();
        map.put("title", "北极熊");
        map.put("info", "生活在极寒之地");
        map.put("img", R.drawable.bear);
        list.add(map);

        map=new HashMap();
        map.put("title", "牛-2");
        map.put("info", "食草动物,家畜");
        map.put("img", R.drawable.cow);
        list.add(map);

        map=new HashMap();
        map.put("title", "孔雀-2");
        map.put("info", "鸟类,开屏很好看");
        map.put("img", R.drawable.peacock);
        list.add(map);

        map=new HashMap();
        map.put("title", "熊猫-2");
        map.put("info", "珍稀,国宝");
        map.put("img", R.drawable.panda);
        list.add(map);

        return list;
    }

    //重写此方法---点击一行时的回调函数--参数含义同前
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        String s=list.get(position).get("title").toString(); //获取该行的Map对象的指定属性的数据内容
        Toast.makeText(SimpleAdapterActivity.this, s, Toast.LENGTH_SHORT).show();
    }
}

 

自定义Adapter:(适合于每行布局复杂的列表,且可以操作该行中的控件)

(1)通过继承BaseAdapter可以定义自己的Adapter,可以将任何复杂组合的数据和资源,以任何想要的显示效果展示出来;

(2)BaseAdapter的使用方法与前述Adapter不一样,前述Adapter可以直接在其构造方法中进行数据的设置,但是BaseAdapter需要实现一个继承自BaseAdapter的子类,继承BaseAdapter之后,一般需要重写以下四个方法:getCount()getView() getItem()getItemId()

(3)getCount()getView()这两个方法是自定义Adapter中最为重要的方法,只要同时重写这两个方法,ListView就能完全按要求显示。而 getItem()getItemId()方法将会在调用ListView的响应方法的时候被调用到, 如果不调用其它响应方法,也可以不重写这2个方法;

(4)ListView绘制过程:系统在绘制ListView之前,(1)先调用getCount()方法来获取Item的个数;(2)之后每绘制一个Item就会调用一次getView()方法,在此方法内就可以将事先定义好的xml布局实例化,并返回一个View对象作为一个Item显示出来。

自定义Adapter的使用步骤:

定义并填充存储底层数据的数据结构;

自定义一个继承自BaseAdapter的子类MyAdapter,并实现它的2个重要方法getCount()getView()

getCount()返回底层数据结构的大小(item个数)

getView()中动态创建ListView中的一行,按需设置该行布局中的子控件的属性值和监听器。

创建MyAdapter对象;

设置Adapter

需要的话,用setOnItemClick()方法添加点击行监听器。

package cn.edu.qtech.csc.lcb.listviewdemo;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MyAdapterActivity extends ListActivity {

    //ListView的底层数据对象变量
    private List> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //获取数据
        list=getData();

        //创建自定义的Adapter对象--内部数据与ListView各项的对应关系在自定义的Adapter中实现
        MyAdapter adapter = new MyAdapter(this);

        //为ListActivity中的ListView设置Adapter
        setListAdapter(adapter);
    }

    //获取List数据对象
    private List> getData(){
        //List对象
        List> list=new ArrayList>();

        //List中存放的Map对象,由多个<键,值>对构成--一个Map对象对应ListView中的一行
        Map map;

        map=new HashMap();
        map.put("title", "牛");
        map.put("info", "食草动物,家畜");
        map.put("img", R.drawable.cow);
        list.add(map);

        map=new HashMap();
        map.put("title", "孔雀");
        map.put("info", "鸟类,开屏很好看");
        map.put("img", R.drawable.peacock);
        list.add(map);

        map=new HashMap();
        map.put("title", "熊猫");
        map.put("info", "珍稀,国宝");
        map.put("img", R.drawable.panda);
        list.add(map);

        map=new HashMap();
        map.put("title", "恐龙");
        map.put("info", "爬行类,已灭绝");
        map.put("img", R.drawable.dinosaur);
        list.add(map);

        map=new HashMap();
        map.put("title", "神龙");
        map.put("info", "神话中的动物");
        map.put("img", R.drawable.dragon);
        list.add(map);

        map=new HashMap();
        map.put("title", "北极熊");
        map.put("info", "生活在极寒之地");
        map.put("img", R.drawable.bear);
        list.add(map);

        //只要漏出一丁点,就要调用getView(...)显示该行
        map=new HashMap();
        map.put("title", "牛-2");
        map.put("info", "食草动物,家畜-2");
        map.put("img", R.drawable.cow);
        list.add(map);

        map=new HashMap();
        map.put("title", "孔雀-2");
        map.put("info", "鸟类,开屏很好看-2");
        map.put("img", R.drawable.peacock);
        list.add(map);

        return list;
    }

    //自定义的Adapter类
    /**Android系统更新ListView时需要调用相关的Adapter的方法:
     *      1)更新前首先调用getCount()获取需要更新的行数,然后更新过程逐行进行
     *      2)更新每行时,需要调用getView()获取当前行对应的View对象,
     *            Adapter需要在getView()方法中适时创建View对象,并对View对象填充需要显示的内容
     * */
    public final class MyAdapter extends BaseAdapter {
        //实例化布局对象---用于实例化每行的布局->View对象
        private LayoutInflater mInflater;

        public MyAdapter(Context context){
            this.mInflater = LayoutInflater.from(context);
        }

        //获取ListView的总行数
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public Object getItem(int arg0) {
            return null;
        }

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

        //ListView中一行对应的对象组合--容器类
        //使用ViewHolder可以减少findViewById()的使用频率,方便数据访问
        public final class ViewHolder{
            public ImageView img;
            public TextView title;
            public TextView info;
            public Button viewBtn;
        }


        //获取指定的一行所对应的View对象--不存在的话则创建之
        // position--当前要显示的数据的位置(行号)
        // convertView--可利用的以前的View对象(上下滚动时,利用旧View对象显示新内容),
        //              如果此项为空,则需要动态创建新的View对象
        // parent--父控件(上层的ListView)
        @Override
        public View getView(int position, View convertView, ViewGroup parent){
            //实例化一行的布局
            /*View rowview=mInflater.inflate(R.layout.myitem, null);
            //找到行内的子控件
            ImageView img=(ImageView)rowview.findViewById(R.id.img);
            TextView title=(TextView)rowview.findViewById(R.id.title);
            TextView info=(TextView)rowview.findViewById(R.id.info);
            Button btn=(Button)rowview.findViewById(R.id.view_btn);
            //子控件赋值
            img.setBackgroundResource((Integer)list.get(position).get("img"));
            title.setText((String)list.get(position).get("title"));
            info.setText((String)list.get(position).get("info"));

            //绑定该行中的Button对象的监听器
            //创建监听器对象时, 用参数传递当前的行号
            //每行中的Button建一个监听器对象,不同对象的position值不同
            btn.setOnClickListener(new viewButtonClickListener(position)) ;

            return rowview;*/

            //本行对应的容器对象
           ViewHolder holder = null;

            //如果该行的View为空, 则动态创建新的View
            //利用已有的View显示新数据,可以减少内存占用,优化响应速度
            if (convertView == null) {
                //先创建容器对象,以便后来向其中填充当前行中的控件对象
                holder=new ViewHolder();

                //实例化ListView的一行, root参数为空说明此View的父控件默认为为上层的ListView
                convertView = mInflater.inflate(R.layout.myitem, null);
                //获取内部的各个控件对象, 保存到容器对象中, 以后直接取来用即可--每个子控件对象只用一次findViewById()
                holder.img = (ImageView)convertView.findViewById(R.id.img);
                holder.title = (TextView)convertView.findViewById(R.id.title);
                holder.info = (TextView)convertView.findViewById(R.id.info);
                holder.viewBtn = (Button)convertView.findViewById(R.id.view_btn);

                //设置容器对象为ListView当前行的Tag--建立容器类对象与ListView当前行的联系
                convertView.setTag(holder);
            }
            else {   //如果该行的View已经存在,则通过标记获取该行对应的对象
                holder = (ViewHolder)convertView.getTag();
            }

            //设置该行内的控件对象对应的属性,Adapter的作用(List<--->ListView)--- 如果不用ViewHolder则需要频繁使用findViewByID
            holder.img.setBackgroundResource((Integer)list.get(position).get("img"));
            holder.title.setText((String)list.get(position).get("title"));
            holder.info.setText((String)list.get(position).get("info"));

            //绑定该行中的Button对象的监听器
            //创建监听器对象时, 用参数传递当前的行号
            //每行中的Button建一个监听器对象,不同对象的position值不同
            holder.viewBtn.setOnClickListener(new viewButtonClickListener(position)) ;

            return convertView;//返回当前行对应的View对象
        }
    }

    //重写此方法---点击ListView一行时的回调函数--参数含义同前
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        String s=list.get(position).get("title").toString(); //获取该行的Map对象的指定属性的数据内容
        Toast.makeText(MyAdapterActivity.this, s, Toast.LENGTH_SHORT).show();
    }

    //使用内部类实现ListView的每行中按钮的监听函数
    //该监听器类会为ListView的每一行提供一个监听器对象,用来监听该行中按钮的点击事件
    class viewButtonClickListener  implements View.OnClickListener {
        //记录按钮所在的行号
        int position;

        //必须使用自定义的构造函数---因为需要在此通过参数记录该监听器对象监听的行号
        public viewButtonClickListener(int pos) {
            position=pos;
        }

        @Override
        public void onClick(View v) {
            //获取该行的具体列的内容,并显示之
            String info=list.get(position).get("info").toString();
            Toast.makeText(MyAdapterActivity.this,info, Toast.LENGTH_SHORT).show();
        }
    }
}

 

 

 

 

 

 

你可能感兴趣的:(Android)