Android核心基础(三)

1、使用嵌入式关系型SQLite数据库存储数据

Android平台上,集成了一个嵌入式关系型数据库—SQLiteSQLite3支持 NULLINTEGERREAL(浮点数字)、TEXT(字符串文本)BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)char(n)decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。 SQLite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。 另外, SQLite 在解析CREATE TABLE 语句时,会忽略 CREATE TABLE 语句中跟在字段名后面的数据类型信息,如下面语句会忽略 name字段的类型信息:

CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))

SQLite可以解析大部分标准SQL语句,如:

查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句

如:select * from person

        select * from person order by id desc

        select name from person group by name having count(*)>1

分页SQLmysql类似,下面SQL语句获取5条记录,跳过前面3条记录

select * from Account limit 5 offset 3 或者 select * from Account limit 3,5

插入语句:insert into 表名(字段列表) values(值列表)。如: insert into person(name, age) values(‘传智’,3)

更新语句:update 表名 set 字段名=值 where 条件子句。如:update person set name=‘传智‘ where id=10

删除语句:delete from 表名 where 条件子句。如:delete from person  where id=10

2、使用SQLiteOpenHelper对数据库进行版本管理

我们在编写数据库应用软件时,需要考虑这样的问题:因为我们开发的软件可能会安装在很多用户的手机上,如果应用使用到了SQLite数据库,我们必须在用户初次使用软件时创建出应用使用到的数据库表结构及添加一些初始化记录,另外在软件升级的时候,也需要对数据表结构进行更新。那么,我们如何才能实现在用户初次使用或升级软件时自动在用户的手机上创建出应用需要的数据库表呢?总不能让我们在每个需要安装此软件的手机上通过手工方式创建数据库表吧?因为这种需求是每个数据库应用都要面临的,所以在Android系统,为我们提供了一个名为SQLiteOpenHelper的抽象类,必须继承它才能使用,它是通过对数据库版本进行管理来实现前面提出的需求。 

为了实现对数据库版本进行管理,SQLiteOpenHelper类提供了两个重要的方法,分别是onCreate(SQLiteDatabase db)onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion),前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结构。当调用SQLiteOpenHelpergetWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法,onCreate()方法在初次生成数据库时才会被调用,onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的,假设数据库现在的版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这一目的,可以把原来的数据库版本设置为2(有同学问设置为3行不行?当然可以,如果你愿意,设置为100也行),并且在onUpgrade()方法里面实现表结构的更新。当软件的版本升级次数比较多,这时在onUpgrade()方法里面可以根据原版号和目标版本号进行判断,然后作出相应的表结构及数据更新。

getWritableDatabase()getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例。但getWritableDatabase() 方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用getWritableDatabase()打开数据库就会出错。getReadableDatabase()方法先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。

public class DatabaseHelper extends SQLiteOpenHelper {

    //类没有实例化,是不能用作父类构造器的参数,必须声明为静态

         private static final String name = "itcast"; //数据库名称

         private static final int version = 1; //数据库版本

         public DatabaseHelper(Context context) {

//第三个参数CursorFactory指定在执行查询时获得一个游标实例的工厂类,设置为null,代表使用系统默认的工厂类

                super(context, name, null, version);

         }

        @Override public void onCreate(SQLiteDatabase db) {

              db.execSQL("CREATE TABLE IF NOT EXISTS person (personid integer primary key autoincrement, name varchar(20), age INTEGER)");   

         }

        @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

               db.execSQL(" ALTER TABLE person ADD phone VARCHAR(12) NULL "); //往表中增加一列

// DROP TABLE IF EXISTS person 删除表

       }

}

在实际项目开发中,当数据库表结构发生更新时,应该避免用户存放于数据库中的数据丢失。

3、使用SQLiteDatabase操作SQLite数据库

Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。对SQLiteDatabase的学习,我们应该重点掌握execSQL()rawQuery()方法。 execSQL()方法可以执行insertdeleteupdateCREATE TABLE之类有更改行为的SQL语句; rawQuery()方法用于执行select语句。

execSQL()方法的使用例子:

SQLiteDatabase db = ....;

db.execSQL("insert into person(name, age) values('传智播客', 4)");

db.close();

执行上面SQL语句会往person表中添加进一条记录,在实际应用中, 语句中的“传智播客”这些参数值会由用户输入界面提供,如果把用户输入的内容原样组拼到上面的insert语句, 当用户输入的内容含有单引号时,组拼出来的SQL语句就会存在语法错误。要解决这个问题需要对单引号进行转义,也就是把单引号转换成两个单引号。有些时候用户往往还会输入像“ ”这些特殊SQL符号,为保证组拼好的SQL语句语法正确,必须对SQL语句中的这些特殊SQL符号都进行转义,显然,对每条SQL语句都做这样的处理工作是比较烦琐的。 SQLiteDatabase类提供了一个重载后的execSQL(String sql, Object[] bindArgs)方法,使用这个方法可以解决前面提到的问题,因为这个方法支持使用占位符参数(?)使用例子如下:

SQLiteDatabase db = ....;

db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"传智播客", 4}); 

db.close();

