Android基础笔记(三)

技术分享

SQLite

SQLite,是一款轻量级的数据库,是遵循ACID(原子性、一致性、隔离性、持久性)的关联式数据库管理系统,多用于嵌入式开发中。

Android平台中嵌入了一个关系型数据库SQLite,和其他数据库不同的是SQLite存储数据时不区分类型。

例如一个字段声明为Integer类型,我们也可以将一个字符串存入,一个字段声明为布尔型,我们也可以存入浮点数。

除非是主键被定义为Integer,这时只能存储64位整数SQLite,无需安装,是Android平台自带的一个数据库。

Android下数据库的创建

什么时候才会使用到数据库技术?
有大量相似结构的数据需要增删改查的时候。
在Android中创建数据一般分为两个步骤
① 写一个类继承SQLiteOpenHelper,并实现指定的构造函数、onCreate方法、onUpgrade方法

很简略的写了一个类,其中在构造函数中详细解释了contextnamefactoryversion等几个需要传入SQLiteOpenHelper中的参数。

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

    public MySQLiteOpenHelper(Context context) {
        // context : 使用上下文环境去打开或者创建一个数据库
        // name : 数据库文件名,如果传入null,则会在内存中创建数据库
        // factory : 游标结果集工厂,如果需要使用则需要自定义结果集工厂,null值代表使用默认结果集工厂
        // version : 数据库版本,如果升级数据库时,version增加会调用onUpgrade()方法
        super(context, "my.db", null, 1);
    }
    // 数据库第一次被创建时调用该方法,这里面主要进行对数据库的初始化操作
    @Override
    public void onCreate(SQLiteDatabase db) {}
    // 数据库的版本更新的时候执行
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  }
}

创建数据库后的结果图:
技术分享

② 调用实现的数据库帮助类中的getReadableDatabase()getWritableDataBase()方法,从而创建数据库

在单元测试中,调用getReadableDatabase()getWritableDataBase()中的任意一个方法,都会创建数据库。

public void testCreate() {
    // 使用Helper帮助类创建数据库
    MySQLiteOpenHelper helper = new MySQLiteOpenHelper(getContext());

    // 无论是Writable还是Readable都会创建或者打开数据库
    SQLiteDatabase writableDatabase = helper.getWritableDatabase();
    SQLiteDatabase readableDatabase = helper.getReadableDatabase();
}
③ 结果图
技术分享
我们发现使用getReadableDatabase()getWritableDataBase()这两个都能够创建数据库,那么有什么不同之处呢?
① 源码上的不同

看一下getWritableDatabase的源码,很简单。可以发现,核心语句被加上了同步,通过望文生义也可以知道给getDatabaseLocked(true)传入true,代表了给数据库加上了锁。这相当于,在多个线程情况下可以同时操作数据库。

public SQLiteDatabase getWritableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(true);
    }
}

看一下getReadableDatabase()的源码。可以发现与上面最大的不同在于,核心语句getDatabaseLocked(false)传入了false,并没有给数据库加锁。

public SQLiteDatabase getReadableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(false);
    }
}
: ② 使用场景上的不同
getReadableDatabase()没有加锁,不能在多线程条件下对数据库进行增删改。
getWritableDatabase()加锁,可以在多线程条件下对数据库进行增删改。
如何创建数据库表?

上面的代码只是创建了一个空的数据库,里面并没有数据库表,那么数据库表该如何创建呢?也是很简单,只需要在onCreate()方法中,执行db.execSQL(sql)方法,就可以创建sql代表的表了。

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("create table info(_id integer primary key autoincrement,name varchar(20))");
}
通过上面的代码,就可以在第一次使用getReadableDatabase()getWritableDatabase()时创建数据库表了。
查看Andriod数据库db文件的两种方式
① 使用SQLite Expert Personal 3应用程序,就可以方便的查看导出的db数据库文件。

② 在adb shell下,使用Android提供的sqlite3命令,访问指定的数据库。

d:\Android-Eclipse\sdk\platform-tools>adb shell
root@android:/ # cd /data/data/com.bzh.dbcreate
cd /data/data/com.bzh.dbcreate
root@android:/data/data/com.bzh.dbcreate # cd databases
cd databases
root@android:/data/data/com.bzh.dbcreate/databases # sqlite3 my.db
sqlite3 my.db
SQLite version 3.7.11 2012-03-20 11:35:50
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> select * from info;
select * from info;
sqlite>
可以看到,虽然数据库表中没有数据,但是已经可以查询了,说明数据库表创建成功。

