Android ListView基本使用

来源:互联网 发布:内存数据库有哪些 编辑:程序博客网 时间:2024/06/04 19:26

扯淡

不太喜欢UI设计,一是自身比较偏重功能设计,一是没有艺术美感,不擅长布局。当然,不同的人,有不同的layout。扯远了~
ListView是很常用的,比较基本的控件。但相对于其他普通控件,如TextView,Button之类还是相对繁琐。因此,对其基本使用进行简单的总结。

ListView的分类

这里将ListView分为三类:显示型,控件响应型,数据库绑定型。

显示型,仅显示内容,并无控件响应设计。比如,每行仅包含TextView或者ImageView。通常使用ArrayAdapter(每行仅显示一个String),或SimpleAdapter(可以为多种控件的组合)。

控件响应型,例如每行都有按钮,点击后对该行进行相应操作。通常对BaseAdapter进行继承扩充。这里需要指出的是,若使用SimpleAdapter也可以完成界面的显示,但无法响应控件的监听事件。

数据库绑定型,则是将数据库内容通过cursor与ListView进行绑定,并实现相关数据库操作。通常使用SimpleCursorAdapter。

本篇由于篇幅原因,不对SimpleCursorAdapter进行讲解,在对数据库总结时再做描述

ArrayAdapter

创建一个ListView需要有两步操作,
(1)给每行Item创建一个布局文件。
(2)给ListView创建一个Adapter。而Adapter的作用就是绑定布局文件和显示数据,将各个数据与对应的控件ID(显示位置)相关联。

先以最简单的显示型为例。

(1)创建布局文件

需要指出的是,由于之后使用的ArrayAdapter,布局文件必须以TextView作为BOOT。

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/tv_show"    android:layout_width="match_parent"    android:layout_height="match_parent"></TextView>

(2)创建ArrayAdapter

首先声明控件和私有变量

@BindView(R.id.lv_3)ListView mlv3;private ArrayAdapter<String> mArrayAdapter;

初始化数据源

private List<String> getArrayAdapterViewListData(){        List<String> list=new ArrayList<String>();        list.add("ArrayItem1");        list.add("ArrayItem2");        list.add("ArrayItem3");        list.add("ArrayItem4");        return list;    }

创建Adapter

mArrayAdapter=new ArrayAdapter<String>(this,R.layout.arraylistviewitem,getArrayAdapterViewListData());mlv1.setAdapter(mArrayAdapter);

可以看到ArrayAdapter有三个参数:Context(谁来创建),R.layout.arraylistviewitem(每行的布局),getArrayAdapterViewListData()(数据源)。

如果要对数据进行添加,删除或者修改,则仅需要对ArrayAdapter中的数据进行操作,并在此调用mlv1.setAdapter(mArrayAdapter);即可。

ArrayAdapter Demo

例如,通过点击,选择要删除的行,点击ListView下方的删除按钮,删除该行。点击ListView下方的添加按钮,弹出输入窗口,完成新一行的添加
(1)为ListView添加监听事件,以记录被点击的Item的Index.

private int mArrayAdapterCurrentID=-1;
mlv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {                mArrayAdapterCurrentID=i;            }        });

这里需要强调的是onItemClick的后两个参数。
int i,表示position,是指item在ArrayAdapter中的Index。
long l,表示item在list中的行号。从0开始。
两者通常相等,但如果list存在标题栏,则会造成偏移。

(2)更新ArrayAdapter,在删除按钮中添加监听代码

if(mArrayAdapterCurrentID>=0){                    mArrayAdapter.remove(mArrayAdapter.getItem(mArrayAdapterCurrentID));                    mlv1.setAdapter(mArrayAdapter);                    mArrayAdapterCurrentID=-1;//删除后,position会发生变化,因此设置positon无效。                }

(3)点击添加按钮,弹出窗口。
弹出窗口并非本文重点,可参考
http://www.cnblogs.com/gzdaijie/p/5222191.html,窗口类型8来实现。

(4)更新ArrayAdapter。

final EditText input=new EditText(this);                AlertDialog.Builder addItemDialog=new AlertDialog.Builder(this);                addItemDialog.setTitle("add Item");                addItemDialog.setView(input);                addItemDialog.setPositiveButton("add", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialogInterface, int i) {                        if(input.getText()!=null){                            String newItem;                            newItem=input.getText().toString();                            if(newItem.length()>0&& mArrayAdapter.getPosition(newItem)<0){                                mArrayAdapter.add(newItem);                                mlv1.setAdapter(mArrayAdapter);                                mArrayAdapterCurrentID=-1;                            }                        }                    }                });                addItemDialog.setNegativeButton("cancel", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialogInterface, int i) {                    }                });                addItemDialog.show();