execSQL(String sql, Object[] bindArgs)方法的第一个参数为SQL语句,第二个参数为SQL语句中占位符参数的值,参数值在数组中的顺序要和占位符的位置对应。

SQLiteDatabaserawQuery() 用于执行select语句,使用例子如下:
 SQLiteDatabase db = ....;

Cursor cursor = db.rawQuery(select * from person, null);

while (cursor.moveToNext()) {

int personid = cursor.getInt(0); //获取第一列的值,第一列的索引从0开始

String name = cursor.getString(1);//获取第二列的值

int age = cursor.getInt(2);//获取第三列的值

}

cursor.close();

db.close(); 

rawQuery()方法的第一个参数为select语句;第二个参数为select语句中占位符参数的值,如果select语句没有使用占位符,该参数可以设置为null。带占位符参数的select语句使用例子如下:

Cursor cursor = db.rawQuery("select * from person where name like ? and age=?", new String[]{"%传智%", "4"});

Cursor是结果集游标,用于对结果集进行随机访问,如果大家熟悉jdbc, 其实CursorJDBC中的ResultSet作用很相似。使用moveToNext()方法可以将游标从当前行移动到下一行,如果已经移过了结果集的最后一行,返回结果为false,否则为true。另外Cursor 还有常用的moveToPrevious()方法(用于将游标从当前行移动到上一行,如果已经移过了结果集的第一行,返回值为false,否则为true )、moveToFirst()方法(用于将游标移动到结果集的第一行,如果结果集为空,返回值为false,否则为true )和moveToLast()方法(用于将游标移动到结果集的最后一行,如果结果集为空,返回值为false,否则为true ) 。

除了前面给大家介绍的execSQL()rawQuery()方法, SQLiteDatabase还专门提供了对应于添加、删除、更新、查询的操作方法: insert()delete()update()query() 。这些方法实际上是给那些不太了解SQL语法的菜鸟使用的,对于熟悉SQL语法的程序员而言,直接使用execSQL()rawQuery()方法执行SQL语句就能完成数据的添加、删除、更新、查询操作。

Insert()方法用于添加数据,各个字段的数据使用ContentValues进行存放。 ContentValues类似于MAP,相对于MAP,它提供了存取数据对应的put(String key, Xxx value)getAsXxx(String key)方法,  key为字段名称,value为字段值,Xxx指的是各种常用的数据类型,如:StringInteger等。

SQLiteDatabase db = databaseHelper.getWritableDatabase();

ContentValues values = new ContentValues();

values.put("name", "传智播客");

values.put("age", 4);

long rowid = db.insert(person, null, values);//返回新添记录的行号,与主键id无关

不管第三个参数是否包含数据,执行Insert()方法必然会添加一条记录,如果第三个参数为空,会添加一条除主键之外其他字段值为Null的记录。Insert()方法内部实际上通过构造insert SQL语句完成数据的添加,Insert()方法的第二个参数用于指定空值字段的名称,相信大家对该参数会感到疑惑,该参数的作用是什么?是这样的:如果第三个参数values Null或者元素个数为0, 由于Insert()方法要求必须添加一条除了主键之外其它字段为Null值的记录,为了满足SQL语法的需要, insert语句必须给定一个字段名,如:insert into person(name) values(NULL),倘若不给定字段名 , insert语句就成了这样: insert into person() values(),显然这不满足标准SQL的语法。对于字段名,建议使用主键之外的字段,如果使用了INTEGER类型的主键字段,执行类似insert into person(personid) values(NULL)insert语句后,该主键字段值也不会为NULL。如果第三个参数values 不为Null并且元素的个数大于,可以把第二个参数设置为null

delete()方法的使用:

SQLiteDatabase db = databaseHelper.getWritableDatabase();

db.delete("person", "personid<?", new String[]{"2"});

db.close();

上面代码用于从person表中删除personid小于2的记录。

update()方法的使用:

SQLiteDatabase db = databaseHelper.getWritableDatabase();

ContentValues values = new ContentValues();

values.put(name“传智播客”);//key为字段名,value为值

db.update("person", values, "personid=?", new String[]{"1"}); 

db.close();

上面代码用于把person表中personid等于1的记录的name字段的值改为“传智播客”。

query()方法实际上是把select语句拆分成了若干个组成部分,然后作为方法的输入参数:

SQLiteDatabase db = databaseHelper.getWritableDatabase();

Cursor cursor = db.query("person", new String[]{"personid,name,age"}, "name like ?", new String[]{"%传智%"}, null, null, "personid desc", "1,2");

while (cursor.moveToNext()) {

         int personid = cursor.getInt(0); //获取第一列的值,第一列的索引从0开始

        String name = cursor.getString(1);//获取第二列的值

        int age = cursor.getInt(2);//获取第三列的值

}

cursor.close();

db.close(); 

上面代码用于从person表中查找name字段含有“传智”的记录,匹配的记录按personid降序排序,对排序后的结果略过第一条记录,只获取2条记录。

query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit)方法各参数的含义:

table:表名。相当于select语句from关键字后面的部分。如果是多表联合查询,可以用逗号将两个表名分开。

columns:要查询出来的列名。相当于select语句select关键字后面的部分。

selection:查询条件子句,相当于select语句where关键字后面的部分,在条件子句允许使用占位符“?

