Sqlite数据库分页查询(ListView分页显示数据)

标签: sqlite 数据库 分页 | 发表时间:2014-03-18 15:07 | 作者:xxm282828
出处:http://blog.csdn.net

今天项目中遇到个问题,之前数据量不算多的时候,ListView显示正常,但是当数据量很大得分时候,进入画面,显示数据比较慢,

而且不能放在UI线程中去拿数据,用子线程去拿把,画面出来了,但是数据要等很久才会出来,因此,这样给人的体验很不好,算不上好的设计。


因此,查了一下,关于数据库分页ListView分页。

对于ListView分页,不管是本地数据查询,还是网络数据获取,都应该是常见而且不可以忽视的一点吧。

下面介绍一下我的这个demo。


流程简述:

我在raw文件夹下面放了名称为city的数据库,里面包含全国2330个城市,以及所属省,拼音简写等信息。

首先在进入MainActivity的时候,创建数据库并读入sd卡文件中data/data/databases/city。

             然后我再开启子线程去读取前50条数据,显示在ListView中。

  当用户浏览数据,前50条不够时,他会滑动ListView以查看更多数据,此时,listview的数据源会递增,50 ,100,150,。。。。

  以50为增量不断增加.....

  这样避免了因一次性加载数据造成ANR,也给用户比较好的体验。


工程目录结构:



cls_city是城市信息类,Common是工具类,ViewHolder是ListView缓存机制帮助类,DataBaseHelper  数据库操作工具类/.....


package com.example.sqlitepagetest;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import android.database.Cursor;
import android.util.Log;

/**
 * <p>
 * </p>
 * 下午9:12:42
 * 
 * @auther dalvikCoder
 */
public class cls_city {

	public String _id = "";
	public String province = "";
	public String name = "";

	/**
	 * 
	 * select _id,province ,name from city order by _id limit perItemNum Offset
	 * currentPage*perItemNum ;--currentPage 从零开始
	 * <p>
	 * 根据条目数量查询
	 * </p>
	 * 
	 * @param dbh
	 * @param num
	 *            num[1]-->每一页显示的条目数量 num[0]--->从第几条开始
	 * @return List<cls_city>
	 */
	public static List<cls_city> getCityList(DatabaseHelper dbh, int num[]) {

		String sql = "select _id,province ,name from city order by _id limit "
				+ num[0] + "," + num[1];

		Log.e("select city's sql --------------", sql);
		Cursor cursor = dbh.rawQuery(sql);
		List<cls_city> list = new ArrayList<cls_city>();
		cls_city cls = null;
		while (cursor.moveToNext()) {

			cls = new cls_city();
			setClassValueBycursor(cls, cursor);
			list.add(cls);
		}

		return list;
	}