SQLiteDatabase简介

Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。

和JDBC访问数据库不同,操作SQLite数据库无需加载驱动,不用获取连接,直接可以使用。

执行SQL语句来操作数据库有两种方式,拼串和使用占位符?。使用占位符”?”来执行SQL语句能够防止SQL注入攻击。

① 拼串方式使用的方法

//增、删、改。
execSQL(String sql) 
 //查询(拼串方式,第二个参数传null即可)。
Cursor rawQuery(String sql, String[] selectionArgs)

② 占位符”?”使用的方法

void execSQL(String sql, Object[] bindArgs)。
Cursor rawQuery(String sql, String[] selectionArgs)。

Android下数据库的第一种增删改查方式

创建一个简单的数据库表
比较简单就不详细介绍了,两个字段name和phone

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("create table info("
            + "_id integer primary key autoincrement," 
            + "name varchar(20)," +
            "phone varchar(20)"
            + ")");

}

写一个简单的界面
技术分享

实现简单的Dao(增删改查)
代码比较简单,直接执行原生的SQL语句,并传入响应的参数,代码如下:

public class InfoDao {

    private static final String Tag = "InfoDao";
    private MyOpenHelper help;

    // 创建数据库帮助类
    public InfoDao(Context context) {
        help = new MyOpenHelper(context);
    }

    // 增加
    public void add(String name, String phone) {
        // 获取数据库操作对象
        SQLiteDatabase db = help.getWritableDatabase();
        // 执行SQL语句
        db.execSQL("insert into info(name,phone) values(?,?)", new String[] {
                name, phone });
        // 关闭数据库
        db.close();
    }

    // 删除
    public void delete(String name) {
        SQLiteDatabase db = help.getWritableDatabase();
        db.execSQL("delete from info where name=?", new String[] { name });
        db.close();
    }

    // 更新
    public void update(String phone, String name) {
        SQLiteDatabase db = help.getWritableDatabase();
        db.execSQL("update info set phone=? where name=?", new String[] {
                phone, name });
        db.close();

    }

    // 查询
    public void find() {
        SQLiteDatabase db = help.getWritableDatabase();
        // Cursor提供了随机读写数据库结果集的一些方法
        Cursor cursor = db.rawQuery("select name,phone from info", null);
        if (cursor != 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);

                Log.i(Tag, "姓名是:" + name + ", 电话是:" + phone);
            }
        }
        db.close();

    }
}

测试结果:技术分享

值得注意几点
Cursor提供了随机读写数据库结果集的一些方法
例如:将游标下移moveToNext()
根据列名获取该列对应的索引值 columnIndex = cursor.getColumnIndex("name");
根据索引获取数据String name = cursor.getString(columnIndex);
② 此种增删改查方式的弊端
SQL语句非常容易拼写错误
没有返回值,没有办法判断成功与否

Android下数据库的第二种增删改查方式

Google工程师为我们提供了更为简洁的方法(insert()delete()updte()query()),帮助我们完成增删改查操作。
与第一种增删改查不同的地方主要在于Dao文件,下面是代码,已经添加了很详细的注释:

public class InfoDao {

    private static final String Tag = "InfoDao";
    private MyOpenHelper help;
    private SQLiteDatabase db;

    // 创建数据库帮助类
    public InfoDao(Context context) {
        help = new MyOpenHelper(context);
        db = help.getWritableDatabase();
    }

    // 增加
    public boolean add(String name, String phone) {
        // Google工程师帮我们封装了更为简单的插入一行的方法

        // 表名
        String table = "info";
        // 如果写null,就无法插入一条空数据(2.3会出异常,4.0之后可以写null)
        String nullColumnHack = null;
        // 实际上是一个Map,K对应着列名,V对应着列值
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("phone", phone);

        // 插入成功返回被插入行的ID;失败返回-1
        long result = db.insert(table, nullColumnHack, values);
        return result != -1;
    }

    // 删除
    public int delete(String name) {
        // Google工程师帮我们封装了更为简单的删除一行的方法

        String table = "info";
        // 过滤条件;输入null,则删除所有行;
        String whereClause = "name=?";
        // 过滤条件对应的参数值;也就是whereClause中?号对应的实际值。
        String[] whereArgs = { name };
        // delete语句会返回执行完删除操作后,所影响的行数
        int affected = db.delete(table, whereClause, whereArgs);
        return affected;
    }