selectionArgs:对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。

groupBy:相当于select语句group by关键字后面的部分

having:相当于select语句having关键字后面的部分

orderBy:相当于select语句order by关键字后面的部分,如:personid desc, age asc;

limit:指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分。

数据库实例

package com.itheima.db.dao;

import java.util.ArrayList;

import java.util.List;

import android.content.ContentValues;

import android.content.Context;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import com.itheima.db.PersonDBOpenHelper;

import com.itheima.db.domain.Person;

/**

 * person表 增删改查的工具类

 * 

 * @author Administrator

 * 

 */

public class PersonDao {

private PersonDBOpenHelper helper;

// 在构造方法里面 初始化了数据库的帮助类

public PersonDao(Context context) {

helper = new PersonDBOpenHelper(context);

}

/**

 * 

 * @param name

 * @param phone

 * @return 返回值代表添加这一行的id  返回-1代表插入失败

 */

public long add(String name, String phone) {

SQLiteDatabase db = helper.getWritableDatabase();

// db.execSQL("insert into person (name,phone) values (?,?)", new

// Object[]{name,phone});

// 其实就是一个map集合

ContentValues values = new ContentValues();

values.put("name", name);

values.put("phone", phone);

long result = db.insert("person", null, values);

db.close();

return result;

}

/**

 * 

 * @param name

 * @return  删除成功多少行

 */

public int delete(String name) {

SQLiteDatabase db = helper.getWritableDatabase();

//db.execSQL("delete from person where name=?", new Object[]{name});

int result = db.delete("person", "name=?", new String[] { name });

db.close();

return result;

}

public int  update(String name, String newphone) {

SQLiteDatabase db = helper.getWritableDatabase();

// db.execSQL("update person set phone=? where name=?", new

// Object[]{newphone,name});

ContentValues values = new ContentValues();

values.put("phone", newphone);

int result  =db.update("person", values, "name=?", new String[] { name });

db.close();

return result;

}

// 查询

public boolean find(String name) {

boolean result;

SQLiteDatabase db = helper.getReadableDatabase();

// Cursor cursor = db.rawQuery("select * from person where name=?", new

// String[]{name});

Cursor cursor = db.query("person", null, "name=?",

new String[] { name }, null, null, null);

if (cursor.moveToNext()) {

result = true;

} else {

result = false;

}

cursor.close();

db.close();

return result;

}

/**

 * 查找全部

 * 

 * @return

 */

public List<Person> findAll() {

SQLiteDatabase db = helper.getReadableDatabase();

List<Person> persons = new ArrayList<Person>();

//Cursor cursor = db.rawQuery("select id,name,phone from person ", null);

Cursor cursor = db.query("person",

new String[] { "id", "name", "phone" }, null, null, null, null, null);

while (cursor.moveToNext()) {

int id = cursor.getInt(0);

String name = cursor.getString(1);

String phone = cursor.getString(2);

Person person = new Person();

person.setId(id);

person.setPhone(phone);

person.setName(name);

persons.add(person);

}

cursor.close();

db.close();

return persons;

}

}

4使用SQLiteOpenHelper获取用于操作数据库的SQLiteDatabase实例

         private static final String name = "itcast"; //数据库名称

         private static final int version = 1; //数据库版本

         ......

}

public class HelloActivity extends Activity {

    @Override public void onCreate(Bundle savedInstanceState) {

        ......

        Button button =(Button) this.findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener(){

public void onClick(View v) {

DatabaseHelper databaseHelper = new DatabaseHelper(HelloActivity.this);

SQLiteDatabase db = databaseHelper.getWritableDatabase();

db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"传智播客", 4});

db.close();  

}});        

    }

}

第一次调用getWritableDatabase()getReadableDatabase()方法后,SQLiteOpenHelper会缓存当前的SQLiteDatabase实例,SQLiteDatabase实例正常情况下会维持数据库的打开状态,所以在你不再需要SQLiteDatabase实例时,请及时调用close()方法释放资源。一旦SQLiteDatabase实例被缓存,多次调用getWritableDatabase()getReadableDatabase()方法得到的都是同一实例。

5使用事务操作SQLite数据库

使用SQLiteDatabasebeginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。使用例子如下:
 SQLiteDatabase db = ....;

db.beginTransaction();//开始事务

