要学习ListView,首先应该掌握适配器,那么什么是适配器呢?
适配器就是在安卓中,把数据变成符合界面风格的形式,并且通过ListView显示出来。也就是说适配器是数据和界面之间的桥梁。
今天我们主要讲三种适配器:
1.ArrayAdapter:简单的数据映射,只包含文字数据。
2.SimpleAdapter:文字和图片映射,内容相对丰富了一丢丢。
3.自定义的Adapter:无所不能,瓶颈在于你自己的水平。
对于前两种,Android内部已经封装好,比如你想点击一项ListView的某一行的某一个控件,前两个精度在于某一行,而自定义的可以精确到某一行的某一个控件。不过第三种要求自己定义,会比较麻烦,功能不太复杂时,前两种可以直接使用。(当然adapter不止这三种)
1.ArrayAdapter:
任务:把数组里的数据映射到ListView中,点击listView的每一行,都会显示所点击的行的数据;
第一步:当然要在xml文件中定义ListView了,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ArrayAdapter ListView test..." />
<ListView
android:id="@+id/simpleListViewControll"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
第二步:建立activity文件,用adapter把数据更改为能被界面显示的数据,然后用ListView读取并显示。
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<String>对象---Model
private List<String> 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<String>();
//添加数据内容
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<String>对象
//如果要使用自定义的布局,必须指明TextView的ID--布局中也可以包含除TextView之外的其它控件
ArrayAdapter<String> adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
//ArrayAdapter<String> adapter=new ArrayAdapter<String>(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();
}
});
}
}
小伙伴有没有发现注释了一行,ArrayAdapter虽然不能控制对于行的操作,但是可以控制每一行的格式,其中android.R.layout.simple_list_item_1是一个Android的内置文件,只用于显示一段文字,如果自定义格式的话,记得一定要包含TextView,并且让他们对应起来(比如上述代码,需要建立一个名为test.xml的文件,找到TextView的id,与list对应起来)。
如果使用自定义,我们怎么定义控制格式的布局文件呢?我们需要新建立一个布局文件(下面这个文件夹即为test.xml),代码如下,大家可以自己补充格式:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0000"/>
</LinearLayout>
两种方式的有什么不同呢?下面展示结果:
2.SimpleAdapter:
任务:把数组里的文本和图片映射到ListView中,点击listView的每一行,都会显示所点击的行的数据:
第一步:仍然是创建布局文件item.xml,然后添加ListView控件。代码如下:
<?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"
android:orientation="horizontal">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:textSize="28sp" />
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
第二步:建立activity文件,用adapter把数据更改为能被界面显示的数据,然后用ListView读取并显示(图片资源这里不给提供,代码运行肯定会出一些问题,大家随便找到图片,命名为下面代码的名字,然后放到drawable中)。
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<Map<String,Object>> 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<Map<String,Object>> getData(){
//建立List对象--具体的ArrayList对象
list=new ArrayList<Map<String,Object>>();
//List中存放的Map对象,由多个<键,值>对构成--一个Map对象对应ListView中的一行
Map<String, Object> map;
map=new HashMap<String,Object>();
map.put("title", "牛");
map.put("info", "食草动物,家畜");
map.put("img", R.drawable.cow);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "孔雀");
map.put("info", "鸟类,开屏很好看");
map.put("img", R.drawable.peacock);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "熊猫");
map.put("info", "珍稀,国宝");
map.put("img", R.drawable.panda);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "恐龙");
map.put("info", "爬行类,已灭绝");
map.put("img", R.drawable.dinosaur);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "神龙");
map.put("info", "神话中的动物");
map.put("img", R.drawable.dragon);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "北极熊");
map.put("info", "生活在极寒之地");
map.put("img", R.drawable.bear);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "牛-2");
map.put("info", "食草动物,家畜");
map.put("img", R.drawable.cow);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "孔雀-2");
map.put("info", "鸟类,开屏很好看");
map.put("img", R.drawable.peacock);
list.add(map);
map=new HashMap<String,Object>();
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();
}
}
运行结果如下,点击恐龙之后:
3:自定义的adapter:
任务:把数组里的文本和图片映射到ListView中,点击listView的每一行的每一个内容,都会显示所点击的内容的数据,点击删除会删除这一行:
既然是重点,那就再详细的想想:
(1):如何实现点击行显示行内容:
这个前两个也可以实现,因为系统内部定义的有点击ListView的监听函数,可以精确到第几行,检测到点击直接操作就行了。
(2):如何实现删除功能:
点击删除的时候需要执行删除功能,问题在于系统提供的ListView点击监听器无法使用,因为只能监测到行,但是检测不到你点了Button控件。为什么呢,肯定是因为没给控件添加监听器。那添加一个不就行了。这就是自定义的好处,你可以自己给数据绑定,自己给数据添加内容。
第一步:仍然是先写布局文件:
这里应该注意,要把Button的focusable的属性设置为false,否则按钮下面的行无法获得点击事件。
<?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"
android:orientation="horizontal">
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="180dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:textSize="28sp" />
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF000000"
android:textSize="18sp" />
</LinearLayout>
<Button
android:id="@+id/view_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="删除"
android:layout_gravity="right|center_vertical"
android:layout_margin="10dp"
android:focusable="false"/>
<!-- 此处的focusable的属性必须为false,否则按钮下面的行无法获得点击事件 -->
</LinearLayout>
第二步:建立activity文件,自己创建Adapter类,内部自己写数据的绑定,自己添加监听器。然后再把自己定义的Adapter把数据转化为可以被界面显示的,放进ListView中。
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<Map<String, Object>> list;
MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取数据
list=getData();
//创建自定义的Adapter对象--内部数据与ListView各项的对应关系在自定义的Adapter中实现
adapter = new MyAdapter(this);
//为ListActivity中的ListView设置Adapter
setListAdapter(adapter);
}
//获取List数据对象
public List<Map<String,Object>> getData(){
//List对象
List<Map<String,Object>> list=new ArrayList<Map<String,Object>>();
//List中存放的Map对象,由多个<键,值>对构成--一个Map对象对应ListView中的一行
Map map;
map=new HashMap<String,Object>();
map.put("title", "牛");
map.put("info", "食草动物,家畜");
map.put("img", R.drawable.cow);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "孔雀");
map.put("info", "鸟类,开屏很好看");
map.put("img", R.drawable.peacock);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "熊猫");
map.put("info", "珍稀,国宝");
map.put("img", R.drawable.panda);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "恐龙");
map.put("info", "爬行类,已灭绝");
map.put("img", R.drawable.dinosaur);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "神龙");
map.put("info", "神话中的动物");
map.put("img", R.drawable.dragon);
list.add(map);
map=new HashMap<String,Object>();
map.put("title", "北极熊");
map.put("info", "生活在极寒之地");
map.put("img", R.drawable.bear);
list.add(map);
//只要漏出一丁点,就要调用getView(...)显示该行
map=new HashMap<String,Object>();
map.put("title", "牛-2");
map.put("info", "食草动物,家畜-2");
map.put("img", R.drawable.cow);
list.add(map);
map=new HashMap<String,Object>();
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){
//本行对应的容器对象
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) {
//从数据源data中删除数据
list.remove(list.get(position));
//通知适配器更新UI
adapter.notifyDataSetChanged();
}
}
}
删除前和删除一部分之后运行结果如下:
上述所有文件的源码:https://download.youkuaiyun.com/download/qq_38367681/10777729