移植Android4.0中的AutoCompleteTextView自动完成文本框控件用于低版本的系统

本文确实没什么技术含量,毕竟我也是一个菜鸟,仅仅是为了新手朋友不要再重蹈我当年的覆辙,多走弯路而已!


本文是在上一篇的基础上实现的的一个自动完成文本框的DEMO


老规矩,先上效果图

移植Android4.0中的AutoCompleteTextView自动完成文本框控件用于低版本的系统_第1张图片



移植Android4.0中的AutoCompleteTextView自动完成文本框控件用于低版本的系统_第2张图片


移植Android4.0中的AutoCompleteTextView自动完成文本框控件用于低版本的系统_第3张图片



做这个小DEMO的主要注意的地方有两处,一是万万不可给popupwindow设置得到焦点,即setFocusable(true),不然他会抢占EditText的焦点,使得EditText得不到输入,

二是不能给popupwindow中的listView绑定OnItemClickListener监听器,此时点击是有选中的效果,即背景色发生了切换,但是监听器的方法没有触发。


那怎么给listView绑定监听器呢?


这时候就需要换个思路了,既然不能给popupwindow中的listView绑定通用监听器,那就挨个给其上的view绑定OnClickListener监听器,我在这里直接给ListView的每一个item绑定上了单击监听器,在监听器的回调方法中取出其中的TextView的内容进行相应的操作。这个时候又出现了一个新的问题,怎么区分点击的是哪一个item呢?所以必须要想一个办法给onClick(View v)中的v中传递参数,然后就想到了holder类,holder类是我们在adapter中保存item视图的,用于实现资源复用的,并且我们用到了他的settag方法,既然可以既可以在holder中动脑筋了,最简单的方法就是在holder声明一个成员变量保存position值。 并且onClick(View v)中的v就是adapter中的一个item对象,其中我们就可以直接通过gettag()方法取出holder,这样我们也得到了position值。至此主要的逻辑也大体清楚了。


接下啦就开始码代码吧。


一些selector和背景贴图我就不贴了,大体上和上一篇下拉列表是一样的


接下来是新建一个工程,。

主要思路,首先继承EditText控件,并绑定一个文本变换监听器,通过该监听器回调通知进行数据变更,然后更新UI


首先新建一个MyAutoCompleteTextView类继承自EditText类。

代码我就直接贴出来了


package com.example.myautocompletetextview;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;

public class MyAutoCompleteTextView extends EditText{

	private Context mContext = null;
	private LayoutInflater mInflater = null;
	
	private AutoCompleteTextViewDropDownItem dropDown = null;
	
	private DataSet mDataSet = null;
	
	/** 自动完成文本框开始搜索的阈值,即输入的数量超过了这个阈值就开始搜索张贴栏,默认为1 **/
	private int beginSearchThreshold = 1;
		
	/** 状态标志量,true表示此次EditText的变化是由选中了下拉列表中的数据导致,结束此次监听事件;否则是由用户输入导致 **/
	private boolean finishMark = false;
	
	private OnItemSelectedListener mOnItemSelectedListener = null;
	/** 使用该控件的类务必实现该方法,否则无法刷新数据 **/
	private MyAutoCompleteTextViewHelperListener mHelpListener = null;
	
	public MyAutoCompleteTextView(Context context) {
		this(context, null);
	}
	
	public MyAutoCompleteTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	
	public MyAutoCompleteTextView(Context context, AttributeSet attrs,int defStyle) {
		super(context, attrs, defStyle);
		init(context);
	}
	
	private void init( Context mContext ){
		this.mContext = mContext;
		this.mInflater = LayoutInflater.from( this.mContext );
		
		/**为了避免空指针异常,提前初始化数据集 **/
		mDataSet = new DataSet();
		
		/** 绑定可编辑文本框的字符变化监听器 **/
		addTextChangedListener( new EditTextInputTextWatcher() );
		
	}
	
	
	/**
	 * 设置自动完成文本框的选择监听器,用于回调,务必在显示下拉列表前载入
	 * @param listener
	 */
	public void setOnItemSelectedListener( OnItemSelectedListener mOnItemSelectedListener ){
		this.mOnItemSelectedListener = mOnItemSelectedListener;
	}
	
	/**
	 *	设置自动完成文本框的助手类监听器,用于调用封装自动填补列表的数据
	 * @param listener
	 */
	public void setAutoCompleteTextViewHelpListener( MyAutoCompleteTextViewHelperListener mHelpListener ){
		this.mHelpListener = mHelpListener;
	}
	