try {

    db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"传智播客", 4});

    db.execSQL("update person set name=? where personid=?", new Object[]{"传智", 1});

    db.setTransactionSuccessful();//调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务

} finally {

    db.endTransaction();//由事务的标志决定是提交事务,还是回滚事务

db.close(); 

上面两条SQL语句在同一个事务中执行。

package com.itheima.db.test;

import com.itheima.db.PersonDBOpenHelper;

import android.database.sqlite.SQLiteDatabase;

import android.test.AndroidTestCase;

public class TestTransction extends AndroidTestCase {

String s;

public void testSendMoney() throws Exception {

PersonDBOpenHelper helper = new PersonDBOpenHelper(getContext());

SQLiteDatabase db = helper.getWritableDatabase();

db.beginTransaction();

try {

db.execSQL("update person set money = money - 10 where id ='1'");

//s.equals("haha");

db.execSQL("update person set money = money + 10 where id ='2'");

db.setTransactionSuccessful();// 代表是事务处理成功.

} finally {

db.endTransaction();// 检查是否设置了事务成功的标志如果设置了 提交数据,

// 如果没有设置 回滚..

}

}

}

6、Android sqlite3工具的使用

1 cmd à adb shell 首先挂载到linux

2 cd data/data/com.android.contacts.provider

3 cd database

4 sqlite3 contacts 打开数据库 eg: sqlite3 contacts.db

5 .tables 查看所有的表  eg: .table

6 .schema 查看所有的创建表、视图的语句 eg: .schema 

7 .help 查看帮助  eg: .help

8 .header(s) NO |OFF是否显示列头信息 eg: headers ON

9 .mode MODE  ?table? 指定数据显示风格 eg: .mode column

10 .nullValue NULL空值数据显示问题 eg: .nullValue NULL

7、使用ContentProvider(内容提供者)共享数据

ContentProvider android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。关于数据共享,以前我们学习过文件操作模式,知道通过指定文件的操作模式为Context.MODE_WORLD_READABLE Context.MODE_WORLD_WRITEABLE同样也可以对外共享数据。那么,这里为何要使用ContentProvider 对外共享数据呢?是这样的,如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,如:采用xml文件对外共享数据,需要进行xml解析才能读取数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。

使用ContentProvider对外共享数据的好处是统一了数据的访问方式。

当应用需要通过ContentProvider对外共享数据时,第一步需要继承ContentProvider并重写下面方法:

public class PersonContentProvider extends ContentProvider{

   public boolean onCreate()

   public Uri insert(Uri uri, ContentValues values)

   public int delete(Uri uri, String selection, String[] selectionArgs)

   public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

   public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

   public String getType(Uri uri)}

第二步需要在AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:

<manifest .... >

    <application android:icon="@drawable/icon" android:label="@string/app_name">

        <provider android:name=".PersonContentProvider" android:authorities="cn.itcast.providers.personprovider"/>

    </application>

</manifest>

package cn.itcast.db;

import cn.itcast.service.DBOpenHelper;

import android.content.ContentProvider;

import android.content.ContentUris;

import android.content.ContentValues;

import android.content.UriMatcher;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.net.Uri;

public class PersonContentProvider extends ContentProvider {

private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

private static final int PERSONS = 1;

private static final int PERSON = 2;

private DBOpenHelper dbOpenHelper;

static{

matcher.addURI("cn.itcast.providers.personprovider", "person", PERSONS);

matcher.addURI("cn.itcast.providers.personprovider", "person/#", PERSON);

}

@Override

public boolean onCreate() {

dbOpenHelper = new DBOpenHelper(this.getContext());

return true;

}

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

int num = 0 ;//已经删除的记录数量

switch (matcher.match(uri)) {

case PERSONS:

num = db.delete("person", selection, selectionArgs);

break;

case PERSON:

long id = ContentUris.parseId(uri);

String where = "personid="+ id;

if(selection!=null && !"".equals(selection)){ // personid=12 and name=?

where = where + " and "+ selection;

}

num = db.delete("person", where, selectionArgs);

break;

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

getContext().getContentResolver().notifyChange(uri, null);

return num;

}

public String getType(Uri uri) {//返回当前操作的数据类型

switch (matcher.match(uri)) {

case PERSONS://操作的是集合类型数据

return "vnd.android.cursor.dir/person";

case PERSON:

return "vnd.android.cursor.item/person";

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

}

@Override

public Uri insert(Uri uri, ContentValues values) {

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

long id = 0 ;

switch (matcher.match(uri)) {

case PERSONS:

id = db.insert("person", "personid", values);//得到记录的id

getContext().getContentResolver().notifyChange(uri, null);

return ContentUris.withAppendedId(uri, id);//返回代表新增记录的Uri

case PERSON:

id = db.insert("person", "personid", values);//得到记录的id

String strUri = uri.toString();

Uri personUri = Uri.parse(strUri.substring(0, strUri.lastIndexOf("/")));

getContext().getContentResolver().notifyChange(personUri, null);

return ContentUris.withAppendedId(personUri, id);

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

}

@Override

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

SQLiteDatabase db = dbOpenHelper.getReadableDatabase();

switch (matcher.match(uri)) {

case PERSONS:

return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);

case PERSON:

long id = ContentUris.parseId(uri);

String where = "personid="+ id;

if(selection!=null && !"".equals(selection)){ // personid=12 and name=?

where = where + " and "+ selection;

}

return db.query("person", projection, where, selectionArgs, null, null, sortOrder);

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

}

@Override

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

SQLiteDatabase db = dbOpenHelper.getWritableDatabase();

int num = 0 ;//已经修改的记录数量

switch (matcher.match(uri)) {

case PERSONS:

num = db.update("person", values, selection, selectionArgs);

break;

case PERSON:

long id = ContentUris.parseId(uri);

String where = "personid="+ id;

if(selection!=null && !"".equals(selection)){ 

where = where + " and "+ selection;

}

num = db.update("person", values, where, selectionArgs);

break;

default:

throw new IllegalArgumentException("Unkown Uri:"+ uri);

}

getContext().getContentResolver().notifyChange(uri, null);//通知数据发生变化

return num;

}

}

8Uri介绍

Uri代表了要操作的数据,Uri主要包含了两部分信息:1》需要操作的ContentProvider 2》对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:

ContentProvider(内容提供者)的scheme已经由Android所规定, scheme为:content://

主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。

路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:

要操作person表中id10的记录,可以构建这样的路径:/person/10

要操作person表中id10的记录的name字段, person/10/name

要操作person表中的所有记录,可以构建这样的路径:/person

要操作xxx表中的记录,可以构建这样的路径:/xxx

当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:

要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name

如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")

9UriMatcher类使用介绍

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher ContentUris 。掌握它们的使用,会便于我们的开发工作。

UriMatcher类用于匹配Uri,它的用法如下:

首先第一步把你需要匹配Uri路径全部给注册上,如下:

//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码

UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1

sMatcher.addURI(cn.itcast.provider.personproviderperson, 1);//添加需要匹配uri,如果匹配就会返回匹配码

//如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2

sMatcher.addURI(cn.itcast.provider.personproviderperson/#, 2);//#号为通配符

switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) { 

   case 1

    break;

   case 2

    break;

   default://不匹配

    break;

}

注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1

10ContentUris类使用介绍

1ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法:

withAppendedId(uri, id)用于为路径加上ID部分:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person")

Uri resultUri = ContentUris.withAppendedId(uri, 10); 

//生成后的Uri为:content://cn.itcast.provider.personprovider/person/10

2parseId(uri)方法用于从路径中获取ID部分:

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10")

long personid = ContentUris.parseId(uri);//获取的结果为:10

11使用ContentProvider共享数据

ContentProvider类主要方法的作用:

public boolean onCreate()

该方法在ContentProvider创建后就会被调用, Android开机后, ContentProvider在其它应用第一次访问它时才会被创建。

public Uri insert(Uri uri, ContentValues values)

该方法用于供外部应用往ContentProvider添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs)

