您的位置:首页 > 数据库

AutoCompleteTextView+SQLite实现自动检索

2013-03-28 10:37 435 查看
比如某些网页或搜索引擎会把用户搜索过的内容记录下来,当下次输入曾经搜索过的内容也会列出来。来个图,就明白了



保存记录有四种方法:Preferences,Files,Databases,Network。这里以Databases 为例,首先搭建个Provider(不会的童鞋可以参考sdk 里的demo:NotePad-记事本)。

先看看目录结构:



Message.java定义一些常量,数据库字段及Uri:

package com.xyz.autocomplete.provider;

import android.net.Uri;
import android.provider.BaseColumns;

public class Message {
public static final String AUTHORITY = "com.xyz.auto";

public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.xyz.auto";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.xyz.auto";

public static final class Info implements BaseColumns {
public static final String TABLE_NAME = "messages";
public static final Uri CONTENT_URI = Uri.parse("content://"
+ AUTHORITY + "/messages");
public static final Uri DELETE_ALL_URI = Uri.parse("content://"
+ AUTHORITY + "/messages/del/all");
public static final String MESSAGE_CONTENT = "message";
public static final String DEFAULT_SORT_ORDER = " message ASC ";
}
}


DatabaseHelper.java创建数据库及建表:

package com.xyz.autocomplete.provider;

import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DatabaseHelper extends SQLiteOpenHelper {

public static final String DB_NAME = "msg.db";
private static final int DB_VERSION = 1;

public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
db.execSQL("CREATE TABLE " + Message.Info.TABLE_NAME + " ("
+ Message.Info._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ Message.Info.MESSAGE_CONTENT + " TEXT);");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS " + Message.Info.TABLE_NAME);
onCreate(db);
}

}


AutoProvider.java是继承ContentProvider对数据库进行插入,删除,查询,更新等操作,本例只需插入,查询,删除表几个操作:

package com.xyz.autocomplete.provider;

import java.sql.SQLException;
import java.util.HashMap;

import com.xyz.autocomplete.R;
import com.xyz.autocomplete.provider.Message.Info;

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.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;

public class AutoProvider extends ContentProvider {

private static HashMap<String, String> sInfoProjectionMap;
private static final int MESSAGE = 1;
private static final int MESSAGE_ID = 2;
private static final int DELETE_ALL = 3;

private static final UriMatcher sUriMatcher;

private DatabaseHelper mOpenHelper;

static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Message.AUTHORITY, Info.TABLE_NAME, MESSAGE);
sUriMatcher.addURI(Message.AUTHORITY, Info.TABLE_NAME + "/#",
MESSAGE_ID);
sUriMatcher.addURI(Message.AUTHORITY, Info.TABLE_NAME + "/del/all",
DELETE_ALL);

sInfoProjectionMap = new HashMap<String, String>();
sInfoProjectionMap.put(Info._ID, Info._ID);
sInfoProjectionMap.put(Info.MESSAGE_CONTENT, Info.MESSAGE_CONTENT);
}

...

