2

I have a listview with 4 list item each containing 2 text views and 1 edittext view. I have defined a addTextChangedListener in the custom adapter to lookout for changes in edittext and then save the text.

The addTextChangedListener is firing once when text in edittext view in list item 2,3 and 4 is changed but it fires multiple times when edit text in list item 1 is clicked. Also when I print the position of list item clicked from base adapter it shows that all 4 list items are looped through.

Could someone please explain why this is happening and suggest a solution please.

Below is the code and logcat output.

List View XML :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.dumbrella.uidesign.ListView">


<ListView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/listView"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="20dp"
    android:descendantFocusability="beforeDescendants" />

<Button
    style="?android:attr/buttonStyleSmall"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Submit"
    android:id="@+id/btnSubmit"
    android:layout_below="@+id/listView"
    android:layout_centerHorizontal="true" />

</RelativeLayout>

List Item XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:textSize="32dp"
    android:text="IC"
    android:gravity="center"
    android:id="@+id/questionIcon"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentTop="true" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:text="Small Text"
    android:id="@+id/questionText"
    android:layout_alignParentRight="true"
    android:layout_alignParentEnd="true"
    android:layout_toRightOf="@+id/questionIcon"
    android:layout_toEndOf="@+id/questionIcon"
    android:layout_alignParentTop="true" />

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/userAnswer"
    android:layout_alignParentBottom="true"
    android:layout_toRightOf="@+id/questionIcon"
    android:layout_alignRight="@+id/questionText"
    android:layout_alignEnd="@+id/questionText"
    android:inputType="text"
    android:textSize="12dp"/>

</RelativeLayout>

List View Activity :

import android.content.Context;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class QuestionsListView extends AppCompatActivity {

String TAG = "Question List View Activity";

ArrayList<ListData> adapterList = new ArrayList<ListData>();
ListView listViewDetail;
QuestionsAdapter mQuestionAdapter;
SharedPreferences userAnswersSharedPreferences;
SharedPreferences.Editor userAnswersSharedPreferencesEditor;

private String[] questionIcon = new String[]{
        "T", "W", "M", "C"
};

private String[] questionDescription = new String[]{
        "Question1?", "Question2?", "Question3?", "Question4?"
};

private String[] userAnswerHint = new String[]{
        "Answer1", "Answer2", "Answer3", "Answer4"
};

private String[] userAnswers = new String[] {
        "Null", "Null", "Null", "Null"
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_list_view);

    listViewDetail = (ListView) findViewById(R.id.listView);

    getDataInList();
    mQuestionAdapter = new QuestionsAdapter();
    listViewDetail.setAdapter(mQuestionAdapter);

    userAnswersSharedPreferences = getSharedPreferences("UserAnswersPreferences", Context.MODE_PRIVATE);
    userAnswersSharedPreferencesEditor = userAnswersSharedPreferences.edit();

    Button btnSubmit = (Button) findViewById(R.id.btnSubmit);
    btnSubmit.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            for (int i=0; i<questionIcon.length; i++) {

                ListData currentListData1 = mQuestionAdapter.getItem(i);
                Log.v("TAG" , currentListData1.getQuestionIcon());
                Log.v("TAG" , currentListData1.getQuestionDescription());
                Log.v("TAG" , currentListData1.getUserAnswerHint());
                Log.v("TAG" , currentListData1.getUserAnswer());
            }
        }
    });
}

private void getDataInList() {
    for (int i = 0; i < questionIcon.length; i++) {
        // Create a new object for each list item
        ListData listData = new ListData();
        listData.setQuestionIcon(questionIcon[i]);
        listData.setQuestionDescription(questionDescription[i]);
        listData.setUserAnswerHint(userAnswerHint[i]);
        listData.setUserAnswer("Null");
        // Add this object into the ArrayList myList
        adapterList.add(listData);
    }
}

public class QuestionsAdapter extends BaseAdapter {

    @Override
    public int getCount() {
        return adapterList.size();
    }

    @Override
    public ListData getItem(int position) {
        return adapterList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        if (convertView == null) {

            LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item, parent, false);
        }

        final TextView questionText = (TextView) convertView.findViewById(R.id.questionText);
        final TextView questionIcon = (TextView) convertView.findViewById(R.id.questionIcon);
        final EditText userAnswer = (EditText) convertView.findViewById(R.id.userAnswer);

        final ListData currentListData = getItem(position);

        questionIcon.setText(currentListData.getQuestionIcon());
        questionText.setText(currentListData.getQuestionDescription());
        //userAnswer.setHint(currentListData.getUserAnswerHint());

        questionIcon.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.v("TAG", "In Listener");
                questionIcon.setText("Z");
            }
        });

        userAnswer.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) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                Log.v("ListView Adapter After", "Text Changed to :" + s.toString());
                Log.v("ListView Adapter After", "List Item Clicked :" + String.valueOf(position));
                currentListData.setUserAnswer(userAnswer.getText().toString());
                switch (position) {
                    case 0:
                        userAnswersSharedPreferencesEditor.putString("T",s.toString());
                        break;
                    case 1:
                        userAnswersSharedPreferencesEditor.putString("W",s.toString());
                        break;
                    case 2:
                        userAnswersSharedPreferencesEditor.putString("M",s.toString());
                        break;
                    case 3:
                        userAnswersSharedPreferencesEditor.putString("C",s.toString());
                        break;
                }
                userAnswersSharedPreferencesEditor.commit();
                userAnswers[position] = currentListData.getUserAnswer();
            }
        });

        return convertView;
    }
}