该方法用于供外部应用从ContentProvider删除数据。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

该方法用于供外部应用更新ContentProvider中的数据。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

该方法用于供外部应用从ContentProvider中获取数据。

public String getType(Uri uri)

该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有person记录的Uricontent://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id10person记录,Uricontent://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。

12使用ContentResolver操作ContentProvider中的数据

当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver 类提供了与ContentProvider类相同签名的四个方法:

public Uri insert(Uri uri, ContentValues values)

该方法用于往ContentProvider添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs)

该方法用于从ContentProvider删除数据。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)

该方法用于更新ContentProvider中的数据。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

该方法用于从ContentProvider中获取数据。

这些方法的第一个参数为Uri,代表要操作的ContentProvider和对其中的什么数据进行操作,假设给定的是: Uri.parse(content://cn.itcast.providers.personprovider/person/10),那么将会对主机名为cn.itcast.providers.personproviderContentProvider进行操作,操作的数据为person表中id10的记录。

使用ContentResolverContentProvider中的数据进行添加、删除、修改和查询操作:

ContentResolver resolver =  getContentResolver();

Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person");

//添加一条记录

ContentValues values = new ContentValues();

values.put("name", "itcast");

values.put("age", 25);

resolver.insert(uri, values);

//获取person表中所有记录

Cursor cursor = resolver.query(uri, null, null, null, "personid desc");

while(cursor.moveToNext()){

Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));

}

//id1的记录的name字段值更改新为liming

ContentValues updateValues = new ContentValues();

updateValues.put("name", "liming");

Uri updateIdUri = ContentUris.withAppendedId(uri, 2);

resolver.update(updateIdUri, updateValues, null, null);

//删除id2的记录

Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);

resolver.delete(deleteIdUri, null, null);

数据库的内容提供者

package com.itheima.db;

import android.content.ContentProvider;

import android.content.ContentValues;

import android.content.UriMatcher;

import android.database.Cursor;

import android.database.sqlite.SQLiteDatabase;

import android.net.Uri;

public class PersonDBProvider extends ContentProvider {

//返回一组的数据

private static final int PERSONS = 1;

private static final int AAABBBCCC = 2;

//返回一个person对象

private static final int PERSON = 3;

private PersonDBOpenHelper helper;

//电话号码的匹配器

private static UriMatcher mUriMatcher= new UriMatcher(UriMatcher.NO_MATCH);

//确定电话匹配的规则

static{

mUriMatcher.addURI("com.itheima.listdb.person", "person", PERSONS);

mUriMatcher.addURI("com.itheima.listdb.person", "person/#", PERSON);

mUriMatcher.addURI("com.itheima.listdb.person", "aaabbbccc", AAABBBCCC);

}

@Override

public boolean onCreate() {

helper = new PersonDBOpenHelper(getContext());

return false;

}

//查询的方法

@Override

public Cursor query(Uri uri, String[] projection, String selection,

String[] selectionArgs, String sortOrder) {

if(mUriMatcher.match(uri)==PERSONS){

System.out.println("满足 person的匹配规则");

SQLiteDatabase db = helper.getReadableDatabase();

return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);

}else if(mUriMatcher.match(uri)==PERSON){

System.out.println("满足一个person的匹配规则");

String path = uri.toString();

System.out.println("path:"+path);

String id = path.substring(path.lastIndexOf("/")+1);

SQLiteDatabase db = helper.getReadableDatabase();

return db.query("person", projection, "id=?", new String[]{id},  null, null, null);

}else{

throw new IllegalArgumentException("路径不匹配,口令错误...滚一边去");

}

}

//返回当前数据的mime的类型 主要作用 区分数据是一个集合 还是单独一个条目

@Override

public String getType(Uri uri) {

if(mUriMatcher.match(uri)==PERSONS){

return "vnd.android.cursor.dir/persons";

}else if(mUriMatcher.match(uri)==PERSON){

return "vnd.android.cursor.item/person";

}

return null;

}

@Override

public Uri insert(Uri uri, ContentValues values) {

if(mUriMatcher.match(uri)==PERSON){

System.out.println("满足 person的匹配规则");

SQLiteDatabase db = helper.getWritableDatabase();

db.insert("person", null, values);

}else{

throw new IllegalArgumentException("路径不匹配,口令错误...滚一边去");

}

return null;

}

@Override

public int delete(Uri uri, String selection, String[] selectionArgs) {

if(mUriMatcher.match(uri)==PERSON){

System.out.println("满足 person的匹配规则");

SQLiteDatabase db = helper.getWritableDatabase();

db.delete("person", selection, selectionArgs);

}else{

throw new IllegalArgumentException("路径不匹配,口令错误...滚一边去");

}

return 0;

}

@Override

public int update(Uri uri, ContentValues values, String selection,

String[] selectionArgs) {

if(mUriMatcher.match(uri)==PERSON){

System.out.println("满足 person的匹配规则");

SQLiteDatabase db = helper.getWritableDatabase();

db.update("person", values, selection, selectionArgs);

}else{

throw new IllegalArgumentException("路径不匹配,口令错误...滚一边去");

}

return 0;

}

}

    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <!-- 测试的指令集需要的函数库 -->

        <uses-library android:name="android.test.runner" />

        <activity

            android:name="com.itheima.db.MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

        <provider

            android:name="com.itheima.db.PersonDBProvider"

            android:authorities="com.itheima.listdb.person" >

        </provider>

</application>

读取用户的短信

package com.itheima.readsms;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import org.xmlpull.v1.XmlSerializer;

import android.app.Activity;

import android.content.ContentResolver;

import android.database.Cursor;

import android.net.Uri;

import android.os.Bundle;

import android.util.Xml;

import android.view.View;

import android.widget.Toast;

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

public void readSms(View view){

//得到一个内容解析者

ContentResolver resolver = getContentResolver();

Uri uri = Uri.parse("content://sms/");

Cursor cursor = resolver.query(uri, new String[]{"address","type","date","body"}, null, null, null);

 XmlSerializer  serializer = Xml.newSerializer();

 try {

File file = new File(getFilesDir(),"sms.xml");

 FileOutputStream os = new FileOutputStream(file);

serializer.setOutput(os, "utf-8");

serializer.startDocument("utf-8", true);

serializer.startTag(null, "smss");

while(cursor.moveToNext()){

serializer.startTag(null, "sms");

String address = cursor.getString(0);

serializer.startTag(null, "address");

serializer.text(address);

serializer.endTag(null, "address");

String type = cursor.getString(1);

serializer.startTag(null, "type");

serializer.text(type);

serializer.endTag(null, "type");

String date = cursor.getString(2);

serializer.startTag(null, "date");

serializer.text(date);

serializer.endTag(null, "date");

String body = cursor.getString(3);

serializer.startTag(null, "body");

serializer.text(body);

serializer.endTag(null, "body");

System.out.println("address="+address);

System.out.println("type="+type);

System.out.println("date="+date);

System.out.println("body="+body);

System.out.println("----------------");

serializer.endTag(null, "sms");

}

serializer.endTag(null, "smss");

serializer.endDocument();

os.close();

Toast.makeText(this, "备份短信完成", 0).show();

} catch (Exception e) {

e.printStackTrace();

Toast.makeText(this, "备份短信失败", 0).show();

}

}

}

还原一条短信

package com.itheima.restoresms;

import android.app.Activity;

import android.content.ContentResolver;

import android.content.ContentValues;

import android.net.Uri;

import android.os.Bundle;

import android.view.View;

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

public void click(View view) {

new Thread(){

public void run() {

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

e.printStackTrace();

}

ContentResolver resolver = getContentResolver();

Uri uri = Uri.parse("content://sms/");

ContentValues values = new ContentValues();

values.put("address", "95533");

values.put("date", System.currentTimeMillis());

values.put("type", "1");

values.put("body", "尊敬的建行客户张先生您的尾号为201的建行卡 在1647分收入,银行转账 1,000,000,000,000.22 人民币,活期余额 2,080,000,000,020.22 ");

resolver.insert(uri, values);

};

}.start();

}

}

13监听ContentProvider中数据的变化

如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下:

public class PersonContentProvider extends ContentProvider {

public Uri insert(Uri uri, ContentValues values) {

db.insert("person", "personid", values);

getContext().getContentResolver().notifyChange(uri, null);

}

}

如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserveronChange()方法:

