In my Appliaction I use a CursorAdapter. This displays a list, which is part of a RelativeLayout. This List includes several elements(TextView, Button, EditText). The EditText does not normally appear on the screen.The problem: I import any data to a EditText and scroll the screen. In this moment I see the imported data in another EditText. OR:
Another case. There are 3 EditText. I use the virtual Keyboard (For example use the second editText). I get in the data to the Edit Text. Push the Back Button. And the data goes to the Edit Text below. (So the data go to the third Edit Text)
Here is the CursorAdapter Code:
class OOSListadapter extends CursorAdapter{
OOSListadapter(Cursor c){
super(OOS.this,c);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
OOSRow newRow = (OOSRow)view.getTag();
newRow.populateRow(cursor);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater=getLayoutInflater();
View row=inflater.inflate(R.layout.oos_row, parent, false);
OOSRow newRow = new OOSRow(row);
row.setTag(newRow);
return (row);
}
}
And here is one row from my application list.
class OOSRow {
private TextView row_Action = null;;
private TextView row_Must = null;;
private TextView row_Lack = null;;
private TextView row_itemName = null;;
private EditText row_price = null;;
private Button row_detail = null;
private View row = null;
OOSRow (View row){
this.row = row;
row_Action = (TextView)row.findViewById(R.id.oos_row_SignalA);
row_Must = (TextView)row.findViewById(R.id.oos_row_SignalK);
row_Lack = (TextView)row.findViewById(R.id.oos_row_SignalO);
row_itemName = (TextView)row.findViewById(R.id.oos_row_itemLabel);
row_price = (EditText)row.findViewById(R.id.oos_row_EditText);
row_detail = (Button)row.findViewById(R.id.oos_row_detailButton);
}
void populateRow (Cursor c){
Cursor specCursor = dbLoc.Query("SELECT PRICE, LACK, ORDERED FROM ORDERED WHERE ITEMID='"+ c.getString(1) +"'", null);
specCursor.moveToFirst();
row_itemName.setText(c.getString(2));
row_itemName.setContentDescription(c.getString(1));
if (specCursor.getString(1).toString().equals("Y")){
row_itemName.setBackgroundColor(Color.parseColor("#FF0000"));
row_itemName.setTextColor(Color.parseColor("#FFFFFF"));
}else{
row_itemName.setBackgroundColor(Color.parseColor("#000000"));
row_itemName.setTextColor(Color.parseColor("#FFFFFF"));
}
row_itemName.setOnClickListener(SelectedLackItem);
if (c.getString(5).toString().equals("I")){
row_Action.setTextColor(Color.parseColor("#000000"));
row_Action.setBackgroundColor(Color.parseColor("#FF0000"));
}
else{
row_Action.setTextColor(Color.parseColor("#FFFFFF"));
row_Action.setBackgroundColor(Color.parseColor("#000000"));
}
if (c.getString(4).toString().equals("I")){
row_Must.setTextColor(Color.parseColor("#000000"));
row_Must.setBackgroundColor(Color.parseColor("#00FF00"));
}
else{
row_Must.setTextColor(Color.parseColor("#FFFFFF"));
row_Must.setBackgroundColor(Color.parseColor("#000000"));
}
specCursor = dbLoc.Query("SELECT LACK FROM LASTORDERED WHERE ITEMID='"+c.getString(1)+"' AND COMPANYID ='"+dbLoc.GetCompanyId()+"'", null);
if (specCursor.moveToFirst())
{
if (specCursor.getString(0).toString().equals("I")){
row_Lack.setTextColor(Color.parseColor("#000000"));
row_Lack.setBackgroundColor(Color.parseColor("#0000FF"));
}else{
row_Lack.setTextColor(Color.parseColor("#FFFFFF"));
row_Lack.setBackgroundColor(Color.parseColor("#000000"));
}
}
row_detail.setOnClickListener(OpenDetailScreenButton);
row_detail.setContentDescription(c.getString(1));
row_price.setContentDescription(c.getString(1));
row_price.setInputType(0);
/*row_price.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
row_price.setInputType(InputType.TYPE_CLASS_NUMBER);
}
#Override
public void afterTextChanged(Editable s) {
}
});*/
specCursor.close();
specCursor = null;
}
}
And some pictures:
After Back Button:
Any Idea?
I had a very similar problem. The following solution helped me:
<activity android:name= ".yourActivity" android:windowSoftInputMode="adjustPan"/>
Related
I have an Expandable List that represents settings in my program. The group view has a TextView description with a Button that represents a reset. The child view has a TextView description with an EditText that represents an individual setting.
The values of the EditText are saved in external variables and updated upon any change. Upon click of the reset Button in the group view, I want to reset all individual settings contained in the EditTexts to default values. This works but of course the EditTexts views are not updated until the child views are redrawn. I want the views to all change immediately upon click of the reset button.
I have tried a myriad of things but keep running into issues. I can't seem to access the EditText views within the reset Button listener to programmatically set the text.
My Expandable List (abridged):
public class ExpandableListAdapter extends BaseExpandableListAdapter
{
private String[] listGroups;
private String[][] listChilds;
private Activity context;
final EditText[] etIndividualLetterValues = new EditText[26];
public ExpandableListAdapter(Activity context, String[] listGroups, String[][] listChilds)
{
this.context = context;
this.listGroups = listGroups;
this.listChilds = listChilds;
#Override
public View getChildView(final int listGroupPosition, final int listChildPosition, boolean isLastChild,
View view, ViewGroup viewGroup)
{
LayoutInflater inflater = context.getLayoutInflater();
TextView tvListItemGeneral;
tvListItemGeneral = (TextView) view.findViewById(R.id.TV_list_item_general);
tvListItemGeneral.setText(getChild(listGroupPosition, listChildPosition).toString());
etIndividualLetterValues[listChildPosition] = (EditText) view.findViewById(R.id.ET_list_item_settings_value);
etIndividualLetterValues[listChildPosition].setText(String.format("%d", Settings.settingsCurrent[listGroupPosition][listChildPosition]));
etIndividualLetterValues[listChildPosition].addTextChangedListener(new TextWatcher()
{
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
Settings.settingsCurrent[listGroupPosition][listChildPosition] = Integer.parseInt(s.toString());
}
#Override
public void afterTextChanged(Editable s)
{
}
});
}
#Override
public View getGroupView(final int listGroupPosition, boolean isExpanded, View view, ViewGroup viewGroup)
{
LayoutInflater layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(view == null)
{
view = layoutInflater.inflate(R.layout.list_group, viewGroup, false);
}
TextView tvListGroup = (TextView) view.findViewById(R.id.TV_list_group);
Button buttonSettingsReset = (Button) view.findViewById(R.id.B_settings_reset);
buttonSettingsReset.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if(listGroupPosition == C.INDIVIDUAL_LETTER_VALUES)
{
for(int i=0; i<26; i++)
{
etIndividualLetterValues[i].setText(String.format("%d", Settings.settingsDefault[C.INDIVIDUAL_LETTER_VALUES][i]));
}
}
}
});
tvListGroup.setText(getGroup(listGroupPosition).toString());
return view;
}
The line of code:
etIndividualLetterValues[i].setText(String.format("%d", Settings.settingsDefault[C.INDIVIDUAL_LETTER_VALUES][i]));
gives a NPE.
OK after much searching (and rephrasing my searches!) I found out about the method notifyDataSetChanged();
So all I did was add a method to the Button click listener that called a method to reset settings and then called notifyDataSetChanged():
buttonSettingsReset.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if(listGroupPosition == C.INDIVIDUAL_LETTER_VALUES)
{
refresh(listGroupPosition);
}
}
});
//...
public void refresh(int settingsID)
{
resetSettingsClick(context, settingsID);
this.notifyDataSetChanged();
}
I have a problem with EditText-fields in a listview. After i scroll some settings seem to be reset (selectAllOnFocus) and the selection cursor goes bananas.
I have a listview with a custom ArrayAdapter and a custom dataobject. In this case the object only holds one String (to simplify it).
My Activity
// adapter + content
List<ListviewObject> listViewContent = new ArrayList<ListviewObject>();
for(int i = 0; i < 50; i++) {
listViewContent.add(new ListviewObject("num: " + i));
}
adapter = new CustomListAdapter(AddNewPerson.this, R.layout.list_item, listViewContent);
// list
ListView mListView = (ListView)findViewById(R.id.sample_list);
mListView.setItemsCanFocus(true);
mListView.setAdapter(adapter);
My Adapter
#Override
public View getView(int position, View convertView, ViewGroup parent) {
HolderObject holder = null;
if(convertView == null) {
convertView = mLayoutInflater.inflate(layoutResourceId, parent, false);
holder = new HolderObject();
holder.name = (TextView) convertView.findViewById(R.id.txt);
convertView.setTag(holder);
} else {
holder = (HolderObject) convertView.getTag();
}
holder.lvObject = items.get(position);
setNameTextChangeListener(holder);
// Retrieve the correct value
holder.name.setText(holder.lvObject.getName());
return convertView;
}
public static class HolderObject {
ListviewObject lvObject;
TextView name;
}
private void setNameTextChangeListener(final HolderObject holder) {
holder.name.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Update the value
holder.lvObject.setName(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
#Override
public void afterTextChanged(Editable s) { }
});
}
To fix all the focusproblems I found in other threads I've set:
.setItemsCanFocus(true) on the listview
android:descendantFocusability="beforeDescendants" in the activity XML
android:windowSoftInputMode="adjustPan" in the manifest XML
Focussing and editing text works fine. When I scroll the correct values are held and all this seems to work fine.
Before I scroll and I click on some of the EditTexts this happens. (Last focused blurs, clicked one focuses, content is selected)
http://imgur.com/eeIKhCv
After I scroll down and up again, and do the same clicks as before, this happens.
http://imgur.com/75mjPc3
This is due to listView recycling mechanism. To know more about listview recycling mechanism you can refer this link
in your case you avoid the problem by storing a last focused editText and In getView set focus to only last stored integer and skip other position. hope this help you...
It's quite easy:
Declare a String[] to keep track each EditText's input inside the afterTextChanged() of "addTextChangedListener().
Becareful of the order:
viewHolder.editText.removeTextChangedListener(viewHolder.textWatcher);
viewHolder.textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
mInputs[position] = editable.toString(); //record input
viewHolder.editText.setSelection(viewHolder.editText.getText().length()); //set cursor to the end
}
};
viewHolder.editText.addTextChangedListener(viewHolder.textWatcher);
viewHolder.editText.setText(mInputs[position]);
Add android:windowSoftInputMode="adjustPan" to your Activity in AndroidManifest file.
Good luck!
In the application, I have a customized adapter which extends BaseAdapter. The adapter includes a header of two items, one of which is a search bar.
What I'm trying to do is to input text on the bar to search content on the listview, using my adapter. The challenge here is for Samsung Galaxy Note II, when I enter a character, the search bar shows what it is. However, for the second character I want to input, the bar shows nothing and it seems the previous character was cleared too. For any other devices, this works well. I have no idea of this. Can any guru knows the solution?
Here follows some snippet of ResearveHeaderAdapter:
#Override
public View getView(final int position, final View convertView, final ViewGroup parent)
{
View rowView = null;
HeaderItem header = mHeaderDef.get(position);
if (header != null)
{
switch(header)
{
case REFRESHBAR:
{
rowView = updateRefreshRow(convertView, parent);
break;
}
case SEARCHBAR:
{
rowView = updateSearchRow(convertView, parent);
break;
}
default:
{
rowView = super.getView(position, convertView, parent);
break;
}
}
}
else
{
rowView = super.getView(position, convertView, parent);
}
return rowView;
}
private View updateSearchRow(final View convertView, final ViewGroup parent)
{
mCacheSearchView = mAllContentInflator.inflate( R.layout.search_bar_item, parent, false );
mEditText = (ClearableEditText) mCacheSearchView.findViewById(R.id.topSearchBar);
mEditText.setText(getSearchVarString());
mEditText.setOnKeyListener(getSearchListener());
mEditText.addTextChangedListener(getSearchTextWatcher());
String search = getSearchVarString();
String stxt = mEditText.getText().toString();
if (getSearchVarString().trim().length() > 0)
{
mEditText.requestFocus();
}
mEditText.setEnabled(true);
return mCacheSearchView;
}
Its subclass AllContentAdapter:
public class AllContentAdapter extends ReserveHeaderAdapter
{
public AllContentAdapter(Activity callerContext,
MediaObjectManager manager, boolean sensitive,
MediaPolicy displayPolicy) {
super(callerContext, manager, sensitive, displayPolicy);
// TODO Auto-generated constructor stub
super.appendHeaderItem(HeaderItem.REFRESHBAR);
super.appendHeaderItem(HeaderItem.SEARCHBAR);
}
private static OnKeyListener mOnKeySearchListener = null;
private static TextWatcher mSearchTextWatcher = null;
private static OnClickListener mOnClickRefreshListener = null;
private static String mSearchString = "";
#Override
protected String getSearchVarString()
{
return mSearchString;
}
#Override
protected void setSearchVarString(String searchString)
{
mSearchString = searchString;
}
#Override
protected OnKeyListener getSearchListener()
{
return mOnKeySearchListener;
}
#Override
protected TextWatcher getSearchTextWatcher()
{
return mSearchTextWatcher;
}
#Override
protected OnClickListener getClickRefreshListener()
{
return mOnClickRefreshListener;
}
public void setOnClickRefreshListener(OnClickListener listener)
{
mOnClickRefreshListener = listener;
}
public void setSearchOnKeyListener(OnKeyListener listener)
{
mOnKeySearchListener = listener;
}
public void addTextChangedListenerForSearch(TextWatcher listener)
{
mSearchTextWatcher = listener;
}
So for the desired Activity, I called the adapter.addTextChangedListenerForSearch(new TextWatcher()) which is like this:
((AllContentAdapter) mAdapter).addTextChangedListenerForSearch(new TextWatcher()
{
public void afterTextChanged(Editable s)
{
String stxt = s.toString();
((AllContentAdapter) mAdapter).setSearchVarString(stxt);
// some handle code omitted...
AllContentScreen.this.loadSearchFilter();// do some filter to search desired content
}
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
});
The real result is that on Note II, when entering the second character, (Editable)s catches noting but empty string. Why?
That's strange. I'm willing to bet that the Note uses a special InputConnection. http://developer.android.com/reference/android/view/inputmethod/BaseInputConnection.html
which produces custom Editables. You may want to set a custom Editable.Factory on your search EditText. Also, look at setting a SpanWatcher to monitor span activity, especially the composing spans. Here's an example from a RichEditText library I created.
// Replaces the default edit text factory that produces editables
// which our custom editable and listener
public class TextFactory extends Editable.Factory{
#Override
public Editable newEditable(final CharSequence source){
return new RichTextStringBuilder(source,
mSpanWatcher);
}
}
At the least, this will let you see a better picture of what's happening.
From debugging process, I probably found what problem was for this special device and solved this issue therefore. That is, when EditText.getText().getString() is not empty, we'll show the searchbar and setSelection(position). The 'position' here represents the searchbar. For any other devices, this works fine. For Note II, it seems that if we want setSelection for the position of EditText, this function will clear the entered text and return empty string. So the solution is to avoid this function by using a flag to see whether the EditText has empty string or not and do catch what is entered in the search bar.
Still new to android and even more to custom cursor adapter so I'm having trouble understanding how to prevent my listview from recycling views to prevent input from one edittext to show up in another when scrolled. I've seen on other post saying to change the name of convertview but how to do that I'm drawing a blank. I was hoping someone here would be able to give more details or example of how to do based of what code I've wrote so far.
public class editview extends ListActivity {
private dbadapter mydbhelper;
private PopupWindow pw;
public static int editCount;
public static ListView listView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mydbhelper = new dbadapter(this);
mydbhelper.open();
View footer = getLayoutInflater().inflate(R.layout.footer_layout, null);
ListView listView = getListView();
listView.addFooterView(footer);
showResults();
}
//Populate view
private void showResults (){
Cursor cursor = mydbhelper.getUserWord();
startManagingCursor(cursor);
String[] from = new String[] {dbadapter.KEY_USERWORD};
int[] to = new int[] {R.id.textType};
ItemAdapter adapter = new ItemAdapter(this, R.layout.edit_row, cursor,
from, to);
adapter.notifyDataSetChanged();
this.setListAdapter(adapter);
editCount = adapter.getCount();
}
//footer button
public void onClick(View footer){
final MediaPlayer editClickSound = MediaPlayer.create(this, R.raw.button50);
editClickSound.start();
startActivity(new Intent("wanted.pro.madlibs.OUTPUT"));
}
//custom cursor adapter
class ItemAdapter extends SimpleCursorAdapter {
private LayoutInflater mInflater;
private Cursor cursor;
public ItemAdapter(Context context, int layout, Cursor cursor, String[] from,
int[] to) {
super(context, layout, cursor, from, to);
this.cursor = cursor;
mInflater = LayoutInflater.from(context);
}
static class ViewHolder {
protected TextView text;
protected EditText edittext;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.edit_row, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.textType);
holder.edittext = (EditText) convertView.findViewById(R.id.editText);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
cursor.moveToPosition(position);
int label_index = cursor.getColumnIndex("userword");
String label = cursor.getString(label_index);
holder.text.setText(label);
return convertView;
}
}
Changed it to
class ItemAdapter extends SimpleCursorAdapter {
private LayoutInflater mInflater;
private Cursor cursor;
Map<Integer, String> inputValues = new HashMap<Integer, String>();
public View getView(final int position, View convertView, ViewGroup parent) {
....
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.edit_row, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.textType);
holder.edittext = (EditText) convertView.findViewById(R.id.editText);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
cursor.moveToPosition(position);
int label_index = cursor.getColumnIndex("userword");
String label = cursor.getString(label_index);
holder.text.setText(label);
String oldText = inputValues.get(position);
holder.edittext.setText(oldText == null ? "" : oldText);
holder.edittext.addTextChangedListener(new TextWatcher(){
public void afterTextChanged(Editable editable) {
inputValues.put(position, editable.toString());
}
but it is recycling after all edittext have data. Tried using holder.edittext.setText(oldText) but same effect.
First of all, you really don't want to prevent a list view from recycling its views. View recycling is a huge optimization. For a lot of really good info on lists, see the google IO talk: http://www.youtube.com/watch?v=wDBM6wVEO70
That being said, you've correctly identified your problem: You have far fewer EditTexts than you do items in your list. As the you scroll through the list those EditTexts are recycled so you see the same input over and over again.
Basically what you need to do is save the input for your EditTexts in some datastructure (a HashMap if they will only edit a few values, maybe a List if they will be changing most of the values, either would work) that maps the position to the input. You can do this by adding a textChangedListener to your edit texts in getView:
#Override
public View getView(final int position, View convertView, ViewGroup parent){
...
cursor.moveToPosition(position);
int label_index = cursor.getColumnIndex("userword");
String label = cursor.getString(label_index);
holder.text.setText(label);
//clear whatever text was there from some other position
//and set it to whatever text the user edited for the current
//position if available
String oldText = yourMapOfPositionsToValues.get(position);
holder.setText(oldText == null ? "" : oldText);
//every time the user adds/removes a character from the edit text, save
//the current value of the edit text to retrieve later
holder.edittext.addTextChangedListener(new TextWatcher(){
#Override
public void afterTextChanged(Editable editable) {
yourMapOfPositionsToValues.put(position, editable.toString());
}
....
};
return convertView;
}
Whenever your user is done editing, you can run through your datastructure and do whatever with those values.
Edit:
I changed onTextChanged to afterTextChanged because I've used that before and I know it works. Keep in mind that afterTextChanged is called every time a LETTER changes, not just after the user finishes typing a word. If the user types "dog" afterTextChanged will be called three times, first with 'd', then with 'do', then with 'dog'.
A HashMap is simple: Map yourMapOfPositionsToValues = new HashMap();
to add or update an item: yourMap.put(position, someText);
to fetch an item: yourMap.get(position);
if hashmaps don't make sense, spend some time researching them. They are an incredibly important data structure.
Your TextWatcher implementation is incorrect. Your data structure should not belong to a single view, but rather the activity or your adapter. It appears to you that positions aren't stable because your List is owned by each view. The positions themselves are stable in that unless the underlying data changes the cursor will return the same data every time for the same position. However, the edit text is used for multiple different positions.
Create a hashmap as an instance variable I demonstrated above in the constructor of your adapter. Then add exactly the TextWatcher I wrote originally, no need for a named class, anonymous is simpler. Your code should work.
The solution to this is removing the added textwatcher before setting the text. Otherwise, the previous textwatcher on that view will still be called along with the new textwatcher. Store the textwatcher as a tag on the EditText to keep track of it.
Object oldWatcher = viewHolder.quantitySold.getTag();
if(oldWatcher != null){
viewHolder.quantitySold.removeTextChangedListener((CustomTextWatcher)oldWatcher);
}
String oldText = inputValues.get("key"+position);
Log.d(TAG, "oldText: "+oldText+" position: "+position);
viewHolder.quantitySold.setText(oldText == null ? "" : oldText);
CustomTextWatcher watcher = new CustomTextWatcher(
cursor.getString(SKUFragment.COL_NAME),
cursor.getInt(SKUFragment.COL_ID),
cursor.getDouble(SKUFragment.COL_UNIT_PRICE),
position
) {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if (s != null) {
int quantity = 0;
if (!TextUtils.isEmpty(s.toString())) {
quantity = Integer.parseInt(s.toString());
inputValues.put("key"+mPosition, "" + quantity);
}else{
inputValues.put("key"+mPosition, "");
}
double value = quantity * skuPrice;
mListener.onQuantityChanged(skuName+", position: "+mPosition, skuId, quantity, value);
}
}
};
viewHolder.quantitySold.setTag(watcher);
viewHolder.quantitySold.addTextChangedListener(watcher);
Just a basic question: If I have several dozen EditText fields that are part of a ListAdapter, how can the individual EditText fields know to which row they belong?
Currently I am using TextWatcher to listen for text input. I have tried extending TextWatcher so that I can pass in the position of the EditText to TextWatcher's constructor.
However, when the soft keyboard pops up, the positions that correspond to the various EditText fields shuffle.
How can I track the EditText fields to their proper position?
I am using a GridView to lay things out. The layout of each item is an ImageView with a TextView and EditText field below it.
The text for each EditText is held in a global String array called strings. It is initially empty, and is updated by my TextWatcher class.
public void initList()
{
ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(this, R.layout.shape, strings)
{
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.shape, null);
}
final String theData = getItem(position);
final EditText editText = (EditText) convertView.findViewById(R.id.shape_edittext);
editText.setText(theData);
editText.addTextChangedListener(
new MyTextWatcher(position, editText)
);
ImageView image = (ImageView) convertView.findViewById(R.id.shape_image);
image.setBackgroundResource(images[position]);
TextView text = (TextView) convertView.findViewById(R.id.shape_text);
if (gameType == SHAPES_ABSTRACT)
text.setText("Seq:");
else
text.setVisibility(View.GONE);
return convertView;
}
#Override
public String getItem(int position) { return strings[position]; }
};
grid.setAdapter(listAdapter);
}
private class MyTextWatcher implements TextWatcher {
private int index;
private EditText edittext;
public MyTextWatcher(int index, EditText edittext) {
this.index = index;
this.edittext = edittext;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
public void afterTextChanged(Editable s) { strings[index] = s.toString(); }
public void setIndex(int newindex) { index = newindex; }
}
When I click into the first EditText (see picture), the EditText shifts to the one under the smiley face.
Not taking into account if this is a good UI design, here's how you'd do it:
public class TestList
{
public void blah()
{
ArrayAdapter<DataBucket> listAdapter = new ArrayAdapter<DataBucket>()
{
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(getContext()).inflate(R.layout.testlayout, null);
}
final DataBucket dataBucket = getItem(position);
final EditText editText = (EditText) convertView.findViewById(R.id.theText);
editText.setText(dataBucket.getSomeData());
editText.addTextChangedListener(new TextWatcher()
{
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
{
}
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2)
{
}
public void afterTextChanged(Editable editable)
{
dataBucket.setSomeData(editable.toString());
}
});
return convertView;
}
};
}
public static class DataBucket
{
private String someData;
public String getSomeData()
{
return someData;
}
public void setSomeData(String someData)
{
this.someData = someData;
}
}
}
'DataBucket' is a placeholder. You need to use whatever class you created to store the data that gets put into and edited in the edit text. The TextWatcher will have a reference to the data object referenced. As you scroll, the edit text boxes should get updated with current data, and text changes should be saved. You may want to track which objects were changed by the user to make data/network updates more efficient.
* Edit *
To use an int position rather than directly referencing the object:
ArrayAdapter<DataBucket> listAdapter = new ArrayAdapter<DataBucket>()
{
#Override
public View getView(final int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(getContext()).inflate(R.layout.testlayout, null);
}
final DataBucket dataBucket = getItem(position);
final EditText editText = (EditText) convertView.findViewById(R.id.theText);
editText.setText(dataBucket.getSomeData());
editText.addTextChangedListener(new TextWatcher()
{
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2)
{
}
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2)
{
}
public void afterTextChanged(Editable editable)
{
getItem(position).setSomeData(editable.toString());
}
});
return convertView;
}
};
* Edit Again *
I feel compelled to say for posterity, I wouldn't actually code it this way. I'd guess you want a little more structured data than a String array, and you're maintaining the String array outside, as well as an ArrayAdapter, so its sort of a weird parallel situation. However, this will work fine.
I have my data in a single String array rather than a multi-dimensional array. The reason is because the data model backing the GridView is just a simple list. That may be counterintuitive, but that's the way it is. GridView should do the layout itself, and if left to its own devices, will populate the row with variable numbers of cells, depending on how much data you have and how wide your screen is (AFAIK).
Enough chat. The code:
public class TestList extends Activity
{
private String[] guess;
//Other methods in here, onCreate, etc
//Call me from somewhere else. Probably onCreate.
public void initList()
{
ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(this, /*some resourse id*/, guess)
{
#Override
public View getView(final int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(getContext()).inflate(R.layout.testlayout, null);
}
final String theData = getItem(position);
final EditText editText = (EditText) convertView.findViewById(R.id.theText);
editText.setText(theData);
editText.addTextChangedListener(
new MyTextWatcher(position)
);
return convertView;
}
};
gridView.setAdapter(listAdapter);
}
class MyTextWatcher extends TextWatcher {
private int position;
public MyTextWatcher(int position) {
this.position = position;
}
public void afterTextChanged(Editable s) {
guess[position] = s.toString();
}
// other methods are created, but empty
}
}
To track the row number, each listener in EditText has to keep a reference to an item in a list and use getPosition(item) to get the position in a ListView. My example uses Button but I think that it can be applied to EditText.
class DoubleAdapter extends ArrayAdapter<Double> {
public DoubleAdapter(Context context, List<Double> list) {
super(context, android.R.layout.simple_list_item_1, list);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_row, null);
}
// keep a reference to an item in a list
final Double d = getItem(position);
TextView lblId = (TextView) convertView.findViewById(R.id.lblId);
lblId.setText(d.toString());
Button button1 = (Button) convertView.findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// the button listener has a reference to an item in the list
// so it can know its position in the ListView
int i = getPosition(d);
Toast.makeText(getContext(), "" + i, Toast.LENGTH_LONG).show();
remove(d);
}
});
return convertView;
}
}
It might be worth considering whether you need the edit texts to be stored in the list cells? It seems a little bit unnecessary when the user will only be editing one at a time.
Whilst I do not know how your app is designed I would recommend rethinking your user experience slightly so that when an list item is pressed a single text edit appears for them to edit. That way you can just get the list items reference as you normally would with a list adapter, store it whilst the user is editing and update it when they have finished.
i'm not sure if that's a nice design you have, as the EditText content will have a good chance of having problems (shuffling content, missing text) once your listview is scrolled. consider trying out m6tt's idea.
but if you really want to go your way, can you post some code, specifically of your TextWatcher?
I tried to solve this and as you can see there is a simple method - I am posting the answer here as it might be useful for someone.
Not able to get the position when list view -> edit text has a text watcher.
This is the solution that worked for me :
In get view -
when I add the text watcher listener to edit text, I also added the below line
edittext.setTag(R.id.position<any unique string identitiy>, position)
in your afterTextChanged -
int position = edittext.getTag(R.id.position)
Gives the correct position number and you can do modifications based on the position number.