private class ListData {

    private String questionIcon;
    private String questionDescription;
    private String userAnswerHint;
    private String userAnswer;

    public String getQuestionIcon() {
        return questionIcon;
    }

    public void setQuestionIcon(String questionIcon) {
        this.questionIcon = questionIcon;
    }

    public String getQuestionDescription() {
        return questionDescription;
    }

    public void setQuestionDescription(String questionDescription) {
        this.questionDescription = questionDescription;
    }

    public String getUserAnswerHint() {
        return userAnswerHint;
    }

    public void setUserAnswerHint(String userAnswerHint) {
        this.userAnswerHint = userAnswerHint;
    }

    public String getUserAnswer() {
        return userAnswer;
    }

    public void setUserAnswer(String userAnswer) {
        this.userAnswer = userAnswer;
    }
}
}

Logcat Output : (when text in list items 2,3 and 4 is changed)

05-11 20:58:03.453 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:58:03.453 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :2 


05-11 20:58:03.453 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:58:03.453 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :3

05-11 20:58:03.453 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f 05-11 20:58:03.453 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :4

Logcat Output : (when text in list item 1 is changed)

05-11 20:59:22.928 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.928 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :0
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :1
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :2
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :3
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :0
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :1
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :2
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :3
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :0
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :1
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.929 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :2
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :3
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign 
V/ListView Adapter After: Text Changed to :f
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :0
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :1
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :2
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
 05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :3
  05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: Text Changed to :f
  05-11 20:59:22.930 3914-3914/com.dumbrella.uidesign V/ListView Adapter After: List Item Clicked :0
Shree Krishna
  • 8,474
  • 6
  • 40
  • 68
  • You aren't approaching the getView method appropriately. That convertView is being recycled, but you are only re-inflating it if it is null. So it is adding a bunch of TextWatchers to the same view. – zgc7009 May 11 '16 at 15:43

3 Answers3

1

zgc7009 is right in that you are not approaching the getView method correctly. Although you aren't wrong with how you are inflating the view. The problem I suspect is that there is no guarantee how many times getView is called there is an arbitrary number of TextWatcher listeners being added to the EditText. At first I thought you could only add one TextWatcher listener to an EditText, but as it turns out you can add many. This is the source for the addTextChangedListener method:

public void addTextChangedListener(TextWatcher watcher) {
    if (mListeners == null) {
        mListeners = new ArrayList<TextWatcher>();
    }
    mListeners.add(watcher);
}

Anyways. getView is being called mutliple times and adding more and more listeners. But there is no way for you to know how many times this is going to be. You need to associate the listeners with the data or someother way. This way you can check if you should add the listener. So something like the following:

if(currentListData.hasListener) { // Just an example. You define the logic or how you want to store the TextWatcher listener
    userAnswer.addTextChangedListener(new TextWatcher() {
        //le code
    }
}

I'm not gonna write the code for you of course. But thats how to go about fixing it.

EDIT

Actually after thinking about it more you shouldn't even create more listeners than necessary. Add the listener when you inflate the view.

  if (convertView == null) {

        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_item, parent, false);
        EditText userAnswer = (EditText) convertView.findViewById(R.id.userAnswer);
        userAnswer.addTextChangedListener(new TextWatcher() {
            //le code
            @Override
            public void afterTextChanged(Editable s) {
                //Figure out the best way to get the current position.
            }
        }
    }
Andy
  • 10,553
  • 21
  • 75
  • 125
0

You need to remove all TextWatcher instances before You set a new one via Adapter

Here You can find solution to do that

Community
  • 1
  • 1
Roman Black
  • 3,501
  • 1
  • 22
  • 31
0

It will be better for you to dynamically add layout instead of ListView item. It is very critical to handle EditText in ListView.

Here is my suggestion.

Step 1: Add a LinearLayout instead of ListView. It's orientation must Vertical

Step 2: Dynamically add List Item layout on this LinearLayout, initialize its components and set Value into that components.

Here is Example that can add layout dynamically into a LinearLayout:

List<EditText> lists=new ArrayList<EditText>();
LinearLayout options_layout = (LinearLayout) findViewById(R.id.root_layout);
for (int i = 0; i < 4; i++) {
  View to_add = inflater.inflate(R.layout.list_element,
                        options_layout,false);

  EditText text = (EditText) to_add.findViewById(R.id.your_edit_text);
  text.setText("YOUR VALUE");
  lists.add(text);

  options_layout.addView(to_add);
}

Step 3: Add All EditText into a List<EditText> and addTextChangedListener on each EditText.

On afterTextChanged callBack you can get All EditText Value from List<EditText> via simple Loop on your List<EditText>.

Hope this will help you :)

Md. Sajedul Karim
  • 6,749
  • 3
  • 61
  • 87