安卓day27数据存储和界面展现 测试 sqlite 事务 listview 适配器 对话框
一、排坑
单元测试类
SQLiteDatabase
重复定义
android Studio里查看db文件
循环体内输出log只显示一条
DB Browse表内只显示类型char[10]
字符串用varchar即可正常显示
更改后需刷新
二、单元测试
- 黑盒测试
- 测试逻辑业务
-
白盒测试
- 测试逻辑方法
-
根据测试粒度
- 方法测试:function test
- 单元测试:unit test
- 集成测试:integration test
- 系统测试:system test
-
根据测试暴力程度
- 冒烟测试:smoke test
- 压力测试:pressure test
public class TestCase extends AndroidTestCase { public void test(){ int result = Utils.add(3, 5); //断言:用来检测实际值与期望值是否一致 assertEquals(8, result); } public void test2(){ Utils.chuyi(2, 1); } }
public class Utils { public static int add(int i, int j){ return i + j; } public static void chuyi(int i, int j){ int result = i / j; } }
androidTest是整合测试。可以运行在设备或虚拟设备上.需要编译打包为APK在设备上运行,可以实时查看细节.
test是单元测试类.运行在本地开发机上,可以脱离Android运行时环境,速度快.
三、SQLite数据库
public class MyOpenHelper extends SQLiteOpenHelper { private static final int VERSION=1; private static final String DBNAME="people.db"; private static final String TAG = "MyOpenHelper"; public MyOpenHelper(Context context) { super(context, DBNAME, null, VERSION); // TODO Auto-generated constructor stub } //数据库创建时,此方法会调用 @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table person(_id integer primary key autoincrement, name varchar(10), salary varchar(20), phone integer(20))"); Log.e(TAG, "onCreate: " ); } //数据库升级时,此方法会调用 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.e(TAG, "onUpgrade: " ); } }
public class MainActivity extends Activity { private SQLiteDatabase db; private MyOpenHelper oh; private static final String TAG = "MainActivity"; private TextView tv1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv1=(TextView)findViewById(R.id.tv1); oh = new MyOpenHelper(MainActivity.this); //如果数据库不存在,先创建数据库,再获取可读可写的数据库对象,如果数据库存在,就直接打开 insert(); delete(); update(); select(); } public void insert(){ db = oh.getWritableDatabase(); db.execSQL("insert into person (name, salary, phone)values(?, ?, ?)", new Object[]{"小志2", "14000", 13888}); db.execSQL("insert into person (name, salary, phone)values(?, ?, ?)", new Object[]{"小志", "14000", 13888}); db.execSQL("insert into person (name, salary, phone)values(?, ?, ?)", new Object[]{"小志3", "14000", 13888}); } public void delete(){ db = oh.getWritableDatabase(); db.execSQL("delete from person where name = ?", new Object[]{"小志"}); } public void update(){ db = oh.getWritableDatabase(); db.execSQL("update person set phone = ? where name = ?", new Object[]{186666, "小志3"}); } public void select(){ db = oh.getWritableDatabase(); Cursor cursor = db.rawQuery("select name, salary from person", null); StringBuilder sb=new StringBuilder(); while(cursor.moveToNext()){ //通过列索引获取列的值 String name = cursor.getString(cursor.getColumnIndex("name")); String salary = cursor.getString(1); sb.append(name + ": " + salary + ";\n"); //Log.e(TAG, name + ": " + salary + ";\n"); //System.out.println(name + ";" + salary); } Log.e(TAG, sb.toString()); }
}
使用api实现增删改查
public void insertApi(){ db = oh.getWritableDatabase(); //把要插入的数据全部封装至ContentValues对象 ContentValues values = new ContentValues(); values.put("name", "游天龙"); values.put("phone", "15999"); values.put("salary", 16000); db.insert("person", null, values); } public void deleteApi(){ db = oh.getWritableDatabase(); int i = db.delete("person", "name = ? and _id = ?", new String[]{"小志3", "4"}); Log.e(TAG, i+""); } public void updateApi(){ db = oh.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("salary", 26000); int i = db.update("person", values, "name = ?", new String[]{"游天龙"}); Log.e(TAG, i+""); } public void selectApi(){ db = oh.getWritableDatabase(); Cursor cursor = db.query("person", null, null, null, null, null, null, null); StringBuilder sb=new StringBuilder(); while(cursor.moveToNext()){ String name = cursor.getString(cursor.getColumnIndex("name")); String phone = cursor.getString(cursor.getColumnIndex("phone")); String salary = cursor.getString(cursor.getColumnIndex("salary")); sb.append(name + ";" + phone + ";" + salary+"\n"); } Log.e(TAG, sb.toString()); }
事务
- 保证多条SQL语句要么同时成功,要么同时失败
- 最常见案例:银行转账
public void transaction(){ db = oh.getWritableDatabase(); try{ //开启事务 db.beginTransaction(); ContentValues values = new ContentValues(); values.put("salary", 12000); int result1=db.update("person", values, "name = ?", new String[]{"小志2"}); values.clear(); values.put("salary", 86000); int result2 = db.update("person", values, "name = ?", new String[]{"游天龙"}); //int i = 3/0; //设置 事务执行成功 //db.setTransactionSuccessful(); if (result1>0 && result2>0) { db.setTransactionSuccessful(); // 事务默认是失败的,要设置成功,否则数据不会修改 Log.e(TAG, "成功: "); } } catch (Exception e){ Log.e(TAG, e.getMessage()); } finally{ //关闭事务,同时提交,如果已经设置事务执行成功,那么sql语句就生效了,反之,sql语句回滚 db.endTransaction(); } }
把数据库的数据显示至屏幕
public class Person { private String _id; private String name; private String phone; private String salary; public String get_id() { return _id; } public void set_id(String _id) { this._id = _id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getSalary() { return salary; } public void setSalary(String salary) { this.salary = salary; } @Override public String toString() { return name + ", " + phone + ", " + salary; } public Person(String _id, String name, String phone, String salary) { super(); this._id = _id; this.name = name; this.phone = phone; this.salary = salary; } }
public class ShowData extends AppCompatActivity { private MyOpenHelper oh; private SQLiteDatabase db; private List<Person> personList; private LinearLayout l1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_data); l1=(LinearLayout)findViewById(R.id.l1); personList = new ArrayList<Person>(); //把数据库的数据查询出来 oh = new MyOpenHelper(this); db = oh.getWritableDatabase(); Cursor cursor = db.query("person", null, null, null, null, null, null, null); while(cursor.moveToNext()){ String _id = cursor.getString(0); String name = cursor.getString(1); String salary = cursor.getString(2); String phone = cursor.getString(3); Person p = new Person(_id, name, phone, salary); personList.add(p); } //把数据显示至屏幕 for (Person p : personList) { //1.集合中每有一条元素,就new一个textView TextView tv = new TextView(this); //2.把人物的信息设置为文本框的内容 tv.setText(p.toString()); tv.setTextSize(18); //3.把textView设置为线性布局的子节点 l1.addView(tv); } } }
四、ListView和BaseAdapter
- 就是用来显示一行一行的条目的
- MVC结构
- M:model模型层,要显示的数据 ————people集合
- V:view视图层,用户看到的界面 ————ListView
- c:control控制层,操作数据如何显示 ————adapter对象
- 每一个条目都是一个View对象
public class ShowData2 extends AppCompatActivity { private MyOpenHelper oh; private SQLiteDatabase db; private List<Person> personList; private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show_data2); lv=(ListView) findViewById(R.id.lv); personList = new ArrayList<Person>(); //把数据库的数据查询出来 oh = new MyOpenHelper(this); db = oh.getWritableDatabase(); Cursor cursor = db.query("person", null, null, null, null, null, null, null); while(cursor.moveToNext()){ String _id = cursor.getString(0); String name = cursor.getString(1); String salary = cursor.getString(2); String phone = cursor.getString(3); Person p = new Person(_id, name, phone, salary); personList.add(p); } lv.setAdapter(new MyAdapter()); } class MyAdapter extends BaseAdapter { //系统调用,用来获知集合中有多少条元素 @Override public int getCount() { return personList.size(); } //由系统调用,获取一个View对象,作为ListView的条目 //position:本次getView方法调用所返回的View对象,在listView中是处于第几个条目,那么position的值就是多少 @Override public View getView(int position, View convertView, ViewGroup parent) { Person p = personList.get(position); // TextView tv = new TextView(MainActivity.this); System.out.println("getView调用:" + position + ";" + convertView); // tv.setText(p.toString()); // tv.setTextSize(18); View v = null; //判断条目是否有缓存 if(convertView == null){ //把布局文件填充成一个View对象 v = View.inflate(ShowData2.this, R.layout.item_listview, null); } else{ v = convertView; } //获取布局填充器对象 // LayoutInflater inflater = LayoutInflater.from(MainActivity.this); // 使用布局填充器填充布局文件 // View v2 = inflater.inflate(R.layout.item_listview, null); // LayoutInflater inflater2 = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); // View v3 = inflater2.inflate(R.layout.item_listview, null); //通过资源id查找组件,注意调用的是View对象的findViewById TextView tv_name = (TextView) v.findViewById(R.id.tv_name); tv_name.setText(p.getName()); TextView tv_phone = (TextView) v.findViewById(R.id.tv_phone); tv_phone.setText(p.getPhone()); TextView tv_salary = (TextView) v.findViewById(R.id.tv_salary); tv_salary.setText(p.getSalary()); return v; } @Override public Object getItem(int position) { return null; } @Override public long getItemId(int position) { return 0; } } }
<?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="match_parent" > <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="名字" android:textSize="25sp" /> <LinearLayout android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/tv_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="号码" /> <TextView android:id="@+id/tv_salary" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="工资" /> </LinearLayout> </RelativeLayout>
- 屏幕上能显示多少个条目,getView方法就会被调用多少次,屏幕向下滑动时,getView会继续被调用,创建更多的View对象显示至屏幕
条目的缓存
- 当条目划出屏幕时,系统会把该条目缓存至内存,当该条目再次进入屏幕,系统在重新调用getView时会把缓存的条目作为convertView参数传入,但是传入的条目不一定是之前被缓存的该条目,即系统有可能在调用getView方法获取第一个条目时,传入任意一个条目的缓存
五、适配器
1.什么是适配器?
适配器:在安卓中,顾名思义就是把数据变成符合界面风格的形式,并且通过ListView显示出来。也就是说适配器是数据和界面之间的桥梁。
适配器在数据库中的数据(后台)和显示页面(前端)中充当一个转换器的角色,数据库中的数据(如数组,链表,数据库,集合等)通过适配器变成类手机页面能够正常显示的数据。可以看作是界面数据绑定的一种理解。假设把数据、适配器和ListView(页面)比喻成一个MVC模式的话,那么适配器(Adapter)在这中间就充当了Controller的角色。
2.为什么对象设置数据源
一般是为ListView提供数据的转换,当然GridView[网格视图]、Spinner[下拉列表]、Gallery[画廊]、ViewPage等都需要使用适配器来为其设置数据源。
ArrayAdapter
public void arrayAdapter(){ String[] objects = new String[]{ "小志", "小志的儿子", "萌萌" }; lv = (ListView) findViewById(R.id.lv); lv.setAdapter(new ArrayAdapter<String>(this, R.layout.item_listview, R.id.tv_name, objects)); }
SimpleAdapter
public void simpleadapter(){ //集合中每个元素都包含ListView条目需要的所有数据,该案例中每个条目需要一个字符串和一个整型,所以使用一个map来封装这两种数据 List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); Map<String, Object> map1 = new HashMap<String, Object>(); map1.put("photo", R.drawable.photo1); map1.put("name", "小志的儿子"); data.add(map1); Map<String, Object> map2 = new HashMap<String, Object>(); map2.put("photo", R.drawable.photo2); map2.put("name", "小志"); data.add(map2); Map<String, Object> map3 = new HashMap<String, Object>(); map3.put("photo", R.drawable.photo3); map3.put("name", "赵帅哥"); data.add(map3); lv = (ListView) findViewById(R.id.lv); lv.setAdapter(new SimpleAdapter(this, data, R.layout.item_listview, new String[]{"photo", "name"}, new int[]{R.id.iv_photo, R.id.tv_name})); }
六、对话框
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click1(View v){ Builder builder = new Builder(this); //设置图标 builder.setIcon(android.R.drawable.alert_dark_frame); //设置标题 builder.setTitle("欲练此功必先自宫"); //设置文本 builder.setMessage("李志平,想清楚哦"); //设置确定按钮 builder.setPositiveButton("确定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "感谢使用本软件,再见", 0).show(); } }); //设置取消按钮 builder.setNegativeButton("取消", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "若不自宫,一定不成功", 0).show(); } }); //使用创建器,生成一个对话框对象 AlertDialog ad = builder.create(); ad.show(); } public void click2(View v){ Builder builder = new Builder(this); builder.setTitle("请选择性别"); final String[] items = new String[]{ "男", "女" }; builder.setSingleChoiceItems(items, -1, new OnClickListener() { //which:用户所选的条目的下标 //dialog:触发这个方法的对话框 @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, "您选择的是:" + items[which], 0).show(); //关闭对话框 dialog.dismiss(); } }); builder.show(); } public void click3(View v){ Builder builder = new Builder(this); builder.setTitle("请选择您觉得帅的人"); final String[] items = new String[]{ "侃哥", "赵帅哥", "赵老师", "赵师兄" }; final boolean[] checkedItems = new boolean[]{ true, true, false, false }; builder.setMultiChoiceItems(items, checkedItems, new OnMultiChoiceClickListener() { //which:用户点击的条目的下标 //isChecked:用户是选中该条目还是取消该条目 @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { checkedItems[which] = isChecked; } }); //设置一个确定按钮 builder.setPositiveButton("确定", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String text = ""; for(int i = 0; i < 4; i++){ text += checkedItems[i]? items[i] + "," : ""; } Toast.makeText(MainActivity.this, text, 0).show(); dialog.dismiss(); } }); builder.show(); } }