Android基础笔记(四)

  • Android下ListView控件入门
  • Android下ListView优化
  • Android下ListView复杂item的显示
  • Android下常用的数据适配器ArrayAdapter
  • 把数据库数据显示到ListView上

Android下ListView控件入门

简介
ListView是我们Android中最重要的控件之一,是用于对数据进行列表展示的控件。
特点
① 屏幕上可以展示几个控件, ListView就初始化几个,节省内存,防止内存溢出。
② 通过使用convertView对创建的视图对象进行复用,ListView始终保持创建的对象个数为: 屏幕显示的条目的个数 + 1。
③ ListView自带ScrollView的功能,可以实现界面滚动。

④ ListView控件的设计遵循MVC设计模式:

  1. mode 数据模型(数据) :要被显示到ListView上的数据集合
  2. view 视图(展示数据) : ListView
  3. controller控制层(把数据展示到空间上) : 适配器Adapter
Android官方的API介绍
A view that shows items in a vertically scrolling list. The items come from the ListAdapter associated with this view.
简单来说就是,ListView是一个View(视图),可以以滚动的list展示许多数据条目;使用ListAdapter类把数据项与View关联起来。
简单使用ListView控件的步骤

① 在布局中添加ListView标签,如下:

<ListView
      android:id="@+id/lv"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />

② 在代码中找到控件,并为其设置实现ListAdapter接口的适配器,如下:

ListView lv = (ListView) findViewById(R.id.lv);
ListAdapter adapter = new MyAdapter();
lv.setAdapter(adapter);
③ 创建内部类,实现ListAdapter接口。这里有些需要注意的需要说明。
ListAdapter接口中,有大量需要实现的方法,显而易见,在实际工作中每次都实现一遍,需要花费巨大的工作量。这一点,Google工程师,也帮我们考虑到了,为我们提供了几个默认的实现类,类图如下:


这里我们使用的就是BaseAdapter,创建一个内部类,继承它,代码如下,当然这些只是简单的实现,后续还会继续深入。

class MyAdapter extends BaseAdapter {

// 适配器中有多少个数据项需要被显示
    @Override
    public int getCount() {
        return 100;
    }

    // 获取指定位置上的数据项,默认不实现;使用的很少
    @Override
    public Object getItem(int position) {
        return position;
    }

    // 获取指定位置数据项的ID,默认不实现;使用的很少
    @Override
    public long getItemId(int position) {
        return position;
    }

    // 获取一个视图展示数据到指定位置(position)
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 这里只给一个简单的实现
        TextView tv = new TextView(getApplicationContext());
        tv.setText("喜欢学习Android,第(" + position + ")天");
        tv.setTextColor(Color.RED);
        tv.setTextSize(23);
        return tv;
    }

}

值得说明的:手机屏幕的高度有限制,而在getCount()返回的是100,需要向下不断的滑动,才能全部加载显示初始来。而一个屏幕能够显示多少个条目与以下几点有关系:

1.. 屏幕的高度

2.. 数据项(item)的高度

测试结果:

Android下ListView优化

说优化之前,要明确为什么需要优化
① 在创建Android模拟器时,默认设置了手机为每个应用程序分配的虚拟机内存只有16M,请看图:

这说明,我们的应用程序中创建的对象最大值是有大小限制的,如果我们无限制的去创建对象,一旦超出16M,并且垃圾回收的速度赶不上对象创建对象的速度,那么就会出现OOM异常。

② 上个案例中在getView()方法中,每次都是创建新的TextView对象,代码如下:

// 获取一个视图展示数据到指定位置(position)
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // 这里只给一个简单的实现
    TextView tv = new TextView(getApplicationContext());
    tv.setText("喜欢学习Android,第(" + position + ")天");
    tv.setTextColor(Color.RED);
    tv.setTextSize(23);
    return tv;
}
而我们在getCount()返回的是100,也就是说,如果要全部显示完,那么需要创建100个对象。如果们让getCount()设置为Integer.MAX_VALUE,要全部显示完 ,需要创建个对象,这显然是非常恐怖的。
但我们快速的滑动时,可以看到LogCat在不停的打印垃圾回收的消息。