@Override
public boolean onCreate() {
// TODO Auto-generated method stub
mOpenHelper = new DatabaseHelper(getContext());
return true;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
if (sUriMatcher.match(uri) != MESSAGE) {
throw new IllegalArgumentException("Provider insert() Unknown URI "
+ uri);
}
if (!values.containsKey(Info.MESSAGE_CONTENT)) {
values.put(Info.MESSAGE_CONTENT, "default");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(Info.TABLE_NAME, Info.TABLE_NAME, values);
if (rowId <= 0) {
try {
throw new SQLException("Failed to insert row into " + uri);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Uri retUri = ContentUris.withAppendedId(Info.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(retUri, null);
Toast.makeText(
getContext(),
getContext().getString(R.string.insert_info)
+ values.getAsString(Info.MESSAGE_CONTENT),
Toast.LENGTH_SHORT).show();
return retUri;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Info.TABLE_NAME);
switch (sUriMatcher.match(uri)) {
case MESSAGE:
queryBuilder.setProjectionMap(sInfoProjectionMap);
break;
case MESSAGE_ID:
queryBuilder.setProjectionMap(sInfoProjectionMap);
queryBuilder.appendWhere(Info._ID + " = "
+ uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Provider query() Unknown URI "
+ uri);
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
...
case DELETE_ALL:
db.execSQL("DROP TABLE IF EXISTS " + Info.TABLE_NAME);
db.execSQL("CREATE TABLE " + Message.Info.TABLE_NAME + " ("
+ Message.Info._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ Message.Info.MESSAGE_CONTENT + " TEXT);");
return -1;
default:
throw new IllegalArgumentException("Provider delete() Unknown URI "
+ uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
...

}

上面3个文件就把Provider搭建好了,剩下要做的就是把记录集里以AutoCompleteTextView输入内容为头(startWith)的记录检索出来并显示出来。AutoCompleteTextView有个很重要的属性是android:completionThreshold,用于表明最小要敲入多少字符才开始显示list filter。AutoCompleteTextView要列出内容肯定要给它传一个数据源(记录集),就是通过setAdapter,这个适配器有个要求就是要实现android.widget.Filterable
接口,最简单的可以用ArrayAdapter ,如果用它的话,PopupListView样式不怎么好看,自定义适配器,自定义布局就好多了,请看:AutoCompleteAdapter.java(参考ArrayAdapter):

package com.xyz.autocomplete;

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

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class AutoCompleteAdapter extends BaseAdapter implements Filterable {

private int mLayoutId;
private AutoFilter mFilter;
private Context mContext;
private List<String> mData;
private List<String> mObjects;

private Object mLock = new Object();

public AutoCompleteAdapter(Context ctx, int layout, ArrayList<String> data) {
mContext = ctx;
mLayoutId = layout;
mData = data;
mObjects = data;
}

@Override
public int getCount() {
// TODO Auto-generated method stub
return mObjects.size() > 0 ? mObjects.size() + 1 : 0;
}

@Override
public String getItem(int position) {
// TODO Auto-generated method stub
return position < getCount() - 1 ? mObjects.get(position) : mContext
.getString(R.string.clear_info);
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.auto_complete_item, null);
holder.mShowText = (TextView) convertView.findViewById(R.id.item);
holder.mIcon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (position == 0) {
convertView.setBackgroundResource(R.drawable.item_corner_top);
holder.mShowText.setText(getItem(position));
holder.mIcon.setVisibility(View.GONE);
} else if (position == getCount() - 1) {
convertView.setBackgroundResource(R.drawable.item_corner_bottom);
holder.mShowText.setText(mContext.getString(R.string.clear_info));
holder.mIcon.setVisibility(View.VISIBLE);
} else {
convertView.setBackgroundResource(R.drawable.item_corner_shape);
holder.mShowText.setText(getItem(position));
holder.mIcon.setVisibility(View.GONE);
}
return convertView;
}

class ViewHolder {
TextView mShowText;
ImageView mIcon;
}

@Override
public Filter getFilter() {
// TODO Auto-generated method stub
if (mFilter == null) {
mFilter = new AutoFilter();
}
return mFilter;
}

private class AutoFilter extends Filter {

@Override
protected FilterResults performFiltering(CharSequence constraint) {
// TODO Auto-generated method stub
FilterResults results = new FilterResults();
if (constraint == null || constraint.length() == 0) {
ArrayList<String> list;
synchronized (mLock) {
list = new ArrayList<String>(mData);
}
results.values = list;
results.count = list.size();
} else {
String prefixString = constraint.toString().toLowerCase();
ArrayList<String> values;
synchronized (mLock) {
values = new ArrayList<String>(mData);
}

final int count = values.size();
final ArrayList<String> newValues = new ArrayList<String>();

for (int i = 0; i < count; i++) {
final String value = values.get(i);
final String valueText = value.toString().toLowerCase();

// First match against the whole, non-splitted value
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;

// Start at index 0, in case valueText starts with
// space(s)
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
}
results.values = newValues;
results.count = newValues.size();
}
return results;
}

@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
// TODO Auto-generated method stub
mObjects = (List<String>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}

MainActivity.java做的事情是查询数据库,绑定数据源给AutoCompleteTextView:

package com.xyz.autocomplete;

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

import com.xyz.autocomplete.provider.DatabaseHelper;
import com.xyz.autocomplete.provider.Message.Info;

import android.app.Activity;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener,
OnItemClickListener {

private EditText mInputText;
private Button mBtn;
private AutoCompleteTextView mAutoCompleteView;
private static final int QUERY_AUTO_TOKEN = 100;
private ArrayList<String> mData = new ArrayList<String>();
private AutoCompleteAdapter mAutoAdaper = null;
private DbChangeResolver mDbChangeResolver;

private QueryHandler mQueryHandler = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

mInputText = (EditText) findViewById(R.id.input);
mBtn = (Button) findViewById(R.id.add);
mBtn.setOnClickListener(this);
mAutoCompleteView = (AutoCompleteTextView) findViewById(R.id.auto_complete);
mAutoCompleteView.setOnItemClickListener(this);

mAutoAdaper = new AutoCompleteAdapter(this,
R.layout.auto_complete_item, mData);
mAutoCompleteView.setAdapter(mAutoAdaper);
mQueryHandler = new QueryHandler(getContentResolver());
startQuery();
mDbChangeResolver = new DbChangeResolver(new Handler());
getContentResolver().registerContentObserver(
Info.CONTENT_URI, true, mDbChangeResolver);
}

private void startQuery() {
mQueryHandler.startQuery(QUERY_AUTO_TOKEN, new Integer(0),
Info.CONTENT_URI, new String[] { Info.MESSAGE_CONTENT }, null,
null, Info.DEFAULT_SORT_ORDER);
}

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (!TextUtils.isEmpty(mInputText.getText().toString().trim())) {
ContentValues cv = new ContentValues(1);
cv.put(Info.MESSAGE_CONTENT, mInputText.getText().toString().trim());
getContentResolver().insert(Info.CONTENT_URI, cv);
mInputText.getText().clear();
} else {
Toast.makeText(this, getString(R.string.toast_info),
Toast.LENGTH_LONG).show();
}
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
if (position == parent.getCount() - 1) {
getContentResolver().delete(Info.DELETE_ALL_URI, null, null);
mAutoCompleteView.getText().clear();
mData.clear();
Toast.makeText(this, "所有记录都清除啦", Toast.LENGTH_LONG).show();
}
}

private class QueryHandler extends AsyncQueryHandler {

public QueryHandler(ContentResolver cr) {
super(cr);
// TODO Auto-generated constructor stub
}

@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
if (QUERY_AUTO_TOKEN == token) {
if (cursor == null) {
return;
}
mData.clear();
while (cursor.moveToNext()) {
mData.add(cursor.getString(cursor
.getColumnIndexOrThrow(Info.MESSAGE_CONTENT)));
}
mAutoAdaper.notifyDataSetChanged();
}
}
}

private class DbChangeResolver extends ContentObserver {
private Handler mHandler;

public DbChangeResolver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
mHandler = handler;
}

@Override
public void onChange(boolean selfChange) {
// TODO Auto-generated method stub
super.onChange(selfChange);
mHandler.removeCallbacks(mDataChangeRunnable);
mHandler.postDelayed(mDataChangeRunnable, 300);
}
}

private Runnable mDataChangeRunnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
startQuery();
}
};
}

过多的就不解释了。

源码下载地址:http://download.csdn.net/detail/zhouyuanjing/5187264
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: