布局与控件(九)-ListView的Adapter们

第10节 ListView的Adapter

安卓系统为ListView设计了多种Adapter作为它的搭档。每种Adapter不仅为ListView提供数据内容,也会告诉ListView如何展示这些数据-规定好列表项的长相。

这些Adapter都是从Adapter类继承而来的,它们的关系如下:

这里我们选择性的介绍常见的2种Adapter-ArrayAdapterSimpleAdapter

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的时候,

  1. 要声明这个Adapter要接受什么类型的数据,

    new ArrayAdapter<String>(......)
  2. 指定布局文件的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);
  3. 传入要显示的数据内容,这个数据内容可以以数组的方式传入,也可以以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。

它的设计思路是,

  1. 每一条列表项上哪些控件希望显示数据都要告诉SimpleAdaperSimpleAdaper只要知道这些控件的ID就可以了。所以在创建的时候,需要传入这些控件的ID值(以数组的形式);

  2. 每一项待显示的数据,要和列表上的控件一一对应。这就需要一个翻译系统,给每个位置的数据取个名字,将待显示数据挨个放入键值对的列表中(map);并将每个位置的数据名字和控件ID值对应起来;

  3. SimpleAdapter在绑定数据到界面上的时候,就根据对应关系,一个一个把它们放上去。

例如,

  1. 准备要显示的数据,让每个数据项,和一个特定的名字(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);
  2. 将特定的名称做成数组,和显示区域的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};
  3. 创建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文件中,

  1. 数据项显示在水平布局的LinearLayout中;
  2. 视频缩略图用ImageView控件显示,给它的android:scaleType属性设置center,让缩略图居中放置,背景设置成应用主题的色调colorPrimary
  3. 其他视频信息包含标题和创建时间,将它们竖直排列放在一个LinearLayout中,占用高度按照2:1分配,前者使用主题中较大的字体?android:attr/textAppearanceMedium,后者使用主题中较小的字体?android:attr/textAppearanceSmall
  4. 至于各个组件之间的间隔,根据自己的视觉偏好调整就好了,用android:paddingandroid: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也继承自它。

  1. 继承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;
        }
    }
  2. 创建构造函数,在构造函数中,保存好布局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;
    }
  3. 实现getCount()函数,返回当前数据项的个数,

    @Override
    public int getCount() {
        return mData != null ? mData.size() : 0;
    }
  4. 实现getItem()函数,根据传入的索引号,返回对应的数据项,

    @Override
    public Object getItem(int position) {
        return mData != null ? mData.get(position): null ;
    }
  5. 实现getItemId()函数,根据传入的索引号,返回对应项的id值,

    @Override
    public long getItemId(int position) {
        return position;
    }
  6. getView()函数中,创建数据项的布局,并为他们赋值,最后将这个布局返回给ListView,让它显示,

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

    这里的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

  1. 在Video List的Activity创建之时,我们在onCreate()中创建并设置VideoAdapter

    @Override
    protected 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);
    
    }
  2. 当列表中的数据有变化时,在主线程中更新数据列表,并使用notifyDataSetChanged()刷新,

    VideoItem data = xxx;
    
    mVideoList.add(data);
    VideoItemAdapter adapter = (VideoItemAdapter) mVideoListView.getAdapter();
    adapter.notifyDataSetChanged();

10.3.5 改进提高效率

在前面实现自定义Adapter的getView()函数中,没有每次都创建一个convertView,而是复用已有的布局,这样就节省重新创建的资源。不过每次都使用findViewById()也会花掉不少的开销。

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