	/**
	 * <p>
	 * 利用反射机制给对象赋值
	 * </p>
	 * @param obj
	 * @param cursor void
	 */
	public static void setClassValueBycursor(Object obj, Cursor cursor) {
		int ColCount = cursor.getColumnCount();
		int i = 0;
		for (i = 0; i < ColCount; i++) {
			String ColName = cursor.getColumnName(i);

			try {
				Field f = obj.getClass().getField(ColName);
				String ret = cursor.getString(i);
				if (f == null)
					continue;
				if (ret == null)
					ret = "";
				f.set(obj, ret);
			} catch (SecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

package com.example.sqlitepagetest;

import android.util.SparseArray;
import android.view.View;

/**
 * <p>
 * ListView缓存的写法比较多种,下面也是其中一中,
 * </p>
 * 下午9:45:29
 * 
 * @auther dalvikCoder
 */
public class ViewHolder {
	// I added a generic return type to reduce the casting noise in client code
	@SuppressWarnings("unchecked")
	public static <T extends View> T get(View view, int id) {
		SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
		if (viewHolder == null) {
			viewHolder = new SparseArray<View>();
			view.setTag(viewHolder);
		}
		View childView = viewHolder.get(id);
		if (childView == null) {
			childView = view.findViewById(id);
			viewHolder.put(id, childView);
		}
		return (T) childView;
	}

}


在使用了ViewHolder之后,自定义适配器里面的代码看起来舒服多了。

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

		if (convertView == null) {

			convertView = inflater.inflate(R.layout.city_lv_item, null);

		}

		TextView cityId = ViewHolder.get(convertView, R.id.cityidtxt);
		TextView provincetxt = ViewHolder.get(convertView, R.id.provincetxt);
		TextView cityName = ViewHolder.get(convertView, R.id.nametxt);

		cls_city city = cityList.get(position);
		cityId.setText(city._id);
		provincetxt.setText(city.province);
		cityName.setText(city.name);

		return convertView;
	}

下面是总的MainActivity类,里面的注释比较详细,也比较简单。

当然如果有兴趣你可以给ListView添加尾部视图,比如加个状态文字或者加个加个进度条,不过像本地数据,好像用不了多少时间

从数据库拿出来把,加了反而感觉多余,我性子比较急,反正我是受不了的。


package com.example.sqlitepagetest;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

public class MainActivity extends Activity {

	private ListView cityListView;
	private List<cls_city> cityList;
	private CityAdapter cityAdapter;

	private MyThread thread = null;

	/**
	 * 每页有数据条数 这个数量可以根据需要更改,而不需在程序中更改具体数值
	 * **/
	private int perPageItemNum = 100;
	/** 当前是第几页 0表示第一页 **/
	private int currentPage = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		setUpView();

	}

	private void setUpView() {

		cityList = new ArrayList<cls_city>();

		try {
			Common.loadCityDatabase(this);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Common.dbh = new DatabaseHelper(this, "city");
		cityListView = (ListView) findViewById(R.id.citylistview);

		cityAdapter = new CityAdapter(this, cityList);
		cityListView.setAdapter(cityAdapter);
		cityListView.setOnScrollListener(new OnScrollListener() {

			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {

				if (view.getLastVisiblePosition() == view.getCount() - 1
						&& scrollState == OnScrollListener.SCROLL_STATE_IDLE) {

					if (thread != null && !thread.isInterrupted()) {

						thread.interrupt();
						thread = null;
					}
					currentPage++;
					cityListView.setSelection(view.getLastVisiblePosition());// 设置显示位置,这句只是让Listview停留在最后末尾的显示而已,加不加影响不大
					thread = new MyThread();
					thread.start();

				}
			}

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {

			}
		});

		if (thread == null) {

			thread = new MyThread();
			thread.start();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	class MyThread extends Thread {

		@Override
		public void run() {

			int num[] = new int[2];
			num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50
			num[1] = perPageItemNum;
			List<cls_city> dataList = cls_city.getCityList(Common.dbh, num);
			// try {
			// Thread.sleep(1000);
			// } catch (InterruptedException e) {
			// e.printStackTrace();
			// }
			Message msg = new Message();
			msg.what = 1;
			msg.obj = dataList;
			mHandler.sendMessage(msg);

		}
	}

	private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {

			super.handleMessage(msg);

			List<cls_city> dataList = (List<cls_city>) msg.obj;

			if (!dataList.isEmpty()) {

				cityAdapter.refresh(dataList);
			}
		}

	};

}

Common类

package com.example.sqlitepagetest;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import android.content.Context;

/**
 * <p>
 * </p>
 * 下午9:02:19
 * 
 * @auther dalvikCoder
 */
public class Common {

	public static DatabaseHelper dbh = null;

	public static void loadCityDatabase(Context context) throws Exception {

		// ------------创建路径
		String path = "/data/data/" + context.getPackageName() + "/databases";
		File file = new File(path);

		if (!file.exists()) {

			file.mkdirs();
		}
		// --------------该路径下创建数据库文件
		File f = new File(path, "city");
		if (!f.exists()) {

			InputStream is = context.getResources().openRawResource(R.raw.city);

			FileOutputStream out = new FileOutputStream(path + "/city");
			byte buffer[] = new byte[2 * 1024];
			int len = 0;
			while ((len = is.read(buffer)) > 0) {

				out.write(buffer, 0, len);

			}
			out.close();
			is.close();

		}
	}

}

最后贴上效果图:


大家可以很清楚的看到加载的数据增长过程、




艹,受不了了,CSDN发布文章的时候还加个验证码。艾玛,我去,无论怎么输入都不对。。。。。哎。。。。

在查阅资料时,看到也有很多写这个问题的:

这里贴出来,

可以作为参考:

http://blog.csdn.net/dawanganban/article/details/9989555 

http://blog.csdn.net/hellogv/article/details/6011934

onScroll会被很多次调用,刚开始我是在onScroll里面判断的,后来发现不行。下面这位仁兄观察的很仔细。

http://blog.csdn.net/zhouzme/article/details/19207671

最后附上我的Demo源码下载地址,如有批评指正请在后面留言:


http://download.csdn.net/detail/xxm282828/7056757


作者:xxm282828 发表于2014-3-18 7:07:40 原文链接
阅读:0 评论:0 查看评论

相关 [sqlite 数据库 分页] 推荐:

Sqlite数据库分页查询(ListView分页显示数据)

- - CSDN博客推荐文章
今天项目中遇到个问题,之前数据量不算多的时候,ListView显示正常,但是当数据量很大得分时候,进入画面,显示数据比较慢,. 而且不能放在UI线程中去拿数据,用子线程去拿把,画面出来了,但是数据要等很久才会出来,因此,这样给人的体验很不好,算不上好的设计. 因此,查了一下,关于数据库分页ListView分页.

将Access转为SQLite数据库

- zhouding - 博客园-首页原创精华区
           SQLite是一个轻量型的数据库,各方面由于Access,对于小型网站来说,非常适合. 我今天决定将系统的数据库由Access转换为SQLite. 在网上搜索了一下,找到了DBManager这个工具,不得不说,这个软件确实好,很强大,在网上找注册码也找到半天,找不到一个.            只要将Access转换为SQLite,然后使用System.Data.SQLite.dll来访问数据库.

SQLite数据库存储引擎设计

- - searchdatabase
  SQLite是一个嵌入式库并且实现了零配置、无服务端和事务功能的SQL数据库引擎. 它在广泛领域内被使用,而且单线程读写性能与MySQL比肩,并且保证ACID性.   SQLite的存储后端是采用Btree实现,多个连接可以并发操作,但是同一时间只允许一个写着存在.   SQLite在硬盘上一个数据库一个文件,每个数据库文件头部保存有这个数据库的元信息,包括版本,大小,Btree根节点位置等等.

关于SQLite数据库的那些事儿

- - ITeye博客
在Android平台上,集成了一个嵌入式关系型数据库——SQLite. SQLite第一个Alpha版本诞生于2000年5月. 它是遵守ACID的关联式数据库管理系统,包含在一个相对小的C库中,同时是D.RichardHipp建立的公有领域项目. SQLite官方网站: http://www.sqlite.org/;SQLite中文社区:http://www.sqlite.com.cn/.

内存数据库FastDB和SQLite性能测评

- - CSDN博客数据库推荐文章
在很多项目中,经常会碰到这样的需求,需要对大量数据进行快速存储、查询、删除等操作,特别是在一些针对诸如运营商、银行等大型企业的应用中,这些需求尤为常见. 比如智能网中的大量在线并发用户的数据管理、软交换平台中的在线信息交互、宽带/3G等数据网中在线用户行为记录等等. 针对这些情形,我们通常需要选择高性能的数据库产品,而且通常需要使用内存数据库,顾名思义,内存数据库指的是所有的数据访问控制都在内存中进行,这是与磁盘数据库相对而言的,磁盘数据库虽然也有一定的缓存机制,但都不能避免从外设到内存的交换,而这种交换过程对性能的损耗是致命的,目前主流数据库如SYBASE、ORACLE等都有这种缓存机制,如将特定表绑定一定的缓存,从而在一定程度上改善数据吞吐性能.

Android SQLite数据库版本升级原理解析

- - CSDN博客推荐文章
Android使用SQLite数据库保存数据,那数据库版本升级是怎么回事呢,这里说一下. 安装v1.0,假设v1.0版本只有一个account表,这时走继承SQLiteOpenHelper的onCreate,不走onUpgrade. 1、v1.0(直接安装v1.0). 1、v1.0   -->  v2.0              不走onCreate,走onUpgrade.

Android学习笔记(六)SQLite

- - 博客园_首页
SQLite是一个极轻量型的数据库. 它在提供了和大型数据库相当的功能,还具有轻便、跨平台等优点,SQLite使用非常方便,并不需要我们像常规数据库(SQLServer,Mysql等)那样进行安装,在Android的JDK中,其实是已经包含了SQLite这个数据库的核心. 当然我们必须要强调一点,SQLite并不是只针对Android的,其实它还可以用在别的很多地方.

【数据库】优化排序 && 高效分页

- - CSDN博客推荐文章
同时使用了order by,limit,如果没有索引会很慢. 而sey的选择又很低,可以增加一些特殊的索引来做排序. 例如,创建(sex,rating)索引. 即使有索引,如果用户需要翻页,并且翻页到比较靠后时查询也可能非常慢. 下面这个查询就通过order by 和limit偏移量的组合翻页到很靠后面的时候:.

SQLite的原子提交原理

- way - chensheng.net
本文源自:http://www.sqlite.org/atomiccommit.html,2007/11/28的版本. 本人正在做一个项目,在项目中定义了自己的文件格式,为了做到停电或程序崩溃不损坏这些文件原有的数据,故针对操作的原子性做一些思考,后来看到sqlite的这篇文章,与自己的实现方式作了一些对比.

sqlite插入速度优化方案

- - CSDN博客移动开发推荐文章
先贴上SQLiteOpenHelper的代码,我们创建测试的表是user,它有四个字段id,name,age,height,remark,测试用的小米2,默认条数为100行. // 必须要有这一个构造方法. // 当数据库第一次创建的时候被调用,. // 当调用getReadableDatabase ()或getWritableDatabase 的时候.