列表视图ListView
ListView是应用最广泛的数据显示视图,每个列表项都对应于一个布局文件。
ListView采用的是MVC设计模式。
M-Model 模特,演员,即即将演示的数据。
V-View 舞台,表演用的。即显示数据用的控件。
C-Controlor 控制者,业务逻辑实现层。
数组资源,字符串数组资源,写到strings.xml也可以写到一个单独的文件arrays.xml中都可以。
<string-array name="hobbies">
<item>唱歌</item>
<item>跳舞</item>
<item>旅游</item>
<item>打游戏</item>
<item>音乐</item>
<item>运动</item>
</string-array>
在Java中引用 R.array.hobbies
xml文件中引用 @array/hobbies
实现ListView的一般步骤
在布局文件中编写代码(xml)
添加ListView标签
在Activity中编写Java代码
获取ListView对象
准备数据源
配置适配器
将适配器关联到ListView
添加事件监听器
注:如果直接继承ListActivity,里面内置了一个ListView对象,可以直接使用显示界面,就可以省略加布局这一步骤。
什么是适配器?
适配器是数据和视图间的桥梁。
它负责把数据所提供的内容显示到视图所定义的外观中。
android.R.xxx 是引用Android系统提供的资源。
R.xxx是指引用自定义的资源。
适配器的种类
ArrayAdapter、SimpleAdapter、自定义的Adapter
SimpleAdapter,关键是构造此适配器对象。查看源代码:
/**
* Constructor
*
* @param context The context where the View associated with this SimpleAdapter is running
* @param data A List of Maps. Each entry in the List corresponds to one row in the list. The
* Maps contain the data for each row, and should include all the entries specified in
* "from"
* @param resource Resource identifier of a view layout that defines the views for this list
* item. The layout file should include at least those named views defined in "to"
* @param from A list of column names that will be added to the Map associated with each
* item.
* @param to The views that should display column in the "from" parameter. These should all be
* TextViews. The first N views in this list are given the values of the first N columns
* in the from parameter.
*/
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
@LayoutRes int resource, String[] from, @IdRes int[] to) {......}
ListActivity中内置了一个ListView,在界面只包含一个ListView时,可以不用单独写布局文件。
List<? extends Map<String, ?>> data
? extends Map ==>就是指Map及Map的子类
自定义Adapter
实现自定义Adapter的步骤
1.继承BaseAdapter
2.实现getView方法
3.关联ListView
需要实现4个抽象方法:
getCount()
getItem()
getItemId()
getView()
关键是写出getView方法的实现,它返回一个View对象,就是用来显示每个数据项所需要的View对象。每显示一个数据项都要执行一次getView方法。
ListView的长按事件setOnItemLongClickListener包含着短按事件setOnItemClickListener,如果长按事件中的方法让其返回true意味着长按完全消化了该事件,不再往传递这个事件了。短事件就不会被执行。
ListView优化
我的例子中一个屏幕可以显示10个Item,当第1个Item只显示一半时,第11个Item也只显示一半,这两个View必需要被完整创建,因此,需要创建11个View对象。当第1个Item完全消失时,这个对象又以convertView对象作为参数传递给getView()方法,可以对这个对象进行重用,换掉上面的数据,让其显示第12个Item对象。
在getView方法中使用convertView和ViewHolder
inflate方法的耗时:convertView解决
findViewById方法的耗时:ViewHolder解决
View对象是重用的,数据是更新的。
Android的存储
掌握Android存储的种类
掌握SharedPreferences存储
掌握File内部存储、外部存储
掌握SQLite数据库存储技术
SD卡,包括内置的SD卡都是外部存储设备
案例:如何让程序自我判断是否是第一次运行?
Android存储包括:
SharedPreferences共享存储,主要保存一些简单的数据类型的键值对数据,这是Android特有的轻量级的存储对象。
File内部存储,使用IO流存储
SQLite数据库
Content Provider
Net网络服务器
SharedPreferences对象存储,是内部存储,数据作为文件保存在/data/data/包名/shared_prefs
保存数据的一般步骤
取得数据
获得SharedPreferences对象
获得SharedPreferences.Editor对象
使用putXXX方法写入数据
提交数据
Mode权限模式
Context.MODE_PRIVATE 当前进程(APP)才有读写权限 -rw-rw---- d l p s c b
Context.MODE_APPEND 当前进程有读写权限,模式内容追加
Context.MODE_WORLD_READABLE 全局可读 rw-rw-r--
Context.MODE_WORLD_WRITEABLE全局可写 rw-rw--w-
File内部存储
是保存的项目的数据目录下,使用IO流存储的,使用openFileOutput方法,采用是覆盖数据打开方式。
准备好数据
获取输出流对象
写入数据
关闭流对象
读取File
获取输入流对象,使用openFileInput方法
读取数据到byte[]数组容器
获取数据
关闭流对象
读写SD外部存储
使用IO流读写。
Android6.0开始要求危险权限必须要用户明确授权才可以使用。
需要用户开通权限。
SQLite数据库
在Android系统内置了SQLite数据库,可以直接使用。
只支持五种数据类型,Null,integer,text,real,blob
经常使用到的类
SQLiteDatabase数据库的主要操作的方法都在这个类中
SQLiteOpenHelper创建数据库、表、表结构、更新升级
SQLite数据库操作的基本方法
SQLiteDatabase db = this.openOrCreateDatabase("mydb",Context.MODE_PRIVATE,null);//打开或创建数据库
db.exeSQL(....); //增、删、改操作
Cursor cursor = db.rawQuery(....); //查询
db.close(); //关闭数据库
Android系统还为我们提供了一个辅助类SQLiteOpenHelper用来创建数据库、表,操作数据库。
此类有两个抽象方法
onCreate()
创建数据库、创建表及表结构、以及生成表的原始数据
这个方法只在创建数据库时被调用
public void onCreate(SQLiteDatabase db)
{
String sql = "create table users " +
"(_id integer primary key autoincrement," +
"username text unique," +
"password text);";
db.execSQL(sql);
}
onUpgrade()
在版本号发现变化时才会被调用。
做升级操作
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
String sql = "drop table users;";
db.execSQL(sql);
onCreate(db);
}
获得SQLiteDatabase对象
getReadableDatabase()
getWritableDatabase()
以上两个方法都能获得一个SQLiteDatabase对象
以上两个方法都是采用读写方式打开数据库,唯一的区别在于当存储满了的时候,getWritableDatabase打开方式会报错,并断开数据库连接;而getReadableDatabase打开方式同样也会报错,但紧接着会尝试以只读方式连接数据库,并可以读取数据。
写一数据库访问操作的工具类DaoUtils
public class DaoUtils
{
private SQLiteDatabase db;
public DaoUtils(Context context)
{
MyDBHelper dbHelper = new MyDBHelper(context);
db = dbHelper.getWritableDatabase();
}
public void insert(User user)
{
String sql = "insert into users(username,password) values('"+user.getUsername()+"','"+user.getPassword()+"');";
db.execSQL(sql);
}
public void insert(String name, String pwd)
{
String sql = "insert into users(username,password) values(?,?);";
db.execSQL(sql,new Object[]{name,pwd});
}
public void update(String name,String pwd)
{
String sql = "update users set password = ? where username = ?;";
db.execSQL(sql,new Object[]{pwd,name});
}
public void delete(String name)
{
String sql = "delete from users where username = '"+name+"';";
db.execSQL(sql);
}
public User getByName(String name)
{
String sql = "select * from users where username = '"+name+"';";
Cursor cursor = db.rawQuery(sql,null);
User user = null;
if (cursor.moveToNext())
{
user = new User();
user.set_id(cursor.getInt(cursor.getColumnIndex("_id")));
user.setUsername(cursor.getString(cursor.getColumnIndex("username")));
user.setPassword(cursor.getString(cursor.getColumnIndex("password")));
}
return user;
}
public List<User> getAll()
{
List<User> data = new ArrayList<>();
String sql = "select * from users;";
Cursor cursor = db.rawQuery(sql,null);
while (cursor.moveToNext())
{
User user = new User();
user.set_id(cursor.getInt(cursor.getColumnIndex("_id")));
user.setUsername(cursor.getString(cursor.getColumnIndex("username")));
user.setPassword(cursor.getString(cursor.getColumnIndex("password")));
data.add(user);
}
return data;
}
public boolean exists(String name)
{
String sql = "select * from users where username = '"+name+"';";
Cursor cursor = db.rawQuery(sql,null);
return cursor.moveToNext();
}
public void close()
{
db.close();
}
}
使用工具类:
case R.id.saveDBBtn:
String str7 = txt1.getText().toString();
String str8 = txt2.getText().toString();
DaoUtils dao = new DaoUtils(this);
if (dao.exists(str7))
{
Toast.makeText(this,str7+" 此用户已存在!",Toast.LENGTH_LONG).show();
dao.close();
return;
}
dao.insert(str7,str8);
Toast.makeText(this,"写入数据库成功!",Toast.LENGTH_LONG).show();
dao.close();
break;
case R.id.readDBBtn:
DaoUtils dao1 = new DaoUtils(this);
StringBuilder buf = new StringBuilder();
List<User> data = dao1.getAll();
for (int i=0;i<data.size();i++)
{
User user = data.get(i);
buf.append(user.toString()+"\n");
}
displayView.setText(buf);
break;
ListView是应用最广泛的数据显示视图,每个列表项都对应于一个布局文件。
ListView采用的是MVC设计模式。
M-Model 模特,演员,即即将演示的数据。
V-View 舞台,表演用的。即显示数据用的控件。
C-Controlor 控制者,业务逻辑实现层。
数组资源,字符串数组资源,写到strings.xml也可以写到一个单独的文件arrays.xml中都可以。
<string-array name="hobbies">
<item>唱歌</item>
<item>跳舞</item>
<item>旅游</item>
<item>打游戏</item>
<item>音乐</item>
<item>运动</item>
</string-array>
在Java中引用 R.array.hobbies
xml文件中引用 @array/hobbies
实现ListView的一般步骤
在布局文件中编写代码(xml)
添加ListView标签
在Activity中编写Java代码
获取ListView对象
准备数据源
配置适配器
将适配器关联到ListView
添加事件监听器
注:如果直接继承ListActivity,里面内置了一个ListView对象,可以直接使用显示界面,就可以省略加布局这一步骤。
什么是适配器?
适配器是数据和视图间的桥梁。
它负责把数据所提供的内容显示到视图所定义的外观中。
android.R.xxx 是引用Android系统提供的资源。
R.xxx是指引用自定义的资源。
适配器的种类
ArrayAdapter、SimpleAdapter、自定义的Adapter
SimpleAdapter,关键是构造此适配器对象。查看源代码:
/**
* Constructor
*
* @param context The context where the View associated with this SimpleAdapter is running
* @param data A List of Maps. Each entry in the List corresponds to one row in the list. The
* Maps contain the data for each row, and should include all the entries specified in
* "from"
* @param resource Resource identifier of a view layout that defines the views for this list
* item. The layout file should include at least those named views defined in "to"
* @param from A list of column names that will be added to the Map associated with each
* item.
* @param to The views that should display column in the "from" parameter. These should all be
* TextViews. The first N views in this list are given the values of the first N columns
* in the from parameter.
*/
public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
@LayoutRes int resource, String[] from, @IdRes int[] to) {......}
ListActivity中内置了一个ListView,在界面只包含一个ListView时,可以不用单独写布局文件。
List<? extends Map<String, ?>> data
? extends Map ==>就是指Map及Map的子类
自定义Adapter
实现自定义Adapter的步骤
1.继承BaseAdapter
2.实现getView方法
3.关联ListView
需要实现4个抽象方法:
getCount()
getItem()
getItemId()
getView()
关键是写出getView方法的实现,它返回一个View对象,就是用来显示每个数据项所需要的View对象。每显示一个数据项都要执行一次getView方法。
ListView的长按事件setOnItemLongClickListener包含着短按事件setOnItemClickListener,如果长按事件中的方法让其返回true意味着长按完全消化了该事件,不再往传递这个事件了。短事件就不会被执行。
ListView优化
我的例子中一个屏幕可以显示10个Item,当第1个Item只显示一半时,第11个Item也只显示一半,这两个View必需要被完整创建,因此,需要创建11个View对象。当第1个Item完全消失时,这个对象又以convertView对象作为参数传递给getView()方法,可以对这个对象进行重用,换掉上面的数据,让其显示第12个Item对象。
在getView方法中使用convertView和ViewHolder
inflate方法的耗时:convertView解决
findViewById方法的耗时:ViewHolder解决
View对象是重用的,数据是更新的。
Android的存储
掌握Android存储的种类
掌握SharedPreferences存储
掌握File内部存储、外部存储
掌握SQLite数据库存储技术
SD卡,包括内置的SD卡都是外部存储设备
案例:如何让程序自我判断是否是第一次运行?
Android存储包括:
SharedPreferences共享存储,主要保存一些简单的数据类型的键值对数据,这是Android特有的轻量级的存储对象。
File内部存储,使用IO流存储
SQLite数据库
Content Provider
Net网络服务器
SharedPreferences对象存储,是内部存储,数据作为文件保存在/data/data/包名/shared_prefs
保存数据的一般步骤
取得数据
获得SharedPreferences对象
获得SharedPreferences.Editor对象
使用putXXX方法写入数据
提交数据
Mode权限模式
Context.MODE_PRIVATE 当前进程(APP)才有读写权限 -rw-rw---- d l p s c b
Context.MODE_APPEND 当前进程有读写权限,模式内容追加
Context.MODE_WORLD_READABLE 全局可读 rw-rw-r--
Context.MODE_WORLD_WRITEABLE全局可写 rw-rw--w-
File内部存储
是保存的项目的数据目录下,使用IO流存储的,使用openFileOutput方法,采用是覆盖数据打开方式。
准备好数据
获取输出流对象
写入数据
关闭流对象
读取File
获取输入流对象,使用openFileInput方法
读取数据到byte[]数组容器
获取数据
关闭流对象
读写SD外部存储
使用IO流读写。
Android6.0开始要求危险权限必须要用户明确授权才可以使用。
需要用户开通权限。
SQLite数据库
在Android系统内置了SQLite数据库,可以直接使用。
只支持五种数据类型,Null,integer,text,real,blob
经常使用到的类
SQLiteDatabase数据库的主要操作的方法都在这个类中
SQLiteOpenHelper创建数据库、表、表结构、更新升级
SQLite数据库操作的基本方法
SQLiteDatabase db = this.openOrCreateDatabase("mydb",Context.MODE_PRIVATE,null);//打开或创建数据库
db.exeSQL(....); //增、删、改操作
Cursor cursor = db.rawQuery(....); //查询
db.close(); //关闭数据库
Android系统还为我们提供了一个辅助类SQLiteOpenHelper用来创建数据库、表,操作数据库。
此类有两个抽象方法
onCreate()
创建数据库、创建表及表结构、以及生成表的原始数据
这个方法只在创建数据库时被调用
public void onCreate(SQLiteDatabase db)
{
String sql = "create table users " +
"(_id integer primary key autoincrement," +
"username text unique," +
"password text);";
db.execSQL(sql);
}
onUpgrade()
在版本号发现变化时才会被调用。
做升级操作
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
String sql = "drop table users;";
db.execSQL(sql);
onCreate(db);
}
获得SQLiteDatabase对象
getReadableDatabase()
getWritableDatabase()
以上两个方法都能获得一个SQLiteDatabase对象
以上两个方法都是采用读写方式打开数据库,唯一的区别在于当存储满了的时候,getWritableDatabase打开方式会报错,并断开数据库连接;而getReadableDatabase打开方式同样也会报错,但紧接着会尝试以只读方式连接数据库,并可以读取数据。
写一数据库访问操作的工具类DaoUtils
public class DaoUtils
{
private SQLiteDatabase db;
public DaoUtils(Context context)
{
MyDBHelper dbHelper = new MyDBHelper(context);
db = dbHelper.getWritableDatabase();
}
public void insert(User user)
{
String sql = "insert into users(username,password) values('"+user.getUsername()+"','"+user.getPassword()+"');";
db.execSQL(sql);
}
public void insert(String name, String pwd)
{
String sql = "insert into users(username,password) values(?,?);";
db.execSQL(sql,new Object[]{name,pwd});
}
public void update(String name,String pwd)
{
String sql = "update users set password = ? where username = ?;";
db.execSQL(sql,new Object[]{pwd,name});
}
public void delete(String name)
{
String sql = "delete from users where username = '"+name+"';";
db.execSQL(sql);
}
public User getByName(String name)
{
String sql = "select * from users where username = '"+name+"';";
Cursor cursor = db.rawQuery(sql,null);
User user = null;
if (cursor.moveToNext())
{
user = new User();
user.set_id(cursor.getInt(cursor.getColumnIndex("_id")));
user.setUsername(cursor.getString(cursor.getColumnIndex("username")));
user.setPassword(cursor.getString(cursor.getColumnIndex("password")));
}
return user;
}
public List<User> getAll()
{
List<User> data = new ArrayList<>();
String sql = "select * from users;";
Cursor cursor = db.rawQuery(sql,null);
while (cursor.moveToNext())
{
User user = new User();
user.set_id(cursor.getInt(cursor.getColumnIndex("_id")));
user.setUsername(cursor.getString(cursor.getColumnIndex("username")));
user.setPassword(cursor.getString(cursor.getColumnIndex("password")));
data.add(user);
}
return data;
}
public boolean exists(String name)
{
String sql = "select * from users where username = '"+name+"';";
Cursor cursor = db.rawQuery(sql,null);
return cursor.moveToNext();
}
public void close()
{
db.close();
}
}
使用工具类:
case R.id.saveDBBtn:
String str7 = txt1.getText().toString();
String str8 = txt2.getText().toString();
DaoUtils dao = new DaoUtils(this);
if (dao.exists(str7))
{
Toast.makeText(this,str7+" 此用户已存在!",Toast.LENGTH_LONG).show();
dao.close();
return;
}
dao.insert(str7,str8);
Toast.makeText(this,"写入数据库成功!",Toast.LENGTH_LONG).show();
dao.close();
break;
case R.id.readDBBtn:
DaoUtils dao1 = new DaoUtils(this);
StringBuilder buf = new StringBuilder();
List<User> data = dao1.getAll();
for (int i=0;i<data.size();i++)
{
User user = data.get(i);
buf.append(user.toString()+"\n");
}
displayView.setText(buf);
break;