Search code examples
androidcustom-adaptercheckedtextview

CustomAdapter - CheckedTextView


I have a problem with "remembering" states of Checkboxes in CheckTextView. Now when I click, it changes the state but on different CheckBox.

class CustomAdapterSmartwatch extends ArrayAdapter<String> {


static class ViewHolder {
    protected CheckedTextView cTextView;
}

ViewHolder holder;


public CustomAdapterSmartwatch(Context context, ArrayList<String> variables) {
    super(context,R.layout.row_smartwatch_resources,variables);
}

@NonNull
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        holder = new ViewHolder();
        LayoutInflater smartwatchinflater = LayoutInflater.from(getContext());
        convertView = smartwatchinflater.inflate(R.layout.row_smartwatch_resources,parent,false);
        holder.cTextView = (CheckedTextView) convertView.findViewById(R.id.row_smartwatch_checked);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.cTextView.setText(getItem(position));

    holder.cTextView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (holder.cTextView.isChecked()) {
                holder.cTextView.setChecked(false);
            } else {
                holder.cTextView.setChecked(true);
            }
        }
    });

    return convertView;
}

}

Now i'm trying to figure out with using Holder but i heard that it's possible to use SparseBooleanArray. Can someone explain how to make it works?

EDIT 1 Ok, I have ListView which is controlled by CustomAdapter. That adapter contains only 1 item (CheckedTextView). The problem is that after scrolling these checkedboxes don't remember their state. I saw your answer with explanation but now after clicking any "CheckedTextView", the state of their checkbox doesn't change.

EDIT 2

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

    holder.cTextView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("MainActivity","Position = " + position);
            CheckedTextView checkedText = (CheckedTextView) v;
            checkedState.put(position, !checkedText.isChecked());

            Log.d("MainActivity","State = " + checkedState.get(position));

        }
    });

    holder.cTextView.setText(getItem(position));
    holder.cTextView.setChecked(checkedState.get(position));

    return convertView;
}

EDIT 3

class CustomAdapterSmartwatch extends ArrayAdapter<String> {

public SparseBooleanArray checkedState = new SparseBooleanArray();
private CheckedTextView cTextView;


public CustomAdapterSmartwatch(Context context, ArrayList<String> variables) {
    super(context,R.layout.row_smartwatch_resources,variables);
}

@NonNull
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    LayoutInflater smartwatchinflater = LayoutInflater.from(getContext());
    convertView = smartwatchinflater.inflate(R.layout.row_smartwatch_resources,parent,false);
    cTextView = (CheckedTextView) convertView.findViewById(R.id.row_smartwatch_checked);

    cTextView.setText(getItem(position));
    cTextView.setChecked(checkedState.get(position));

    cTextView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("MainActivity","Position = " + position);
            CheckedTextView checkedText = (CheckedTextView) v;
            checkedText.toggle();

            if (checkedText.isChecked())
                checkedState.put(position,true);
            else
                checkedState.delete(position);
            Log.d("MainActivity","State = " + checkedState.get(position));

        }
    });
    return convertView;
}

}


Solution

  • You have to keep a boolean flag for each CheckTextView. This can be an array, List, or SparseBooleanArray. The data structure you choose should be a field of your adapter:

    class CustomAdapterSmartwatch extends ArrayAdapter<String> {
        private SparseBooleanArray checkedState = new SparseBooleanArray();
    
        // ...
    }
    

    You do not want to put it in the holder because the holder is retired only fr visible rows. On the other hand, you want to maintain the checked state for every row, visible or not.

    Now, you set the stored checked state when the user clicks on a CheckedTextView:

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // ...
    
        holder.cTextView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                CheckedTextView checkedText = (CheckedTextView)v;
                checkedState.put(position, checkedText.isChecked());
            }
        });
    }
    

    And you restore the checked state whenever you inflate a view or reuse one from a holder:

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // ...
    
        holder.cTextView.setText(getItem(position));
        holder.cTextView.setChecked(checkedState.get(position));
    
        // ...
    }