	/**
	 *	设置刷新阈值
	 * @param searchThreshold
	 */
	public void setSearchThreshold( int searchThreshold ){
		beginSearchThreshold = searchThreshold;
	}
	
	/**
	 * 可编辑文本框的输入动态监听器,其中的方法是依次调用
	 *
	 */
	private class EditTextInputTextWatcher implements TextWatcher{

		public void beforeTextChanged(CharSequence s, int start, int count, int after) {  }

		public void onTextChanged(CharSequence s, int start, int before, int count) {
			if( finishMark ){
				finishMark = !finishMark;
				return;
			}
			if( mHelpListener != null ){
				mHelpListener.resetMarkNum();
			}
			if( dropDown == null ){
				dropDown = new AutoCompleteTextViewDropDownItem(mContext);
			}
			if(s.length() >= beginSearchThreshold){
				if( mHelpListener != null ){
					mDataSet = mHelpListener.refreshPostData( s.toString() );
					if(dropDown != null){
						dropDown.notifyDataSetChanged();
					}
				}
				if(!dropDown.isShowing()){
					if( mDataSet.size() == 0 ){
						return;
					}
					dropDown.showAsDropDown(MyAutoCompleteTextView.this);
				} else {
					//关闭popupwindow
					if(mDataSet.size() == 0){
						dropDown.dismiss();
					}
				}
			} else {
				if(dropDown.isShowing()){
					dropDown.dismiss();
				}
			}
		}
		
		public void afterTextChanged(Editable s) {  }
		
	}
	
	/**
	 *	在弹出式窗口结束后修改了EditText的值
	 *	此时要重置一遍该编辑框中的游标
	 */
	public void setInputCursor(){
		setSelection(getText().toString().length());
	}
	
	private class AutoCompleteTextViewDropDownItem extends PopupWindow{
		
		private ListView listView = null;
		private AutoCompleteTextViewDropDownItemAdapter adapter = null;
		
		private CustomAutoCompleteTextViewItemOnClickListener mOnClickListener = new CustomAutoCompleteTextViewItemOnClickListener();

		public AutoCompleteTextViewDropDownItem( Context context ){
			super(context);
			
			adapter = new AutoCompleteTextViewDropDownItemAdapter();
			
			View view = mInflater.inflate(R.layout.custom_auto_complete_text_view, null);
			listView = (ListView)view.findViewById(R.id.my_auto_complete_text_view_list);
			listView.setAdapter(adapter);
			
			setWidth(MyAutoCompleteTextView.this.getWidth());
			setHeight(LayoutParams.WRAP_CONTENT);
			
			/** 得到焦点,这句话务必注释掉,否则会阻塞可编辑文本框的输入 **/
//			setFocusable(true);
			setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
			setOutsideTouchable(true);	//点击布局外失去焦点
			setFocusableInTouchMode(true);
			
			setContentView(view);
			
		}
		
		public void showAsDropDown(View view) {
			showAsDropDown(view, 0, 0);	// 保证尺寸是根据屏幕像素密度来的
			update();		//刷新
		}

		public void notifyDataSetChanged(){
			adapter.notifyDataSetChanged();
		}
		
		
		/**
		 *	内部类,自动完成文本框的下拉列表的适配器
		 *
		 */
		private class AutoCompleteTextViewDropDownItemAdapter extends BaseAdapter{
			
			public int getCount() {
				return mDataSet.size();
			}

			public Object getItem(int position) {
				return mDataSet.getIndexData(position);
			}

			public long getItemId(int position) {
				return position;
			}

			public View getView(int position, View convertView, ViewGroup parent) {
				AutoCompleteTextViewDropDownItemHolder holder = null;
				if(convertView == null){
					holder = new AutoCompleteTextViewDropDownItemHolder();
					convertView = mInflater.inflate(R.layout.my_auto_complete_text_view_item, null);
					holder.position = position;
					holder.txt = (TextView)convertView.findViewById(R.id.my_auto_complete_text_view_item_text);
					convertView.setTag(holder);
				} else {
					holder = (AutoCompleteTextViewDropDownItemHolder)convertView.getTag();
				}
				
				convertView.setOnClickListener( mOnClickListener );
				
				
				holder.txt.setText( mDataSet.getIndexData(position) );
				
				return convertView;
			}
			
		}
		
		private final class AutoCompleteTextViewDropDownItemHolder {
			