    // 更新
    public int update(String phone, String name) {
        // Google工程师帮我们封装了更为简单的更新一行的方法

        String table = "info";

        // 注意:null在此处是一个合法的值,会被转化为NULL更新到数据库
        ContentValues values = new ContentValues();
        values.put("phone", phone);
        String whereClause = "name=?";
        String[] whereArgs = { name };

        int affected = db.update(table, values, whereClause, whereArgs);
        return affected;
    }

    // 查询
    public void find() {

        String table = "info";

        // 查询哪些指定的列;如果传入null,表示返回所有列;
        String[] columns = { "name", "phone" };

        // 过滤条件;如果传入null,表示不进行过滤,返回所有行;
        String selection = null;
        // 过滤条件中?号对应的值
        String[] selectionArgs = null;

        // 分组条件;如果传入null,表示不分组;
        String groupBy = null;
        // 分组后的过滤条件;传入null,表示不过滤;
        String having = null;

        // 排序条件;传入null,进行默认排序ASC
        String orderBy = null;

        // 分页条件;传入null,不进行分页;
        String limit = null;

        // 更为简易的查询方法
        Cursor cursor = db.query(table, columns, selection, selectionArgs,
                groupBy, having, orderBy, limit);
        if (cursor != 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);

                Log.i(Tag, "姓名是:" + name + ", 电话是:" + phone);
            }
        }
    }
}

测试结果:
技术分享

Android下数据库的事务

1.Andriod下的事务也是很简单,只涉及到了beginTransaction()setTransactionSuccessful()endTransaction()三个方法,具体的使用如下:

// 事务操作
private void transaction() {
    MyOpenHelper helper = new MyOpenHelper(getApplicationContext());
    SQLiteDatabase db = helper.getWritableDatabase();

    // 使用独占模式开启一个数据库事务
    db.beginTransaction();
    try {
        db.execSQL("update info set money=money-100 where name=‘张三‘");
        int i = 1 / 0;
        db.execSQL("update info set money=money+100 where name=‘李四‘");

        // 给事务做一个成功的标记
        db.setTransactionSuccessful();
    } catch (Exception e) {
        Toast.makeText(getApplicationContext(), "系统忙..正在维护...请稍后再试...",
                Toast.LENGTH_SHORT).show();
    } finally {
        // 结束事务,包括提交和回滚,需要放在finally中执行,否则事务只有到超时的时候才自动结束,会降低数据库并发效率
        db.endTransaction();
    }
}

测试结果如下所示:
技术分享

查询数据库的结果:
技术分享

2.事务对效率的提高

在批量修改数据的时候,由于事务是在进行事务提交时将要执行的SQL操作一次性打开数据库连接执行,其执行速度比逐条执行SQL语句的速度快了很多倍。因此当我们开发中遇到对数据库的批量操作那么,使用事务是提高效率的重要原则。

案例:插入一万条数据到数据库,比较使用事务和不使用事务各自所需的时间。

public void testTransactionEfficient(){
        PersonOpenHelper helper = new PersonOpenHelper(getContext(), "person", null, 2);
        SQLiteDatabase database = helper.getWritableDatabase();
//      ------测试不使用事务时插入1w条数据耗时--------------------
        long beginTime = System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            database.execSQL("insert into person(name,age,phone) values(‘text‘+"+i+","+i+",‘"+(1320000+i)+""+"‘)");
        }
        long endTime = System.currentTimeMillis();
        System.out.println("不使用事务插入1w条数据耗时:"+(endTime-beginTime)+"毫秒");
//      ---------测试使用事务时耗时-----------------------
        beginTime = System.currentTimeMillis();
        database.beginTransaction();
        for(int i=0;i<10000;i++){
            database.execSQL("insert into person(name,age,phone) values(‘text‘+"+i+","+i+",‘"+(1320000+i)+""+"‘)");
        }
        database.setTransactionSuccessful();
        database.endTransaction();
        endTime = System.currentTimeMillis();
        System.out.println("使用事务插入1w条数据耗时:"+(endTime-beginTime)+"毫秒");
    }

执行上面代码,查看控制台,发现不使用事务耗时19397毫秒,使用事务耗时3404毫秒,性能差别还是相当的明显。

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

}

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。