布局与控件(九)-ListView的Adapter们
来源:互联网 发布:网络兼职网 编辑:程序博客网 时间:2024/05/17 05:19
第10节 ListView的Adapter
安卓系统为ListView
设计了多种Adapter
作为它的搭档。每种Adapter
不仅为ListView
提供数据内容,也会告诉ListView
如何展示这些数据-规定好列表项的长相。
这些Adapter
都是从Adapter类
继承而来的,它们的关系如下:
这里我们选择性的介绍常见的2种Adapter
-ArrayAdapter
和SimpleAdapter
。
10.1 ArrayAdapter
ArrayAdapter
是最简单的Adapter
,我们在前面已经使用过它,
ListView lv = (ListView) findViewById(R.id.list_view);String data[] = {"a", "b", "c"};ArrayAdapter adapter = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1 , data);lv.setAdapter(adapter);lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //添加需要响应的操作 }});
在创建ArrayAdapter
的时候,
要声明这个
Adapter
要接受什么类型的数据,new ArrayAdapter<String>(......)
指定布局文件的
layout id
,android.R.layout.simple_list_item_1
这个布局就是一个
TextView
,只不过被指定了android:id
为@android:id/text1
,<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" ....../>
注意这里在给控件指定ID的时候用的是
`@id/text1
,而没有用+
。这表明,这个ID值是已经存在的,而不需要编译器再单独分配。这样
ArrayAdapter
会自动寻找这个布局文件中ID名字为text1
的控件,并认为它就是可以用来显示数据的TextView
;<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" ....../>
如果不想用系统给
TextView
指定的ID,也可以给自己定义的布局的TextView
取个名字,例如show_content_textview
。<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/show_content_textview" ....../>
然后向
ArrayAdapter
再传入一个ID,告诉它将要显示的文字,放到这个ID所指示的控件上,ArrayAdapter adapter = new ArrayAdapter<String>(context, R.layout.custom_item_layout, R.id.show_content_textview, data);
传入要显示的数据内容,这个数据内容可以以数组的方式传入,也可以以
List
的方式放入,//数组方式传入String [] data = {"a","b","c"};ArrayAdapter adapter = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1 , data);//列表方式传入List data =new ArrayList<String>();data.add("a");data.add("b");data.add("c");ArrayAdapter adapter = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1 , data);
10.2 SimpleAdapter
看得出ArrayAdapter
是相当的简单,只能在单一的TextView
上显示一条数据。如果有好几条不同类型的数据要显示到同一列表项的不同部位,那就不好办了。SimpleAdapter
正好能帮我们解决这样的问题。虽然它叫Simple,不过不是说功能simple,而是使用很simple。
它的设计思路是,
每一条列表项上哪些控件希望显示数据都要告诉
SimpleAdaper
,SimpleAdaper
只要知道这些控件的ID就可以了。所以在创建的时候,需要传入这些控件的ID值(以数组的形式);每一项待显示的数据,要和列表上的控件一一对应。这就需要一个翻译系统,给每个位置的数据取个名字,将待显示数据挨个放入键值对的列表中(map);并将每个位置的数据名字和控件ID值对应起来;
SimpleAdapter
在绑定数据到界面上的时候,就根据对应关系,一个一个把它们放上去。
例如,
准备要显示的数据,让每个数据项,和一个特定的名字(title pic content)对应上,
Map<String, Object> family = new HashMap<String, Object>();family.put("title", "家");family.put("pic", "http://xxx.xxx.com/family.jpg");family.put("content", "I love my family");Map<String, Object> dog = new HashMap<String, Object>();dog.put("title", "狗");dog.put("pic", "http://xxx.xxx.com/dog.jpg");dog.put("content", "I love my dog");List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();dataList.add(family);dataList.add(dog);
将特定的名称做成数组,和显示区域的ID一一对应上,例如,
title <-> R.id.title_id
pic <-> R.id.pic_id
content <-> R.id.content_id
String [] from = {"title", "pic", "content"};int [] to = {R.id.title_id, R.id.pic_id, R.id.content_id};
创建
SimpleAdapter
,adapter = new SimpleAdapter(this, dataList, android.R.layout.simple_list_item_1 , from, to);
10.3 自定义Adapter
虽然Android SDK为我们提供了好几种现成的Adapter使用,但有时它们也并不能完全符合我们的要求,要么用起来还是麻烦,要么大材小用。另外,为了把ListView
介绍的全面一些,我们准备自定义一个Adapter。
10.3.1 定义数据项的布局
为了让列表的数据项按照我们设计的模样显示,我们需要为它设计一个布局,用展示的视频列表为例,加以说明。
数据项的布局定义在res\layout\video_item.xml
文件中,
- 数据项显示在水平布局的
LinearLayout
中; - 视频缩略图用
ImageView
控件显示,给它的android:scaleType
属性设置center
,让缩略图居中放置,背景设置成应用主题的色调colorPrimary
; - 其他视频信息包含标题和创建时间,将它们竖直排列放在一个
LinearLayout
中,占用高度按照2:1分配,前者使用主题中较大的字体?android:attr/textAppearanceMedium
,后者使用主题中较小的字体?android:attr/textAppearanceSmall
; - 至于各个组件之间的间隔,根据自己的视觉偏好调整就好了,用
android:padding
和android:layout_margin
设置;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/vidoe_thumb" android:layout_width="150dp" android:layout_height="100dp" android:scaleType="center" android:padding="5dp" android:layout_margin="5dp" android:background="@color/colorPrimary"/> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/video_title" android:layout_width="match_parent" android:layout_height="0dp" android:gravity="center_vertical" android:layout_margin="2dp" style="?android:attr/textAppearanceMedium" android:lines="2" android:layout_weight="2"/> <TextView android:id="@+id/video_date" android:layout_width="match_parent" android:layout_height="0dp" android:gravity="center_vertical" android:layout_margin="2dp" style="?android:attr/textAppearanceSmall" android:singleLine="true" android:layout_weight="1"/> </LinearLayout></LinearLayout>
10.3.2 定义数据项内容
为每个视频信息,定义一个数据结构VideoItem
,
public class VideoItem { String name; String path; Bitmap thumb; String createdTime; VideoItem(String strPath, String strName, String createdTime) { this.path = strPath; this.name = strName; ...... }}
10.3.3 定义Adapter
所有Adapter
都是继承自BaseAdapter
的,我们自定义的Adapter
也继承自它。
继承
BaseAdapter
,准备实现必须实现的基类函数;public class VideoItemAdapter extends BaseAdapter { @Override public int getCount() { return 0; } @Override public Object getItem(int position) { return null ; } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { return null; }}
创建构造函数,在构造函数中,保存好布局ID,以便以后使用,通过
Context
获取Inflater
,为以后数据项布局的创建做准备;保存要展示的数据项们;private List<VideoItem> mData;private final LayoutInflater mInflater;private final int mResource;public VideoItemAdapter(Context context, int resId, List<VideoItem> data){ mData = data; mInflater = LayoutInflater.from(context); mResource = resId;}
实现
getCount()
函数,返回当前数据项的个数,@Overridepublic int getCount() { return mData != null ? mData.size() : 0;}
实现
getItem()
函数,根据传入的索引号,返回对应的数据项,@Overridepublic Object getItem(int position) { return mData != null ? mData.get(position): null ;}
实现
getItemId()
函数,根据传入的索引号,返回对应项的id值,@Overridepublic long getItemId(int position) { return position;}
在
getView()
函数中,创建数据项的布局,并为他们赋值,最后将这个布局返回给ListView
,让它显示,@Overridepublic View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(mResource, parent, false); } VideoItem item = mData.get(position); TextView title = (TextView) convertView.findViewById(R.id.video_title); title.setText(item.name); TextView createTime = (TextView) convertView.findViewById(R.id.video_date); createTime.setText(item.createdTime); ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb); thumb.setImageBitmap(item.thumb); return convertView;}
这里的
convertView
就是数据项所代表的那个布局,当ListView
刚创建,还没有产生任何数据项的时候,它就是为null
的,此时我们就需要创建一个布局,并通过getView()
将这个布局返回给ListView
。假如
ListView
上的数据项布局已经足够了,那么这里传入的convertView
就不会再是“null”,而是之前的某个数据项布局,我们就不必为此重新创建了,只需要更新上面的内容就好。这样提高了界面刷新的效率。当然,这里还能通过其他方法减少使用
findViewById()
,进一步提高效率,不过目前就不改进了,先把功能实现完成。
综合以上内容,最后的代码就是,
public class VideoItemAdapter extends BaseAdapter { private List<VideoItem> mData; private final LayoutInflater mInflater; private final int mResource; public VideoItemAdapter(Context context, int resId, List<VideoItem> data) { mData = data; mInflater = LayoutInflater.from(context); mResource = resId; } @Override public int getCount() { return mData != null ? mData.size() : 0; } @Override public Object getItem(int position) { return mData != null ? mData.get(position): null ; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(mResource, parent, false); } VideoItem item = mData.get(position); TextView title = (TextView) convertView.findViewById(R.id.video_title); title.setText(item.name); TextView createTime = (TextView) convertView.findViewById(R.id.video_date); createTime.setText(item.createdTime); ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb); thumb.setImageBitmap(item.thumb); return convertView; }}
10.3.4 使用自定义Adapter
在Video List的Activity创建之时,我们在
onCreate()
中创建并设置VideoAdapter
,@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_list); mVideoList = new ArrayList<VideoItem>(); mVideoListView = (ListView) findViewById(R.id.video_list); VideoItemAdapter adapter = new VideoItemAdapter(this, R.layout.video_item, mVideoList); mVideoListView.setAdapter(adapter); //设置数据项被点击的监听器 mVideoListView.setOnItemClickListener(this);}
当列表中的数据有变化时,在主线程中更新数据列表,并使用
notifyDataSetChanged()
刷新,VideoItem data = xxx;mVideoList.add(data);VideoItemAdapter adapter = (VideoItemAdapter) mVideoListView.getAdapter();adapter.notifyDataSetChanged();
10.3.5 改进提高效率
在前面实现自定义Adapter的getView()
函数中,没有每次都创建一个convertView
,而是复用已有的布局,这样就节省重新创建的资源。不过每次都使用findViewById()
也会花掉不少的开销。
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { //节省了资源 if (convertView == null) { convertView = mInflater.inflate(mResource, parent, false); } VideoItem item = mData.get(position); //消耗了资源 TextView title = (TextView) convertView.findViewById(R.id.video_title); title.setText(item.name); //消耗了资源 TextView createTime = (TextView) convertView.findViewById(R.id.video_date); createTime.setText(item.createdTime); //消耗了资源 ImageView thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb); thumb.setImageBitmap(item.thumb); return convertView;}
为此,我们可以引入一个数据结构,将这些控件保存下来,在使用的时候直接获取,不需要进行耗时的findViewById()
操作了。
创建一个数据类,准备存放控件,
private class Holder { public TextView title; public TextView createTime; public ImageView thumb;}
创建布局的时候,通过创建
Holder
将控件保存起来,@Overridepublic View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(mResource, parent, false); Holder holder = new Holder(); holder.title = (TextView) convertView.findViewById(R.id.video_title); holder.createTime = (TextView) convertView.findViewById(R.id.video_date); holder.thumb = (ImageView) convertView.findViewById(R.id.vidoe_thumb); //存放到View当中 convertView.setTag(holder); } ......}
获取
Holder
,直接使用,提高效率,@Overridepublic View getView(int position, View convertView, ViewGroup parent) { ...... //从View当中取出Holder Holder holder = (Holder) convertView.getTag() VideoItem item = mData.get(position); holder.title.setText(item.name); holder.createTime.setText(item.createdTime); holder.thumb.setImageBitmap(item.thumb); return convertView;}
/*******************************************************************/
* 版权声明
* 本教程只在CSDN和安豆网发布,其他网站出现本教程均属侵权。
*另外,我们还推出了Arduino智能硬件相关的教程,您可以在我们的网店跟我学Arduino编程中购买相关硬件。同时也感谢大家对我们这些码农的支持。
*最后再次感谢各位读者对安豆
的支持,谢谢:)
/*******************************************************************/
- 布局与控件(九)-ListView的Adapter们
- Adapter类型控件之ListView Item多布局的实现
- Android控件:ListView与Adapter
- 关于adapter布局中有Button控件时,导致ListView的onItemClick无法触发的问题
- ListView与Adapter的关系
- ListView与Adapter的关系
- listView与adapter的用法
- Adapter与ListView的合作
- Listview 控件的头尾布局
- Android listview加载不同布局与adapter用法
- Android教程(九)-- ListView控件的使用
- [转]Android:listView控件,adapter,以及其他控件的组合
- 布局与控件(七)-ListView知多少(上)
- 布局与控件(八)-ListView知多少(下)ChoiceMode详解
- ListView多布局Adapter封装
- Android UI设计——ListView控件与Adapter适配器(一)
- Android UI设计:ListView 控件与 Adapter 适配器
- LIstView与Adapter的配合使用
- hibernate的hql查询语句总结
- 欢迎使用CSDN-markdown编辑器
- Androi:ListView+GridView实现仿微信微博朋友圈无焦点冲突
- ftp命令批处理下载文件
- PADS盲孔和NC的输出方法
- 布局与控件(九)-ListView的Adapter们
- 在h18客户端上使用hive操作数据库
- java生成二维码
- easy ui 如何从一个tab 跳转到另外一个tab
- margin负边距布局
- BackgroundSubtractorMOG 前景检测
- 25个简单shell例子
- Qt for Android 开发大坑 2
- ViewPager手势滑动禁用