那么该如何优化?

通过使用getView参数中的View convertView复用历史对象!

代码如下:

public View getView(int position, View convertView, ViewGroup parent) {
    TextView tv = null;
    // 判断历史缓存对象为空则代表还没有创建对象
    if (convertView == null) {
        tv = new TextView(getApplicationContext());
        Log.i(TAG, "新创建了TextView对象,当前位置(" + position + ")");
    } else {
        // 复用
        tv = (TextView) convertView;
        Log.i(TAG, "利用converView复用的历史对象,当前位置(" + position + ")");
    }

    tv.setText("喜欢学习Android,第(" + position + ")天");
    tv.setTextColor(Color.RED);
    tv.setTextSize(23);
    return tv;
}

当我们运行程序,稍微向下滑动一点点时,历史对象就已经开始复用了。

而当我们快速向下滑动时,依旧使用的历史对象,也并未新建对象。

除此之外在ListView还有隐藏起来的一个小问题

在之前的布局文件中,ListView标签的android:layout_height="match_parent"属性值都是match_parent,如果我们改成包裹内容wrap_content,并把代码中的getCount()返回值改成3,再运行程序会发生什么?

<ListView
      android:id="@+id/lv"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />
public int getCount() {
    return 3;
}

运行起来后,界面如我们所想,显示了3个数据条目:

但是我们看看,LogCat中发生了什么?

竟然调用了很多次创建和多次复用!这是由于ListView的布局文件中高度使用的是warp_content,在加载布局时,会不断的测定ListView的高度,并不断加载所造成的。这样也会造成性能的损耗,而避免的方法也很简单,用到ListView标签的地方,高度使用match_parent就可以了。

最后,ListView中可以优化的地方还有很多,这些就等在以后再叙述了。

Android下ListView复杂item的显示

在之上的例子中,getView()方法中获取的View都是我们手动创建的对象。而查看API可以看到这句话:You can either create a View manually or inflate it from an XML layout file。

它简单的意思是,即可以手工创建一个View,也可以使用inflate把一个XML布局文件加载成为View对象。

那么下面我们就这样做,在getView()方法中,加载一个复杂一些的布局。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // 这里就暂时不复用历史对象了

    // 解释一下inflate三个参数的含义
    // 参数1:使用上下文环境
    // 参数2:被加载的XML布局文件ID
    // 参数3:传入null时,返回整个XML布局对象(ViewGorup),包括ViewGroup的所有子布局;不为null,只返回XML布局对象的父元素;
    View view = View.inflate(getApplicationContext(), R.layout.item,
            null);
    TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);
    TextView tvContent = (TextView) view.findViewById(R.id.tv_content);

    tvTitle.setText("标题(" + position + ")");
    tvContent.setText("当前位置:" + position);
    return view;
}

这里,最重要的就是使用View.inflate()方法来加载XML布局文件,具体参数的含义也已经在代码中解释了。

测试结果:

Android下布局填充的三种方式

方式一

View view = View.inflate(getApplicationContext(), R.layout.item,
                    null);

方式二

View view1 = LayoutInflater.from(getApplicationContext()).inflate(
                    R.layout.item, null);

方式三

View view2 = ((LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE))
                    .inflate(R.layout.item, null);

关于LayoutInflater的更多详细信息,可以参看我的另一个文章:Android LayoutInflater 详解

Android下常用的数据适配器(ArrayAdapter)

在所有的适配器中,最常用的莫过于BaseAdapter了,但是除了它,Android还提供了一些其他易用的适配器。

快速的使用ArrayAdapter

① 写一个用于显示内容的布局文件(items.xml),如下:

<?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="vertical" >

    <TextView
        android:id="@+id/tv"
        android:textSize="25sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView" />

</LinearLayout>

② 为ListView设置ArrayAdapter适配器,代码如下:

