0

I'm trying to create a listview with each row having a checkbox and edittext. I've made edittext enabled on checkbox checked event by using notifyDatasetChanged().

But it resets position which makes it difficult to track current position. I've created an instance variable to keep track of current position on checkbox checked event, but that too resets to the last item checked.

The checkbox works fine. It enables edittext when checked and disables on uncheck, also checked status is fine. But the problem is edittext only getting last checked value if I use listposition, and last item if I use position.

My adapter is given below:

public class ListViewAdapter extends ArrayAdapter<Model> implements TextWatcher{

private final List<Model> list;
private final Context context;
private int listPosition;
private ViewHolder newHolder = null;

public ListViewAdapter(Context context, List<Model> list) {
    super(context, R.layout.row, list);
    this.context = context;
    this.list = list;
}

public static class ViewHolder {
    public CheckBox mCheckBox;
    public EditText mEditText;
}

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

    if (convertView == null) {
        LayoutInflater inflator = LayoutInflater.from(context);
        convertView = inflator.inflate(R.layout.row, null);
        newHolder = new ViewHolder();
        newHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.checkbox);
        newHolder.mEditText = (EditText) convertView.findViewById(R.id.txtValue);
        newHolder.mCheckBox.setText(list.get(position).getCheckText());
        newHolder.mEditText.setText(list.get(position).getValue());
        setEdit(false);

        convertView.setTag(newHolder);
        convertView.setTag(R.id.checkbox, newHolder.mCheckBox);
        convertView.setTag(R.id.txtValue, newHolder.mEditText);
    } else {
        newHolder = (ViewHolder) convertView.getTag();
    }

    newHolder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            int getPosition = (int) compoundButton.getTag();
            list.get(getPosition).setSelected(compoundButton.isChecked());
            list.get(getPosition).setEnabled(compoundButton.isChecked());
            setEdit(compoundButton.isChecked());
            listPosition = getPosition;
            if(!compoundButton.isChecked()){
                list.get(getPosition).setValue("");
            }
            notifyDataSetChanged();
        }
    });

    newHolder.mCheckBox.setTag(position); 
    newHolder.mEditText.setTag(position);
    newHolder.mEditText.addTextChangedListener(this);
    setEdit(list.get(position).isEnabled());
    return convertView;
}

public void setEdit(boolean bool){
    newHolder.mEditText.setEnabled(bool);
    newHolder.mEditText.setFocusableInTouchMode(bool);
    newHolder.mEditText.setCursorVisible(bool);
}

@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 s) {
    list.get(listPosition).setValue(s.toString());
}

}
Shrenik Shah
  • 1,900
  • 1
  • 11
  • 19
Nagaraju Gajula
  • 560
  • 5
  • 12
  • 1
    I'm not sure why you are needing to set tags and get positions in the list. The `getView` method has a position parameter for the current row. Also `notifyDatasetChanged` only needs to be called if you edit the `Model` at that position, so maybe you should do that instead and base how the view is drawn on that – OneCricketeer Oct 06 '16 at 05:11
  • See this : http://stackoverflow.com/a/18671362/4813855 and http://stackoverflow.com/a/36542854/4813855 and http://stackoverflow.com/questions/10536128/how-to-get-checkbox-edittext-with-listview-values-in-android-while-clicking-a-bu and http://stackoverflow.com/questions/21548722/custom-listview-with-edittext-checkbox-and-textview –  Oct 06 '16 at 05:11
  • See this for solving how to get position of your item http://www.mysamplecode.com/2012/07/android-listview-checkbox-example.html – Vishal Senjaliya Oct 06 '16 at 05:17
  • 1
    @cricket_007 I need to call `notifyDatasetChanged` to enable/disable `EditText`. The position parameter given in `getView` method resets to `last position` once we call `notifyDatasetChanged`. I tried using it earlier, but it didn't work. Also present code works fine if I check one item and enter text in the corresponding `EditText`, but fails if any user enters text in a non-current row. – Nagaraju Gajula Oct 06 '16 at 05:23
  • Maybe that's because `afterTextChanged` only uses the row of the last checkbox that was selected and only updates that Model. You might want to call notifyDatasetChanged there as well – OneCricketeer Oct 06 '16 at 05:28
  • `notifyDatasetChanged` takes more resources, so it's not a good idea to use it often. Also using it twice slows down or hangs app. You are right that `afterTextChanged` uses last checked value, that's the reason I'm looking for an alternative. I've tried using an int within `ViewHolder` and tagging it to `EditText`, but that too is getting same value as `position`. – Nagaraju Gajula Oct 06 '16 at 05:36
  • @ERVishalSenjaliya My `checkbox` works fine, but the problem is retrieving text from `EditText`. The link you've posted is a simple `listview` to retrieve checked status. – Nagaraju Gajula Oct 06 '16 at 06:03

1 Answers1