			public int position;
			public TextView txt;
		}
		
		
		private class CustomAutoCompleteTextViewItemOnClickListener implements OnClickListener{

			@Override
			public void onClick(View v) {
				/** 置标记变量为true,表示接下来给可编辑文本框的赋值是我们控制的,不是用户输入的,跳过此次监听事件 **/
				finishMark = true;
				TextView mTextView = (TextView) v.findViewById(R.id.my_auto_complete_text_view_item_text);
				String content = mTextView.getText().toString();
				MyAutoCompleteTextView.this.setText(content);
				setInputCursor();
				if( mOnItemSelectedListener != null ){
					AutoCompleteTextViewDropDownItemHolder holder = ( AutoCompleteTextViewDropDownItemHolder )v.getTag();
					/** 注意第一个变量我用了null,所以在Activity中不可取值,这点要切记,尽管基本用不到它 **/
					mOnItemSelectedListener.onItemSelected(null, holder.txt, holder.position, holder.position);
				}

然后我定义了一个回调类,用于实现数据刷新


package com.example.myautocompletetextview;

/**
 *	自动完成文本框的助手监听器
 *
 */
public interface MyAutoCompleteTextViewHelperListener {
	
	
	/** 搜索匹配 **/
	public abstract DataSet refreshPostData( String key );
	
	/**
	 *	设置重新在下拉提示文本框选择的标志量
	 *	这里为什么设置这么一个方法,是有原因的
	 *	这个方法一般在下拉列表的数据发生了变化重新填装但是用户还没有选择的阶段
	 *	通知Activity方面修改标志量,我这里是重置了选中项的position,即置为-1,防止用户没有选择,而导致position保存的是上一次选中的位置
	 *	造成一些错误,其实就是相当于了OnItemSelectedListener.onNothingSelected(parent);方法,只不过当时图省事就这么处理了
	 *	当然大家也可以自己在MyAutoCompleteTextView类里面监听,即在里面设置标志量,当dismiss的时候如果标志量为-1,表明用户未选择,即可调用
	 *	OnItemSelectedListener.onNothingSelected(parent);方法来通知即可
	 */
	public abstract void resetMarkNum();
}

最后就是Activity类


package com.example.myautocompletetextview;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;

public class MainActivity extends Activity {

	private MyAutoCompleteTextView mTest = null;
	
	/** 静态数据集 **/
	private DataSet mDataSet = null;
	
	private DataSet mMyAutoCompleteTextViewDataSet = null;
	
	private int num = -1;		//整数标记变量,标记是第几个item被选中
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mTest = (MyAutoCompleteTextView)findViewById(R.id.test);
		
		mTest.setOnItemSelectedListener( new InputOnItemSelectedListener() );
		mTest.setAutoCompleteTextViewHelpListener( new InputMyAutoCompleteTextViewHelperListener () );
		
		mDataSet = new DataSet();
		mDataSet.addData("sunyanzi-hey jude");
		mDataSet.addData("sunyanzi-the moment");
		mDataSet.addData("sunyanzi-tonight I feel close to you");
		mDataSet.addData("sunyanzi-leave me alone");
		mDataSet.addData("liangjingru");
		mDataSet.addData("liangjingru-xiaoshouladashou");
		
		mMyAutoCompleteTextViewDataSet = new DataSet();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	
	private void packDataSet( String key ){
		mMyAutoCompleteTextViewDataSet.clear();
		
		for( int i = 0; i < mDataSet.size(); i++ ){
			if( mDataSet.getIndexData(i).startsWith(key) ){
				mMyAutoCompleteTextViewDataSet.addData( mDataSet.getIndexData(i) );
			}
		}
	}
	
	private class InputOnItemSelectedListener implements OnItemSelectedListener{

		public void onItemSelected(AdapterView parent, View view, int position, long id) {
			num = position;
		}

		public void onNothingSelected(AdapterView parent) {  }
		
	}
	
	private class InputMyAutoCompleteTextViewHelperListener implements MyAutoCompleteTextViewHelperListener{

		@Override
		public DataSet refreshPostData(String key) {
			packDataSet(key);
			return mMyAutoCompleteTextViewDataSet;
		}

		@Override
		public void resetMarkNum() {
			num = -1;
		}
		
	}

}


数据类大家自行封装即可

好了,有疑问的朋友请在下面留言。

工程文件下载请点这里!



你可能感兴趣的:(移植,android4.0,控件,自动完成文本框,移植)