此文旨在能帮助有需要的Android新手实现一个小小的功能,代码质量不具有任何参考意义,文章最下面的格式也有点小问题,搞了一会没搞定,放弃了。
目前无论是PC上还是手机上,浏览器的地址栏都带有历史访问记录自动提示功能,例如之前访问过http://www.qq.com,那么当下次再次输入qq,或者www.q的时候(具体出发规则可定制),http://www.qq.com就会在地址栏下面以下拉窗口的形式自动给出提示,方便用户选择并完成地址的输入工作。
在Android中,通过sdk提供的AutoCompleteTextView我们可以完成类似的功能,当然这里布局限于浏览器的地址提示,任何历史记录都可以通过该控件来实现,下面将通过代码来说明具体的实现过程。
要实现历史记录的提示功能,首先要解决的问题就是历史记录的存储,Android中提供了几种存储方式,官方sdk文档中给出了详细的说明:http://developer.android.com/guide/topics/data/data-storage.html ,大概分析了一下,对于浏览器历史记录而言,SQLite应该是最合适的存储方式,Android原生浏览器也使用了SQLite的存储方式,因此本文也基于SQLite来保存历史记录。对于SQLite的介绍可以参考这篇文章:http://www.ibm.com/developerworks/cn/opensource/os-cn-sqlite/,下面就先来实现历史记录的存储部分。
Android 提供了 SQLiteOpenHelper 来创建数据库,我们只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库,并在onCreate()中创建需要用到的表,代码如下:
import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class SuggestionDBHelper extends SQLiteOpenHelper{ private static final String DBNAME = "url.db";// 数据库名 public SuggestionDBHelper(Context context){ super(context, DBNAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS history" + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT, title TEXT)");//创建history表,包含id,url和title字段 } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}
为了解除上层逻辑和数据库操作之间的耦合,我们通过创建类SuggestionDBManage来封装url历史记录的添加和删除操作,这里主要实现了历史记录的insert和query函数,代码如下,代码不难理解:
import java.util.ArrayList;import java.util.List;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import com.tencent.test.SuggestionItem;public class SuggestionDBManage { private SuggestionDBHelper mDateBaseHelper; private SQLiteDatabase mDatabase; public SuggestionDBManage(Context context) { mDateBaseHelper = new SuggestionDBHelper(context); mDatabase = mDateBaseHelper.getWritableDatabase(); } public void insert(SuggestionItem urlitem) { mDatabase.beginTransaction(); // 开始事务 try { mDatabase.execSQL("INSERT INTO history VALUES(null, ?, ?)", new Object[] { urlitem.getUrl(), urlitem.getTitle() }); mDatabase.setTransactionSuccessful(); // 设置事务成功完成 } finally { mDatabase.endTransaction(); // 结束事务 } } public List<SuggestionItem> query(String prefix) { String like = prefix + "%"; List<SuggestionItem> values = new ArrayList<SuggestionItem>(); final String selection = "(url LIKE ? OR url LIKE ? OR url LIKE ? OR url LIKE ?)"; String[] selectionArgs = new String[4]; selectionArgs[0] = "http://" + like; selectionArgs[1] = "http://www." + like; selectionArgs[2] = "https://" + like; selectionArgs[3] = "https://www." + like; Cursor cursor = mDatabase.query("history", new String[] { "url", "title" }, selection, selectionArgs, null, null, null); cursor.moveToFirst(); for (int i = 0; i < cursor.getCount(); i++) { String url = cursor.getString(0); String title = cursor.getString(1); values.add(new SuggestionItem(url, title)); cursor.moveToNext(); } return values; } public void close(){ mDatabase.close(); }}为了实现历史记录的自动提示功能,我们需要借助Android提供的AutoCompleteTextView控件,该控件使用了Adapter方式来绑定用来提示的数据,用过类似ListView控件的话应该对这种方式不会陌生,然而对于AutoCompleteTextView来说,绑定的Adapter必须是实现了Filterable接口的Adapter,关键字的过滤功能是通过Filter这个类来实现的,而AutoCompleteTextView会调用Filterable接口的getFilter()函数,得到具体的Filter实例后执行过滤,具体的过滤规则是通过继承Filter这个类,并且重载Filter类的performFiltering函数来定制的。
数据库的访问通过AsycTask来完成,这是考虑到数据库的query操作有可能是一个比较耗时的操作,因此不适合放到主线程中,通过AsycTask可以避免界面失去响应的问题,从数据库取得数据后通过notifyDataSetChanged()来更新界面,具体代码如下:
import java.util.ArrayList;import java.util.List;import android.content.Context;import android.os.AsyncTask;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Filter;import android.widget.Filterable;import android.widget.TextView;import com.tencent.urldatabase.SuggestionDBManage;public class SuggestionAdapter extends BaseAdapter implements Filterable { private Context context; private SuggestionDBManage mDbManage; private ArrayFilter mFilter; private List<SuggestionItem> mFilterItems = new ArrayList<SuggestionItem>();// 过滤后的item public SuggestionAdapter(Context context, SuggestionDBManage dbManage) { this.context = context; mDbManage = dbManage; } @Override public Filter getFilter() { if (mFilter == null) { mFilter = new ArrayFilter(); } return mFilter; } private class SuggestionAsyncTask extends AsyncTask<String, Void, List<SuggestionItem>>{ @Override protected List<SuggestionItem> doInBackground(String... params) { List<SuggestionItem> suggestionItems = mDbManage.query(params[0]); return suggestionItems; } @Override protected void onPostExecute(List<SuggestionItem> result) { mFilterItems = result; notifyDataSetChanged(); super.onPostExecute(result); } } private class ArrayFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { String prefixString = prefix.toString().toLowerCase(); startSuggestionsAsync(prefixString); return null; } private void startSuggestionsAsync(String prefixString) { new SuggestionAsyncTask().execute(prefixString.toString()); } @Override protected void publishResults(CharSequence constraint, FilterResults results) {} } @Override public int getCount() { return mFilterItems.size(); } @Override public Object getItem(int position) { return mFilterItems.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate( R.layout.simple_list_item_for_autocomplete, null); holder.url = (TextView) convertView.findViewById(R.id.url); holder.title = (TextView) convertView.findViewById(R.id.title); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.url.setText(mFilterItems.get(position).getUrl()); holder.title.setText(mFilterItems.get(position).getTitle()); return convertView; } class ViewHolder { TextView url; TextView title; }}SuggestionItem
public class SuggestionItem { private String mUrl; private String mTitle; public SuggestionItem(String url, String title) { mUrl = url; mTitle = title; } public String getUrl() { return mUrl; } public String getTitle() { return mTitle; }}
MainActivity.java
import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.AutoCompleteTextView;import android.widget.Button;import android.widget.TextView;import com.tencent.urldatabase.SuggestionDBManage;public class MainActivity extends Activity implements OnItemClickListener{ private AutoCompleteTextView mAutoCompleteTextView; private Button mSaveButton; private SuggestionDBManage mUrlDBManage; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mUrlDBManage = new SuggestionDBManage(getApplicationContext()); mAutoCompleteTextView = (AutoCompleteTextView)findViewById(R.id.autotextview); mAutoCompleteTextView.setText(""); mAutoCompleteTextView.setOnItemClickListener(this); SuggestionAdapter adapter = new SuggestionAdapter(this, mUrlDBManage); mAutoCompleteTextView.setAdapter(adapter); mSaveButton = (Button)findViewById(R.id.savaurl); mSaveButton.setOnClickListener(new OnClickListener() { //这部分代码只是为了添加初始数据,只考虑http://开头的地址 @Override public void onClick(View v) { String prefix = "http://"; String urlString = mAutoCompleteTextView.getText().toString(); if (!urlString.startsWith(prefix)) { mUrlDBManage.insert(new SuggestionItem(prefix + urlString, "unknow")); } else { mUrlDBManage.insert(new SuggestionItem(urlString, "unknow")); } } }); } @Override protected void onDestroy() { mUrlDBManage.close(); super.onDestroy(); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { TextView urlteTextView = (TextView)view.findViewById(R.id.title); mAutoCompleteTextView.setText(urlteTextView.getText()); }}
main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <AutoCompleteTextView android:id="@+id/autotextview" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:singleLine="true" /> <Button android:id="@+id/savaurl" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="save"/></LinearLayout>
simple_list_item_for_autocomplete.xml
<?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="match_parent" android:orientation="horizontal" > <TextView android:id="@+id/url" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:textSize="16sp"/> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="right" android:textSize="16sp"/></LinearLayout>