getContentResolver().registerContentObserver(Uri.parse("content://cn.itcast.providers.personprovider/person"),

        true, new PersonObserver(new Handler()));

public class PersonObserver extends ContentObserver{

public PersonObserver(Handler handler) {

super(handler);

 }

public void onChange(boolean selfChange) {

    //此处可以进行相应的业务处理

}

}

14、窃听用户发出的短信

用户使用系统自带的短信程序发送短信,程序会通过ContentProvider把短信保存进数据库,并且发出一个数据变化通知,使用ContentObserver对数据变化进行监听,在用户发送短信时,就会被ContentObserver窃听到短信:

注册监听:

getContentResolver().registerContentObserver(Uri.parse("content://sms"),  true, new SmsObserver(new Handler()));

监听类:

private final class SmsObserver extends ContentObserver{

public SmsObserver(Handler handler) {

super(handler);

}

public void onChange(boolean selfChange) {//查询发送箱中的短信(处于正在发送状态的短信放在发送箱)

Cursor cursor = getContentResolver().query(Uri.parse("content://sms/outbox"),null, null, null, null); 

while(cursor.moveToNext()){

StringBuilder sb = new StringBuilder();

sb.append("_id=").append(cursor.getInt(cursor.getColumnIndex("_id")));

sb.append(",address=").append(cursor.getString(cursor.getColumnIndex("address")));

sb.append(";body=").append(cursor.getString(cursor.getColumnIndex("body")));

sb.append(";time=").append(cursor.getLong(cursor.getColumnIndex("date")));

Log.i("ReceiveSendSMS", sb.toString());

             } }    

}

15、通信录操作

使用ContentResolver对通信录中的数据进行添加、删除、修改和查询操作:

加入读写联系人信息的权限

<uses-permission android:name="android.permission.READ_CONTACTS" />

<uses-permission android:name="android.permission.WRITE_CONTACTS" />

加入读取联系人信息的权限

<uses-permission android:name="android.permission.READ_CONTACTS"/>

content://com.android.contacts/contacts 操作的数据是联系人信息Uri

content://com.android.contacts/data/phones 联系人电话Uri

content://com.android.contacts/data/emails 联系人Email Uri

读取联系人信息

Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,  

    null, null, null, null);  

  while (cursor.moveToNext()) {  

   String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));  

   String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));  

   

   Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,  

        null,  

        ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,  

        null, null);  

    while (phones.moveToNext()) {  

     String phoneNumber = phones.getString(phones.getColumnIndex(  

         ContactsContract.CommonDataKinds.Phone.NUMBER));  

     Log.i("RongActivity", "phoneNumber="+phoneNumber);

    }  

phones.close();  

      Cursor emails = getContentResolver().query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,  

       null,  

       ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId,  

       null, null);  

       while (emails.moveToNext()) {  

        // This would allow you get several email addresses  

        String emailAddress = emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));

        Log.i("RongActivity", "emailAddress="+ emailAddress);

       }  

       emails.close();  

  }  

  cursor.close(); 

==================== 添加联系人 ===========================

方法一:

/**

 * 首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId 

 * 这时后面插入data表的依据,只有执行空值插入,才能使插入的联系人在通讯录里面可见

 */

public void testInsert() {

ContentValues values = new ContentValues();

//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId 

Uri rawContactUri = this.getContext().getContentResolver().insert(RawContacts.CONTENT_URI, values);

long rawContactId = ContentUris.parseId(rawContactUri);

//data表入姓名数据

values.clear();

values.put(Data.RAW_CONTACT_ID, rawContactId); 

values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);//内容类型

values.put(StructuredName.GIVEN_NAME, "李天山");

this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);

//data表入电话数据

values.clear();

values.put(Data.RAW_CONTACT_ID, rawContactId);

values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);

values.put(Phone.NUMBER, "13921009789");

values.put(Phone.TYPE, Phone.TYPE_MOBILE);

this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);

//data表入Email数据

values.clear();

values.put(Data.RAW_CONTACT_ID, rawContactId);

values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);

values.put(Email.DATA, "liming@itcast.cn");

values.put(Email.TYPE, Email.TYPE_WORK);

this.getContext().getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);

}

方法二:批量添加,处于同一个事务中

public void testSave() throws Throwable{

//文档位置:reference\android\provider\ContactsContract.RawContacts.html

ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();

int rawContactInsertIndex = 0;

ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)

.withValue(RawContacts.ACCOUNT_TYPE, null)

.withValue(RawContacts.ACCOUNT_NAME, null)

.build());

//文档位置:reference\android\provider\ContactsContract.Data.html

ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)

.withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)

.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)

.withValue(StructuredName.GIVEN_NAME, "赵薇")

.build());

ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)

 .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)

         .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)

    .withValue(Phone.NUMBER, "13671323809")

         .withValue(Phone.TYPE, Phone.TYPE_MOBILE)

         .withValue(Phone.LABEL, "手机号")

         .build());

ops.add(ContentProviderOperation.newInsert(android.provider.ContactsContract.Data.CONTENT_URI)

 .withValueBackReference(Data.RAW_CONTACT_ID, rawContactInsertIndex)

         .withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)

         .withValue(Email.DATA, "liming@itcast.cn")

         .withValue(Email.TYPE, Email.TYPE_WORK)

         .build());