ListView lv = (ListView) findViewById(R.id.lv);
// 上下文对象
Context context = getApplicationContext();
// XML布局文件ID
int resource = R.layout.item;
// 被显示的TextView控件的ID
int textViewResourceId = R.id.tv;
// 被显示的数据
String[] objects = {"别志华","胡玉琼"};
ListAdapter adapter = new ArrayAdapter<String>(context, resource, textViewResourceId, objects);
lv.setAdapter(adapter );

很简单吧!测试结果如下:

把数据库数据显示到ListView上

这个例子,需要使用到Android下数据库的第二种增删改查方式中的InfoDao和MyOpenHelper两个类

那我们在实际应用中,应该怎么使用呢?一般分为以下几个步骤

① 在布局中增加<ListView>标签

<ListView
     android:id="@+id/lv"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
</ListView>

② 为适配器准备一个item子布局,内容很简单,里面有两个<TextView>

<?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" >

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="name"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/tv_phone"
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="phone"
        android:textSize="18sp" />

</LinearLayout>

③ 把数据库中获取到的数据封装成List<Bean>集合,需要准备beanDao

public class InfoBean {
    private String name;
    private String phone;
}

操作也比较简单,拿到游标循环给Bean设置属性,并添加到List集合中。

// 查询
public List<InfoBean> find() {
    Cursor cursor = db.query("info", new String[] { "name", "phone" },
            null, null, null, null, null, null);
    List<InfoBean> infoList = null;
    if (cursor != null) {
        infoList = new ArrayList<InfoBean>();
        InfoBean info = null;
        while (cursor.moveToNext()) {
            int columnIndex = 0;
            columnIndex = cursor.getColumnIndex("name");
            String name = cursor.getString(columnIndex);
            columnIndex = cursor.getColumnIndex("phone");
            String phone = cursor.getString(columnIndex);
            // 设置info属性
            info = new InfoBean();
            info.setName(name);
            info.setPhone(phone);
            // 存入集合中
            infoList.add(info);
        }
    } // end if
    return infoList;
}

④ 找到布局文件中ListView控件,做成成员变量,为其设置适配器。获取到数据集合,也做成成员变量,这样才可以在内部类中MyAdapter中访问。

lv = (ListView) findViewById(R.id.lv);
// 查询
public void find(View v) {
    infoList = dao.find();
    if (infoList == null) {
        throw new RuntimeException();
    }
    lv.setAdapter(new MyAdapter());
}

一个比较完整的适配器如下,需要注意的两点:1. 在getCount()中使用了return infoList.size();返回,即动态又实效。2. 在getView()中使用InfoBean info = infoList.get(position);当前条目的position来获取真实的数据,用以展示。

class MyAdapter extends BaseAdapter {