为此,我们可以引入一个数据结构,将这些控件保存下来,在使用的时候直接获取,不需要进行耗时的findViewById()操作了。

  1. 创建一个数据类,准备存放控件,

    private class Holder {
        public TextView title;
        public TextView createTime;
        public ImageView thumb;
    }
  2. 创建布局的时候,通过创建Holder将控件保存起来,

    @Override
    public 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);
        }
        ......
    }
  3. 获取Holder,直接使用,提高效率,

    @Override
    public 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;
    }

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
本文由腾讯WeTest团队提供,未经授权严禁转载!更多资讯可直接戳链接查看: http://wetest.qq.com/lab/   微信号:TencentWeTest 小编导读:无论是开发还是发行,不可避免的会遇到包体过大需要压缩的情况。 对于发行商来说,尽管现在wifi遍地,但就算移动运营有一天开放4G免费,包体越小的你依然具备优势。 对于玩家来说则更为简单明了,用户喜欢连续点击几个应用同时下载,包体小的很快就跑完读条更容易被玩家接受。 但真正压缩的时候遇到的麻烦事可真的不少:对游戏整体的压缩却不影响
通讯录数据的存取 有选择性的翻译自: https://developer.android.com/training/contacts-provider/index.html Contacts Provider 是用户通信信息仓库,包含通讯录应用程序和社交网络应用程序的数据。我们可以通过直接调用ContactsResolver的方法或直接发送调用通讯录应用程序的intent来获取Contacts Provider提供的信息。 目录 通讯录数据的存取 目录 获取通讯录列表 匹配通信人姓名 定义ListView
转载请注明出处 http://blog.csdn.net/ghostwolfliu/article/details/51596911 转载请注明出处 http://www.cnblogs.com/coldcode/p/5564687.html 简介 由于其他项目中断了几天更新,继续~~这一篇主要是讲光照的(包含漫反射和高光以及多光源的处理)还是先来看看具体效果(多光源后面单独展示) 有了基本的光照处理之后越来越有立体感了有不有 ╮(╯▽╰)╭ 最基本的漫反射 原理 恩~~ 这次我们先来解释下原理比较好 比
这几天还是在做那个项目 有一个部分是需要有一个类似微信朋友圈那样的功能 开始自己实现是用RecycleView嵌套RecycleView 然后已经把别的弄好了 动态图片那块还没有加上结果我不会搞也没有找到栗子 然后就换了一个思路 看到有listview+gridview的栗子就照着做了一个 先看一下现在的效果 呐 这个是listview嵌套Gridview实现的 评论那些我在recycleview里面做好了之后再加 觉得 把这个嵌套弄好了真是超级开心呐; 嗯 就再回顾一下说一下思路; 1 listview

关于安卓性能和内存 - 2016-06-07 14:06:21

内存相关的问题在面试中被问到的概率还是比较大的,而且内存优化对于一个程序的性能而言也是至关重要的,现在就让我们一起来学习吧! 不废话,直接上干货~ 一、内存泄漏 内存泄漏就是我们对某一内存空间的使用完成后没有释放。 主要原因:导致内存泄漏最主要的原因就是某些长存对象持有了一些其它应该被回收的对象的引用,导致垃圾回收器无法去回收掉这些对象。 出现的场景: 1.数据库的cursor没有关闭; 2.构造adapter时,没有使用缓存contentview; 3.Bitmap对象不使用时采用recycle()释放
写在前面: AsyncTask不用多介绍,今天不说怎样使用,我带大家看看AsyncTask的进化史,希望大家能从中有所收获。顺便问一句:你认为你应用中实例化多个AsyncTask去execute,这些AsyncTask都在高效的并发运行吗? 在很久很久以前(2.3以前) 一群可爱的程序猿发现了一个叫做AsyncTask的东西,觉得它很好用,比起Thread来方便多了。于是AsyncTask一夜间红遍五大洲四大洋。可是用着用着,一个细心的程序猿(比如说我)发现了一个问题。在应用中使用了5个AsyncTask
之前笔者已经讲过了LauchMode的作用,以及尽量避开栈的概念使用GIF图片的方式尽可能简单地阐述了一下Activity的启动模式,这篇文章就再次深入,好好讲一下在各种启动模式下,Activity与任务栈到底是如何作用的。 如果还是刚入门的读者,建议还是先看一下笔者的前一篇文章。 上一篇文章地址: http://blog.csdn.net/double2hao/article/details/50929431 任务栈: (笔者此处就复制一下官方文档中的解释) 如果对任务栈有深入了解兴趣的,可以看一下官方
本文为楼主原创,转载请表明出处: http://blog.csdn.net/Suma_sun/article/details/51584026 在程序开发中,为了让程序表现的更快更流畅,我们会使用多线程来提升应用的并发性能。但多线程并发代码是一个棘手的问题,线程的生命周期处理不好就会造成内存泄漏。 new Thread(){ @Override public void run () { doSomeThing(); } }.start(); 上面给出了一个最简单的新建线程并执行任务,也是最不好管理的。为什
Monkey测试文档 Monkey介绍: Monkey是Android中的一个命令行工具,可以运行在模拟器里或实际设备中。它向系统发送伪随机的用户事件流(如按键输入、触摸屏输入、手势输入等),实现对正在开发的应用程序进行压力测试。Monkey测试是一种为了测试软件的稳定性、健壮性的快速有效的方法。 Monkey的特征 1、 测试的对象仅为应用程序包,有一定的局限性。 2、 Monky测试使用的事件流数据流是随机的,不能进行自定义。 3、 可对Test的对象,事件数量,类型,频率等进行设置。 Monkey测

MJExtension的使用 - 2016-06-06 14:06:08

前言 MJExtension是一套“字典和模型之间互相转换”的轻量级框架 MJExtension能完成的功能 字典 – 模型 模型 – 字典 字典数组 – 模型数组 模型数组 – 字典数组 具体用法主要参考 “NSObject+MJKeyValue.h” 实例 1.简单的字典 - 模型 // // MJUser.h // 字典与模型的互转 // 用户模型 // # import Foundation/Foundation.htypedef enum { SexMale, SexFemale} Sex; @