ContentProviderResult[] results = this.getContext().getContentResolver()

.applyBatch(ContactsContract.AUTHORITY, ops);

for(ContentProviderResult result : results){

Log.i(TAG, result.uri.toString());

}

}

16、Listview数据适配器

listview   列表view

1.知道一共要显示有多少个条目

2.知道一个屏幕上最多能显示多个条目

3.创建一个集合 双向链表 

4.滑动界面的时候 最上面条目移除屏幕 双向链表移除对象双向链表添加对象.

m : model 数据模型   Person

v : view  展示视图   ListView

c : controller 控制器 adapter

ListView数据库界面展现

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content" >

    <TextView

        android:id="@+id/tv_name"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignParentLeft="true"

        android:layout_alignParentTop="true"

        android:text="姓名"

        android:textColor="#000000"

        android:textSize="20sp" />

    <TextView

        android:id="@+id/tv_id"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_below="@id/tv_name"

        android:text="联系人id"

        android:textColor="#66000000"

        android:textSize="14sp" />

    <TextView

        android:id="@+id/tv_phone"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_alignParentRight="true"

        android:layout_centerVertical="true"

        android:text="电话号码"

        android:textColor="#000000"

        android:textSize="16sp" />

    <ImageView

        android:layout_toLeftOf="@id/tv_phone"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerVertical="true"

        android:src="@drawable/phone" />

</RelativeLayout>

package com.itheima.db;

import java.util.List;

import android.app.Activity;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.LinearLayout;

import android.widget.ListAdapter;

import android.widget.ListView;

import android.widget.RelativeLayout;

import android.widget.TextView;

import com.itheima.db.dao.PersonDao;

import com.itheima.db.domain.Person;

import com.itheima.listdb.R;

public class MainActivity extends Activity {

public static final String TAG = "MainActivity";

private ListView lv;

private List<Person> persons;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

lv = (ListView) findViewById(R.id.lv);

PersonDao dao = new PersonDao(this);

persons = dao.findAll();

lv.setAdapter(new MyAdapter());

}

private class MyAdapter extends BaseAdapter {

/**

 * 返回整个数据适配器列表里面有多少个条目

 */

@Override

public int getCount() {

return persons.size();

}

/**

 * 返回某个位置要显示的view对象

 */

@Override

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

//根据layout目录下  list_item的布局为模板  创建一个view对象.

// 一般情况下 创建的view对象都是独立的view对象 第三个参数 null

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

TextView tv_name = (TextView) view.findViewById(R.id.tv_name);

TextView tv_id = (TextView) view.findViewById(R.id.tv_id);

TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);

Person person = persons.get(position);

tv_id.setText("联系人id:"+person.getId());

tv_name.setText(person.getName());

tv_phone.setText(person.getPhone());

return view;

}

@Override

public Object getItem(int position) {

return null;

}

@Override

public long getItemId(int position) {

return 0;

}

}

}

ArrayAdapter实例:

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

    android:gravity="center_vertical"

    android:orientation="horizontal" >

    <ImageView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@drawable/ic_launcher" />

    <TextView

        android:id="@+id/tv_info"

        android:text="哈哈"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:textColor="#000000"

        android:textSize="20sp" />

</LinearLayout>

package com.itheima.arrayadapter;

import android.os.Bundle;

import android.app.Activity;

import android.view.Menu;

import android.widget.ArrayAdapter;

import android.widget.ListView;

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ListView lv = (ListView) findViewById(R.id.lv);

lv.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item,

R.id.tv_info, new String[] { "功能1", "功能2", "功能3", "功能4", "功能5",

"功能1", "功能2", "功能3", "功能4", "功能5" }));

}

}

SimpleAdapter数据库界面展现

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

    android:gravity="center_vertical"

    android:orientation="horizontal" >

    <ImageView

        android:id="@+id/iv_icon"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:src="@drawable/ic_launcher" />

    <TextView

        android:id="@+id/tv_info"

        android:text="哈哈"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:textColor="#000000"

        android:textSize="20sp" />

</LinearLayout>

package com.itheima.simpleadapter;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import android.app.Activity;

import android.os.Bundle;

import android.widget.ListView;

import android.widget.SimpleAdapter;

public class MainActivity extends Activity {

private ListView lv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

lv = (ListView) findViewById(R.id.lv);

List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();

// 准备第一个map对象 第一个要显示条目对应的数据

Map<String, Object> map1 = new HashMap<String, Object>();

map1.put("image", R.drawable.app);

map1.put("text", "功能1");

Map<String, Object> map2 = new HashMap<String, Object>();

map2.put("image", R.drawable.atools);

map2.put("text", "功能2");

Map<String, Object> map3 = new HashMap<String, Object>();

map3.put("image", R.drawable.safe);

map3.put("text", "功能3");

Map<String, Object> map4 = new HashMap<String, Object>();

map4.put("image", R.drawable.settings);

map4.put("text", "功能4");

data.add(map1);

data.add(map2);

data.add(map3);

data.add(map4);

lv.setAdapter(new SimpleAdapter(this, data, R.layout.list_item,

new String[] { "image", "text" }, new int[]{R.id.iv_icon,R.id.tv_info}));

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值