Android之AutoCompleteTextView自定义匹配规则

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




  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值