Android中的AutoCompleteTextVie控件具有根据输入内容自动匹配结果的功能。但有时候其默认的匹配规则不能满足我们的需求。例如在浏览器的输入框内输入网址的一部分,需要自动找出匹配的结果。如输入www.bai,则自动匹配出http://www.baidu.com。
研究ArrayAdapter的默认Filter,发现其匹配规则大致如下:
if (valueText.startsWith(prefixString)) {
newValues.add(value);
} else {
final String[] words = valueText.split(" ");
final int wordCount = words.length;
for (int k = 0; k < wordCount; k++) {
if (words[k].startsWith(prefixString)) {
newValues.add(value);
break;
}
}
}
首先是检查startsWith(prefixString),如果成功则添加到结果集里面;如果失败,则将valueText以空格分片,然后对每个分片检查startsWith(prefixString),如果成功,则添加到结果集。而我们需要的网址匹配则不需要这么做,我们需要的是如果valueText.contains(constraint)成功,则将记录添加到结果集,即输入www.bai,则自动列出http://www.baidu.com。
要实现这种功能,需要实现一个自定义的Adapter,继承BaseAdapter,同时实现Filterable接口。然后在自定义的Adapter里面,实现一个自定义的Filter。关键代码如下:
private class MyFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
int count = mOriginalValues.size();
ArrayList<T> values = new ArrayList<T>();
for (int i = 0; i < count; i++) {
T value = mOriginalValues.get(i);
String valueText = value.toString();
if (null != valueText && null != constraint
&& valueText.contains(constraint)) {
values.add(value);
}
}
results.values = values;
results.count = values.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
然后在getFilter()方法里面,返回自定义的Filter。
以下代码使用WebView实现了一个简易的浏览器,使用了上述的自定义Filter,具有如下功能:
1、输入网址时,自动匹配历史记录
2、保存/清空历史记录
3、网页刷新
4、网页回退
代码如下:
1、MainActivity.java
package com.lxg.webview;
import java.util.ArrayList;
import java.util.List;
import com.lxg.webview.R;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private WebView mWebView = null;
private AutoCompleteTextView mUrlInput = null;
private ImageButton mUrlGoto = null;
private ImageButton mUrlRefresh = null;
private ProgressDialog mProgressDialog = null;
private Handler mHandler = null;
private FilterAdapter<String> mUrlFilterAdapter = null;
private List<String> mUrlHistory;
private SharedPreferences mPreferences = null;
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.setMessage("Please Wait...");
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (!Thread.currentThread().isInterrupted()) {
switch (msg.what) {
case 0:
mProgressDialog.show();
break;
case 1:
mProgressDialog.cancel();
break;
default:
break;
}
}
}
};
initWebView();
mUrlGoto = (ImageButton) findViewById(R.id.web_goto);
mUrlGoto.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendEmptyMessage(0);
String url = mUrlInput.getText().toString();
if (!url.startsWith("http://")) {
url = "http://" + url;
}
Log.d(TAG, "Goto: " + url);
mWebView.loadUrl(url);
}
});
mUrlRefresh = (ImageButton) findViewById(R.id.web_refresh);
mUrlRefresh.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("javascript:window.location.reload( true )");
}
});
mUrlHistory = getHistory();
mUrlFilterAdapter = new FilterAdapter<String>(this,
R.layout.url_listitem, mUrlHistory);
mUrlInput = (AutoCompleteTextView) findViewById(R.id.web_input);
mUrlInput.setAdapter(mUrlFilterAdapter);
mUrlInput.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER
&& event.getAction() == KeyEvent.ACTION_DOWN) {
mUrlGoto.performClick();
return true;
}
return false;
}
});
mUrlInput.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
String url = ((TextView) view).getText().toString();
Log.d(TAG, "Item clicked: " + url);
mUrlInput.setText(url);
mUrlGoto.performClick();
}
});
}
private void initWebView() {
mWebView = (WebView) findViewById(R.id.webView);
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setBuiltInZoomControls(true);
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(true);
settings.setDatabaseEnabled(true);
mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
Log.e(TAG, "Load Url failed: " + failingUrl);
Log.e(TAG, "Error Message: " + description);
super.onReceivedError(view, errorCode, description, failingUrl);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (100 == newProgress) {
mHandler.sendEmptyMessage(1);
refreshUrlHistory(mUrlFilterAdapter, view.getOriginalUrl());
}
super.onProgressChanged(view, newProgress);
}
});
}
private void refreshUrlHistory(FilterAdapter<String> adapter, String url) {
if (null == url) {
Log.e(TAG, "url is null");
return;
}
if (!mUrlHistory.contains(url)) {
mUrlHistory.add(url);
adapter.add(url);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_clearHistory:
clearHistory();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
if (mWebView.canGoBack()) {
mWebView.goBack();
return true;
} else {
confirmExit();
}
}
return super.onKeyDown(keyCode, event);
}
private void confirmExit() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Quit");
builder.setMessage("Are you sure want to quit?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
MainActivity.this.finish();
}
});
builder.setNegativeButton("No", null);
builder.create().show();
}
@Override
protected void onDestroy() {
saveHistory(mUrlFilterAdapter);
super.onDestroy();
}
private List<String> getHistory() {
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
List<String> urlHistory = new ArrayList<String>();
int historyCount = mPreferences.getInt("URL_COUNT", 0);
Log.d(TAG, "Url count: " + historyCount);
for (int i = 0; i < historyCount; i++) {
String url = mPreferences.getString("URL_ITEM_" + i, null);
Log.d(TAG, url);
urlHistory.add(url);
}
return urlHistory;
}
private void saveHistory(FilterAdapter<String> adapter) {
SharedPreferences.Editor editor = mPreferences.edit();
editor.clear();
int historyCount = mUrlHistory.size();
Log.d(TAG, "Url count: " + historyCount);
editor.putInt("URL_COUNT", historyCount);
for (int i = 0; i < historyCount; i++) {
String url = mUrlHistory.get(i);
Log.d(TAG, url);
editor.putString("URL_ITEM_" + i, url);
}
editor.commit();
}
private void clearHistory() {
mUrlHistory.clear();
mUrlFilterAdapter.clear();
Toast.makeText(this, R.string.toast_clearHistory, Toast.LENGTH_LONG)
.show();
}
}
2、FilterAdapter.java
package com.lxg.webview;
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.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
public class FilterAdapter<T> extends BaseAdapter implements Filterable {
private List<T> mOriginalValues;
private List<T> mObjects;
private final Object mLock = new Object();
private int mResource;
private int mDropDownResource;
private Context mContext = null;
private MyFilter mFilter = null;
private LayoutInflater mInflater = null;
public FilterAdapter(Context context, int textViewResourceId,
List<T> objects) {
init(context, textViewResourceId, objects);
}
private void init(Context context, int resource, List<T> objects) {
mContext = context;
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mResource = mDropDownResource = resource;
mObjects = new ArrayList<T>(objects);
mFilter = new MyFilter();
}
public void add(T object) {
if (mOriginalValues != null) {
synchronized (mLock) {
mOriginalValues.add(object);
}
} else {
mObjects.add(object);
}
}
public void insert(T object, int index) {
if (mOriginalValues != null) {
synchronized (mLock) {
mOriginalValues.add(index, object);
}
} else {
mObjects.add(index, object);
}
}
public void remove(T object) {
if (mOriginalValues != null) {
synchronized (mLock) {
mOriginalValues.remove(object);
}
} else {
mObjects.remove(object);
}
}
public void clear() {
if (mOriginalValues != null) {
synchronized (mLock) {
mOriginalValues.clear();
}
} else {
mObjects.clear();
}
}
public Context getContext() {
return mContext;
}
@Override
public int getCount() {
return mObjects.size();
}
@Override
public T getItem(int position) {
return mObjects.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mResource);
}
private View createViewFromResource(int position, View convertView,
ViewGroup parent, int resource) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(resource, parent, false);
} else {
view = convertView;
}
try {
text = (TextView) view;
} catch (ClassCastException e) {
throw new IllegalStateException(
"ArrayAdapter requires the resource ID to be a TextView", e);
}
T item = getItem(position);
if (item instanceof CharSequence) {
text.setText((CharSequence) item);
} else {
text.setText(item.toString());
}
return view;
}
@Override
public Filter getFilter() {
return mFilter;
}
public void setDropDownViewResource(int resource) {
this.mDropDownResource = resource;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent,
mDropDownResource);
}
private class MyFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
}
}
int count = mOriginalValues.size();
ArrayList<T> values = new ArrayList<T>();
for (int i = 0; i < count; i++) {
T value = mOriginalValues.get(i);
String valueText = value.toString();
if (null != valueText && null != constraint
&& valueText.contains(constraint)) {
values.add(value);
}
}
results.values = values;
results.count = values.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
3、在AndroidManifest文件里面添加权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
完整代码下载地址: http://download.csdn.net/detail/lxgwm2008/5190811