修改操作,就不再啰嗦,毕竟是删除和添加的组合。

SimpleAdapter

有时,我们的ListView并没有那么简单,可能存在多个显示用的控件,如何将数据与显示位置一一对应?SimpleAdapter便派上用场了。

其实SimpleAdapter与ArrayAdapter最大的不同,仅限于构造函数!

SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

context:谁创建我。

data:数据源。所有数据存在于List中,这与arrayAdapter相同。而不同之处在于,每行数据以Map的形式进行保存,而非arrayAdapter中的简单String。每行中的数据单元以key&value的形式存储。

resource:每行所用的layout。

from:Map中的key。

to:与Map中的key所对应的layout中的控件resource ID。这样每行中的数据单元,就与每行中显示位置相对应了。

SimpleAdapter Demo

(1)为每行创建公用的布局文件

<?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"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal">    <ImageView        android:id="@+id/iv_pic"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_weight="1"        app:srcCompat="@mipmap/ic_launcher_round" />    <TextView        android:id="@+id/tv_name"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="TextView" />    <TextView        android:id="@+id/tv_age"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="TextView" />    <TextView        android:id="@+id/tv_tel"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="TextView" /></LinearLayout>

(2)组织数据源

    private List<Map<String,Object>> getSimpleAdapterViewListData(){        List<Map<String, Object>> list=new ArrayList<Map<String, Object>>();        Map<String, Object> item1=new HashMap<String, Object>();        item1.put("name","user1");        item1.put("age","age1");        item1.put("tel","tel1");        item1.put("pic",R.mipmap.ic_launcher);        list.add(item1);        ....        Map<String, Object> itemN=new HashMap<String, Object>();        itemN.put("name","userN");        itemN.put("age","ageN");        itemN.put("tel","telN");        itemN.put("pic",R.mipmap.ic_launcher);        list.add(itemN);        return list;    }

这里需要注意的是,对于Resource,给出的不是Object(比如, Image对象,Video对象)而是Resource ID,否则Resource将无法显示

(3)构造SimpleAdatpter对象

SimpleAdapterm SimpleAdapter=new SimpleAdapter(this,                getSimpleAdapterViewListData(),                R.layout.simplelistviewitem,                new String[]{"name","age","tel","pic"},                new int[]{R.id.tv_name,R.id.tv_age,R.id.tv_tel,R.id.iv_pic});

Key与Resource ID一一对应。

(4)显示

mlv1.setAdapter(mSimpleAdapter);

对于添加/修改/删除操作,就不再多说了,实现上跟arrayAdapter并无不同。

BaseAdapter

开篇也提过,若ListView中存在需要监听的控件(例如,每行中都存在按钮,点击后,对该行进行相应操作),那么SimpleAdapter便无能为力了,我们需要从BaseAdapter进行重新继承。当然,SimpleAdapter和ArrayAdapter都是BaseAdapter的子类。

BaseAdapter是一抽象类,继承BaseAdapter后,需要实现以下四个接口。

    @Override    public int getCount() {        return 0;    }    @Override    public Object getItem(int i) {        return null;    }    @Override    public long getItemId(int i) {        return 0;    }    @Override    public View getView(int i, View view, ViewGroup viewGroup) {        return null;    }

(1)getView()

其中最为重要的是getView方法。该方法用于显示ListView中每行的内容。
先解释一下getView中的重要参数。

i:position,即当前行所显示的内容在Adapter数据源中的位置。
view:容器view,即每行的布局。可以从该对象中调用findViewbyID,来获取对应的控件对象。

系统为了加快显示速度,同时又控制资源的使用,会把最近使用的View暂放于内存。例如,ListView有N条记录,且整个ListView无法全部显示。当拖到滚动条时,ListView会调用getView显示各行。

当系统资源不足时,会释放部分内存。因此,在实现该getView方法时,需要对容器view进行是否为null的判断。

如果为空,则需要重新初始化View(该行)中的各个控件变量,并用View的setTag,保存控件变量组,我们称为holder,可以视为Views,即该行的控件组合。
如果不为空,则调用View的getTag获取该行的控件组合。

(2)Holder

Holder可以看做是一个组合,该组合包含了该行中所包括的所有控件。
那么问题来了。

如何对Holder中的控件进行初始化呢?view.findViewbyId(R.id.xxxx)。view即为getView的第二个传参。有了控件,我们便可以对其设置监听了。

如果资源不足,导致view为null,我们该如何获取view呢?

view=LayoutInflater.from(context).inflate(R.layout.list_item,null);

而context为创建Adapter时所使用的context。因此我们要在构造Adapter时,要求以context作为传参。当然,数据源保存至Adapter也是有必要的。

我们直接通过一个Demo代码来体会。

BaseAdapter Demo

(1)创建布局

<?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"    android:orientation="horizontal">    <TextView        android:id="@+id/tv_type"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_weight="1"        android:text="TextView" />    <TextView        android:id="@+id/tv_format"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_weight="1"        android:text="TextView" />    <TextView        android:id="@+id/tv_date"        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_weight="1"        android:text="TextView" />    <Button        android:id="@+id/btn_del"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_weight="1"        android:text="Button" /></LinearLayout>

(2)定义holder

public class myholder {    public TextView mType;    public TextView mFormat;    public TextView mDate;    public Button mDel;}

(3)组织数据源

private List<Map<String,Object>> getMyBaseAdapterViewListData(){        List<Map<String, Object>> list=new ArrayList<Map<String, Object>>();        Map<String, Object> item1=new HashMap<String, Object>();        item1.put("type","Type1");        item1.put("format","format1");        item1.put("date","date1");        list.add(item1);        ...        Map<String, Object> itemN=new HashMap<String, Object>();        itemN.put("type","TypeN");        itemN.put("format","formatN");        itemN.put("date","dateN");        list.add(itemN);        return list;    }

(4)创建BaseAdapter扩展类

public class MyAdapter extends BaseAdapter {    private final String TAG=getClass().getName();    private Context mContext;    private List<Map<String, Object>> mdata;    public MyAdapter(Context context, List<Map<String, Object>> data){        mContext=context;        mdata=data;    }    @Override    public int getCount() {        return mdata!=null?mdata.size():0;    }    @Override    public Object getItem(int i) {        return mdata!=null?mdata.get(i):null;    }    @Override    public long getItemId(int i) {        return 0;    }    @Override    public View getView(final int i, View view, ViewGroup viewGroup) {        myholder holder=new myholder();        if(view==null){            view= LayoutInflater.from(mContext).inflate(R.layout.mybaseadapterlistviewitem,null);            holder.mType=(TextView) view.findViewById(R.id.tv_type);            holder.mFormat=(TextView) view.findViewById(R.id.tv_format);            holder.mDate=(TextView) view.findViewById(R.id.tv_date);            holder.mDel=(Button) view.findViewById(R.id.btn_del);            holder.mDel.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View view) {                    Log.i(TAG, "onClick: delete "+i);                    mdata.remove(i);                    notifyDataSetInvalidated();                }            });            view.setTag(holder);        }else {            holder=(myholder) view.getTag();        }        holder.mType.setText(mdata.get(i).get("type").toString());        holder.mFormat.setText(mdata.get(i).get("format").toString());        holder.mDate.setText(mdata.get(i).get("date").toString());        holder.mDel.setText("delete");        return view;    }}

这里需要注意的是,在更新数据源后,一定要调用notifyDataSetInvalidated()方法,通知ListView更新显示!

当然,对于BaseAdapter扩展类的设计可以根据自己的需求随意,例如使用回调函数传入监听函数,加入dialog提示框等。

至此,ListView的基本使用总结完毕。

原创粉丝点击