/EditTextInListView

[DEPRECATED] Demonstrate how to put EidtText widget in ListView as an item.

Primary LanguageJava

#DEPRECATED Please use RecyclerView instead.

EditTextInListView

Demonstrate how to put EidtText widget in ListView as an item.

Demo

Download

Code Instruction

com.aspsine.edittextinlistview.LineAdapter#getView()

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
	final ViewHolder holder;
	if (convertView == null) {
		holder = new ViewHolder();
		convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_line, parent, false);
		holder.etLine = (EditText) convertView.findViewById(R.id.etLine);
		convertView.setTag(holder);
	} else {
		holder = (ViewHolder) convertView.getTag();
	}

	final Line line = lines.get(position);
	
	// step 1: remove android.text.TextWatcher added in step 5 to make sure android.text.TextWatcher 
	//         don't trigger in step 2;
	// why?
	// 
	// note: When an object of a type is attached to an Editable, 
	//       TextWatcher's methods will be called when the EidtText's text is changed.
	//       
	//       EditText use a ArrayList<TextWatcher> type object to store the listener, so we must
	//       make sure there's only one TextWatcher object in this list;
	// 
	// Avoid triggering TextWatcher's method in step 2 we remove it at first time.
	// 
	if (holder.etLine.getTag() instanceof TextWatcher) {
		holder.etLine.removeTextChangedListener((TextWatcher) (holder.etLine.getTag()));
	}
	
	// step 2: set text and focus after remove android.text.TextWatcher(step 1);
	holder.etLine.setHint(position + ".");
	
	// set text
	if (TextUtils.isEmpty(line.getText())) {
		holder.etLine.setTextKeepState("");
	} else {
		holder.etLine.setTextKeepState(line.getText());
	}
	
	// set focus status
	// why?
	//
	// note: ListView has a very elegant recycle algorithm. So views in ListView is not reliable.
	//       Especially in this case, EditText is an item of ListView. Software input window may cause
	//       ListView relayout leading adapter's getView() invoke many times.
	//       Above all if we change EditText's focus state directly in EditText level(not in Adapter). 
	//       The focus state may be messed up when the particularly view reused in other position. 
	//       
	//       So using data source control View's state is the core to deal with this problem.	
	if (line.isFocus()) {
		if (!holder.etLine.isFocused()) {
			holder.etLine.requestFocus();
		}
		CharSequence text = line.getText();
		// reset cursor.
		holder.etLine.setSelection(TextUtils.isEmpty(text) ? 0 : text.length());
	} else {
		if (holder.etLine.isFocused()) {
			holder.etLine.clearFocus();
		}
	}
	
	// step 3: set an OnTouchListener to EditText to update focus status indicator in data source
	// why?
	// 
	// in step 2, we know we must control view state through data source. We use OnTouchListener
	// to watch the state change and update the data source when user move up fingers(ACTION_UP).
	// We don't want to consume the touch event, simply return false in method onTouch().
	holder.etLine.setOnTouchListener(new View.OnTouchListener() {
		@Override
		public boolean onTouch(View v, MotionEvent event) {
			if (event.getAction() == MotionEvent.ACTION_UP) {
				check(position);
				// make cursor blink
				if (!focus || !holder.etLine.isFocused()) {
					holder.etLine.requestFocus();
					holder.etLine.onWindowFocusChanged(true);
				}
			}
			return false;
		}
	});
	
	// step 4: set TextWatcher to EditText to listen text changes in EditText to updating the text in data source
	// why?
	// 
	// again, use data source to control view state.
	// When user edit the text in one EditText item and scroll the ListView. The particularly EditText item will be
	// reuse in adapter's getView(), this may lead text messed up in ListView.
	// How to deal with this problem?
	// Easy! We update the text in data source at the same time when user is editing. TextWatcher is the best way to
	// do this.
	final TextWatcher watcher = new SimpeTextWather() {

		@Override
		public void afterTextChanged(Editable s) {
			if (TextUtils.isEmpty(s)) {
				line.setText(null);
			} else {
				line.setText(String.valueOf(s));
			}
		}
	};
	holder.etLine.addTextChangedListener(watcher);
	
	// step 5: Set watcher as a tag of EditText.
	// so we can remove the same object which was setted to EditText in step 4;
	// Make sure only one callback is attached to EditText
	holder.etLine.setTag(watcher);

	return convertView;
}

/**
 * change focus status in data source
 */
private void check(int position) {
	for (Line l : lines) {
		l.setFocus(false);
	}
	lines.get(position).setFocus(true);
}

static class ViewHolder {
	EditText etLine;
}

ScreenShot

Cover Cover Cover Cover

License

Copyright 2015 Aspsine. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.