    @Override
    public int getCount() {return infoList.size();  }

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // 复用对象
        View view = null;
        if (convertView == null) {
            view = ((LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(R.layout.item, null);
        } else {
            view = convertView;
        }

        // 找到item中的控件,并设置值
        InfoBean info = infoList.get(position);
        TextView tvName = (TextView) view.findViewById(R.id.tv_name);
        TextView tvPhone = (TextView) view.findViewById(R.id.tv_phone);
        tvName.setText(info.getName());
        tvPhone.setText(info.getPhone());

        return view;
    }

}

时间: 2024-08-26 13:51:38

Android基础笔记(四)的相关文章

Android学习笔记四:添加Source

问题描述 Source not foundThe JAR file D:\.....\sdk\platforms\android-20\android.jar has no source attachment. 问题原因及解决办法 1. 使用SDK Manager下载最新版本的Sources for Android SDK 一般文件下载目录默认在SDK下的sources文件中即 \adt-bundle-windows-x86_64-20130522\sdk\sources\android-20

Java for Android 基础笔记-数据类型

Java的基本数据类型 布尔类型 boolean true | false java是一个强类型的语言,与JS中的布尔类型的自由转换相比,JAVA的Boolean类型只有两种true和false,JS中相应的只可以自行转换. 字符类型 char 可以存储一个汉字,和其他语言非常类似不再赘述. 整型 byte, 占用一个字节 -128`-127 short 2字节 -2^15~2^15-1 int 4字节 -2^31~2^31-1 long 8字节  -2^63~2^63-1 整型常量默认为int

Android 面试题总结之Android 基础(四)

Android 面试题总结之Android 基础Service(四) 在上一章节Android 面试题总结之Android 基础Broadcast Receiver(三) 我们讲了Broadcast Receiver基础知识.本节主要讲解Service相关基础知识,Service也是我们开发过程中经常使用到. 在阅读过程中有任何问题,请及时联系.如需转载请注明 fuchenxuan de Blog 本章系<Android 之美 从0到1 – 高手之路>Android基础Service 总结了A

Android基础笔记(十四)- 内容提供者读取联系人

利用内容提供者读取联系人 利用内容提供者插入联系人 内容观察者的原理 利用内容观察者监听系统应用数据库或者自己应用数据库的变化 利用内容提供者读取联系人 读取联系人相对于读取短信来说就复杂很多了,我们一步一步来吧. 先看看一下联系人的数据库,是位于什么地方! 既然很复杂,我们就一步步分析吧,我们把contacts2.db导出到电脑中,并使用SQLite数据库软件打开.你可以看到一大堆的表和视图,当然我们使用到的也只有三张.分别是raw_contacts.data.mimetypes分别存储着联系

Android基础笔记(十七)- 多媒体编程

MediaPlayer播放音频的基本使用 音乐播放器的完善 视频播放SurfaceView 视频播放VideoView vitamio框架 如何调用照相机和录像机 MediaPlayer播放音频的基本使用 MediaPlayer播放音频的基本使用步骤: ①创建一个MediaPlayer实例 ②设置播放的数据格式 ③设置数据源 ④准备播放 ⑤开始播放 整体的示例代码很简单: // 1. 创建一个音频播放器实例 final MediaPlayer player = new MediaPlayer()

Android基础笔记(一)

1G - 5G的介绍 Android的操作系统的介绍 Android版本 Android系统的架构 两种虚拟机的不同 ART模式 模拟器的简介 SDK目录 Android工程目录结构 打包的过程 ADB常用指令 案例电话拨号器 四种点击事件 Android的常用布局 Andriod中的单位 1G - 5G的介绍 1st - Generation 代表作是:大哥大,只能进行语音通信,不能够发短信.更不能上网. 2nd - Generation 代表作是:小灵通,可以收发短信.可以上网,但是速度几十

Android读书笔记四

第四章 这是一次源代码之旅,学到了如何下载和编译Android源代码和Linux内核源代码.来详细阐述一下一些具体过程 一.Android源代码下载环境 1.安装下载Android源代码的环境配置 (1)先创建一个用于存放下载脚本文件repo的目录 (2)下载repo脚本文件 (3)创建用于存放Android代码的目录: make dir android_source cd android_source (4)初始化 (5)开始下载Android源代码: repo sync 2.下载Androi

Android基础笔记(十五)- 图形、解决大图OOM、绘画工具的使用和练习

计算机图形表示的原理 加载大图出现OOM 缩放加载大的图片资源 创建一个原图的副本 图形处理的常用的API 傻瓜版美图秀秀 画画版 计算机图形表示的原理 首先要明确的一点是,一张图片的在内存中存储所需的大小和图片在屏幕设备上完整显示所需的内存大小是有非常大的差异的. 如下图,从中我们可以清晰的看出这张图片,在硬盘上所占的存储空间是303KB,也就是310272个字节.但是如果想把这样图片完整的展示到屏幕设备上,所需的内存空间远远不止这些. 有这样一个计算公式:图片展示所需内存 = 图片的宽度像素

Android基础笔记(十)- 帧动画、补间动画详解、对话框

帧动画 补间动画Tween Animation 对话框以及面试中的注意点 帧动画 帧动画很简单,我们首先看一下Google官方解释This is a traditional animation in the sense that it is created with a sequence of different images. 意思表达的很明了,一个传统的动画是由一组不同的图片组成的.帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果. 创建一帧动画分为一下几步(支持4