0

You should take arraylist to store checked position instead of storing into single interger. Define temp arraylist in your adpater and Create Hashmap to store value entered on specific edittext

ArrayList<String> selectedPosition = new ArrayList<String>();
HashMap<Integer, String> editHash = new HashMap<Integer, String>();

Now update your getView() method as per below :

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

    if (convertView == null) {
        LayoutInflater inflator = LayoutInflater.from(context);
        convertView = inflator.inflate(R.layout.row, null);
        newHolder = new ViewHolder();
        newHolder.mCheckBox = (CheckBox) convertView.findViewById(R.id.checkbox);
        newHolder.mEditText = (EditText) convertView.findViewById(R.id.txtValue);
        newHolder.mCheckBox.setText(list.get(position).getCheckText());
        newHolder.mEditText.setText(list.get(position).getValue());
        setEdit(false);

        convertView.setTag(newHolder);
        convertView.setTag(R.id.checkbox, newHolder.mCheckBox);
        convertView.setTag(R.id.txtValue, newHolder.mEditText);
    } else {
        newHolder = (ViewHolder) convertView.getTag();
    }


    newHolder.mCheckBox.setTag(position); 
    newHolder.mEditText.setTag(position);
    //newHolder.mEditText.addTextChangedListener(this); Remove this
    setEdit(list.get(position).isEnabled());

    //Added Change here...Check if arraylist contains selectedposition or not?

    if(selectedPosition.contains(String.valueOf(position))){
           list.get(getPosition).setSelected(compoundButton.isChecked());
            list.get(getPosition).setEnabled(compoundButton.isChecked());
            setEdit(compoundButton.isChecked());
    }else{
           setEdit(false);
           list.get(getPosition).setValue("");
    }

   //Same way compare for edittext

   if(editHash.containsKey(Integer.parseInt(position))){
       newHolder.mEditText.setText(editHash.get(position));
    }else{
       newHolder.mEditText.setText("");
    }

    newHolder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

        int position = (int) compoundButton.getTag();
        //Simply store and check selectedPosition

       if(!b)
           selectedPosition.remove(String.valueOf(position));
       else
           selectedPosition.add(String.valueOf(position));

       //And then update adapter
       notifyDataSetChanged();

        }
    });

   //Let's add Textwatcher for our Edittext
   newHolder.mEditText.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) {

             int position = (int) newHolder.mEditText.getTag();
             //Storing into Hashmap
             editHash.put(position, s.toString());

        }
    });


    return convertView;
}
Jai
  • 1,974
  • 2
  • 22
  • 42
  • Why a list of strings instead of integers? – OneCricketeer Oct 06 '16 at 05:19
  • @cricket_007 : yeah we could take list of integers also. doesn't matter. I only took string because when you apply add and remove operation it assmes position of arraylist you want to remove instead of value container. So it's bit easy with string – Jai Oct 06 '16 at 05:21
  • 1
    I think you're right. Adding and remove int primitives will perform auto-boxing – OneCricketeer Oct 06 '16 at 05:26
  • Thanks for the answer, but that doesn't solve the issue. First of all, list.get(getPosition).setSelected()` and `list.get(getPosition).setEnabled()` are working fine. That's the reason I'm able to enable/disable `EditText` by checking. Your code only confirms if the checkbox at current position is checked or not, which is already working fine. I particularly need to identify row number/id of the `EditText` user clicked so the `TextWatcher` can save the text to the corresponding `EditText`. – Nagaraju Gajula Oct 06 '16 at 06:08
  • @NagarajuGajula : yeah I got something, I have updated my answer. Kindly check it. I took hashmap to store value for particular edittext and then compared it – Jai Oct 06 '16 at 06:33
  • @NagarajuGajula : Let me know does it work for you or not? – Jai Oct 06 '16 at 06:58
  • Using `notifyDatasetChanged` within `TextWatcher` made it inaccessible. `EditText` is enabled, but couldn't type anything. It doesn't even display cursor. So, I guess it reloads as soon as is active. – Nagaraju Gajula Oct 06 '16 at 07:04
  • Removing `notifyDatasetChanged` within `TextWatcher` solved input issue, but there are other problems. Whenever I check/uncheck `checkbox`, all `EditText` are reset with value of first row. Testing with some changes. I will update if it works. – Nagaraju Gajula Oct 06 '16 at 07:10
  • @NagarajuGajula : have you removed this newHolder.mEditText.addTextChangedListener(this); line from your code? – Jai Oct 06 '16 at 07:22
  • @NagarajuGajula : I tested all changes I made here with getView() method. It's working fine for me. So check once you are not missing anything to update in your code yet – Jai Oct 06 '16 at 07:24
  • @NagarajuGajula : I have added this int position = (int) newHolder.mEditText.getTag(); in textWatcher to get proper position while storing data – Jai Oct 06 '16 at 07:43