ListView的子项中包含EditText,由于复用引起的Editext中的内容显示异常的解决方案


        这是小生的第一篇博文。萌发写博客的念头并不是一时的脑热,早在上大学的时候就有写写博客的想法,由于那时没有一点经历,这想法也就胎死腹中。算下来,真正开始工作也已经有半年了,在网上看到各种达人的文章,受益匪浅,于是,决定自己也开始写博客。不求太多回报,只希望能将自己学到的东西与大家一起分享下,如果,自己的某个想法能帮助到别人,那真是我莫大的幸福了。好了,废话到此,开正文。

        最近做的一个项目需要在ListView的子项中添加一个EditText组件,不必说,为了解决ListView的复用问题又要请Map同学帮忙了,平时遇到的TextView,ImageView,CheckBox等等这些组件只用在Map中记录下它们的状态或者内容就可以轻松解决复用问题,但EditText却另当别论,首先,EditText需要实时保存其中输入的内容,并且在它再次出现的时候其上应有的内容需要被正确复现;再者,Editext与别的组件另外不同的一点是,它还存在焦点的问题,由于复用的存在,焦点可能会同时落在多个EditText上!是一个很有意思的问题。为了保存EditText上输入的数据,需要在其上注册OnTextChangedListener监听事件,并在afterTextChanged()中写入存储操作,当EditText再次被显示出来时,在getView()中为EditText加上之前存储好的值,第一个问题感觉已经完美的解决了。但事情远没有那么简单,由于复用产生的焦点问题,你在操作这个EditText时,下面看不见的地方的EditText的监听也在偷偷运行的,等你输入完了再一滚动,就乱七八糟了。所以,必须解决焦点问题。我的方案是这样的:在Adapter中写两个内部类,它们分别继承于TextWatcher和OnFocusChangeListener,在EditText上分别注册上OnFocusChangeListener和OnTextChangedListener监听,通过OnFocusChangeListener监听事件,动态的改变TextWatcher,使它始终只作用在真正获得焦点的EditText上,这是大概思路。上代码了,只有Adapter部分。


public class ImproveDetailInputAdapter extends BaseAdapter {

	private Holder holder;
	private Context context;
	private LayoutInflater inflater;
	// 用来存放输入的数据
	public HashMap inputContainer;
	// 这是我继承写的两个监听,没用匿名内部类,首先是为了方便对position这个变量进行操作,再者
	// Adapter中我只分别实例化了一个监听对象,这样对内存开支应该会小些,好吧,我是对Java的垃圾回收机制信不过。
	private MyFoucus myFoucus;
	private MyWatch myWatch;

	public HashMap getInputContainer() {
		return inputContainer;
	}

	// 构造函数
	public ImproveDetailInputAdapter(Activity context) {
		this.context = context;
		inflater = LayoutInflater.from(context);
		inputContainer = new HashMap();
		myFoucus = new MyFoucus();
		myWatch = new MyWatch();
	}

	@Override
	public int getCount() {

		return 20;
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return null;
	}

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

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if (null == convertView) {

			holder = new Holder();
			convertView = inflater.inflate(
					R.layout.yhl_adapter_detail_input_adapter, null);
			holder.brand = (TextView) convertView
					.findViewById(R.id.detail_brand);
			holder.station = (TextView) convertView
					.findViewById(R.id.detail_station);
			holder.stockNum = (TextView) convertView
					.findViewById(R.id.detail_stock_num);
			holder.type = (TextView) convertView.findViewById(R.id.detail_type);
			holder.unit = (TextView) convertView.findViewById(R.id.detail_unit);
			// 上面的部分完全可以忽略,直接看这里
			holder.input = (EditText) convertView
					.findViewById(R.id.detail_input_num);
			// 注册上自己写的焦点监听
			holder.input.setOnFocusChangeListener(myFoucus);

			convertView.setTag(holder);
		} else {
			holder = (Holder) convertView.getTag();
		}
		// setTag是个好东西呀,把position放上去,一会用
		holder.input.setTag(position);

		View currentFocus = ((Activity) context).getCurrentFocus();
		if (currentFocus != null) {
			currentFocus.clearFocus();
		}

		// 为了实现最小的内存开销,复用两个不同的监听对象,通过每次点击的事件来修正mywatch中的position;s

		// 使用remove和add来区别开复用修正和手动添加;之所以费劲的加个remove又加个add也是为了能尽量减少些
		// 思考量,剔除修正EditText时的TextChange监听事件,整个世界都清净了。。。
		holder.input.removeTextChangedListener(myWatch);
		if (inputContainer.containsKey(position)) {
			holder.input.setText(inputContainer.get(position).toString());
		} else {
			holder.input.setText("");
		}
		holder.input.addTextChangedListener(myWatch);
		return convertView;
	}

	class MyFoucus implements OnFocusChangeListener {
		// 当获取焦点时修正myWatch中的position值,这是最重要的一步!
		@Override
		public void onFocusChange(View v, boolean hasFocus) {
			if (hasFocus) {
				int position = (int) v.getTag();
				myWatch.position = position;
			}
		}
	}

	class MyWatch implements TextWatcher {
		// 不得不吐槽一下这里,java的内部类机制怎么就和我想的不一样呢,外部依然能很轻松的访问这个“私有
		// 化的”position,我是不是该去看看《think in java》了。
		private int position;

		@Override
		public void afterTextChanged(Editable s) {
			inputContainer.put(position, s.toString());
		}

		@Override
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
			// TODO Auto-generated method stub

		}

	}

	class Holder {
		TextView brand;
		TextView station;
		TextView stockNum;
		TextView type;
		TextView unit;
		EditText input;
	}

}



之前还有过几个思路,也都实现出来了,觉着就这个实现起来最简洁,思路也最顺畅。

希望对大家有帮助,欢迎来丢砖。




你可能感兴趣的:(安卓开发)