0

The question is simple, as it has already been discussed here - extensively! -, about Listview with checkboxes loosing state after scrooling or unable to maintain it's state. In my case, when a scroll out of the view, the checked boxs become unckecked. In this post: Listview, custom adapter and checkboxes, I almost achieved the desire behaviour; however, it's using ArrayList, and I've read that the number of redraws this implementation makes, it's worth when you have a very long list... I used more than 20 implementations available here. Unfortunately, I was unable to find out why is not working. So, please have patience with me. I'll post only what I believe is fundamental to my problem. Below ShowChosenItensFromCategoriaActivity. Note: I'm passing the dbAdapter along with the SimpleCursorAdapter.

    public class ShowChosenItensFromCategoriaActivity1 extends AppCompatActivity
{ 

    private ListView mListView;
    private SimpleCursorAdapter mCursorAdapter;
    private Cursor mCursor;
    private Bundle bundle;
    private DBAdapter dbAdapter;
    private int CATEGORIA_ID;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
    
        super.onCreate(savedInstanceState);
        bundle = this.getIntent().getExtras();
        CATEGORIA_ID = bundle.getInt("CATEGORIA_ID");

        setContentView(R.layout.show_all_produtos_from_chosen_categoria);
        dbAdapter = new DBAdapter(this);
        dbAdapter.open();
        mCursor = dbAdapter.getChosenProdutosFromCategoria(CATEGORIA_ID);

        String[] From = new String[]{"produtoname", "checked"};
        int[] To = new int[]{R.id.idlistitem, R.id.idcheck};

        mCursorAdapter = new ShowChosenItensFromCategoriaAdapter1(this,
                                                                  R.layout
                                                                  .show_all_produtos_from_chosen_categoria_row,
                                                                  mCursor, From, To, 0, dbAdapter);
        mListView = (ListView) findViewById(R.id.produtos_listview);
        mListView.setAdapter(mCursorAdapter);
        }
    }

Now the show_all_produtos_from_chosen_categoria.xml, the method getChosenProdutosFromCategoria(CATEGORIA_ID) and it's .show_all_produtos_from_chosen_categoria_row.xml.

Code for: show_all_produtos_from_chosen_categoria.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

      <ListView
            android:id="@+id/produtos_listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@+id/toolbar"
            android:layout_marginBottom="@dimen/list_padding"
            android:layout_marginTop="@dimen/list_padding"
            android:background="@color/lime_100"
            android:padding="@dimen/list_padding"
            android:scrollbarStyle="outsideOverlay"
      tools:listitem="@layout/show_all_produtos_from_chosen_categoria_row"/>
    </RelativeLayout>

Code for getChosenProdutosFromCategoria(CATEGORIA_ID):

    public Cursor getChosenProdutosFromCategoria(int categoriaID)
    {
        mCursor = mDB.rawQuery(
            "SELECT l._id, l.listaprodutoid as produtoID, p.produtoname as produtoname, " +
            "l.listacheckbox as checked " +
            "FROM tbllistadecompras l, tblprodutos p WHERE l.listaprodutoid = p._id and " +
            "l.listacategoriaid=" + categoriaID + " ORDER BY p.produtoname", null);


        if (mCursor != null) mCursor.moveToFirst();
        assert mCursor != null;
        return mCursor;
    }

Code for: show_all_produtos_from_chosen_categoria_row.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:background="?android:attr/activatedBackgroundIndicator"
                android:orientation="horizontal"
                tools:context=".ShowChosenItensFromCategoriaActivity">

        <TextView
            android:id="@+id/idlistitem"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:elegantTextHeight="true"
            android:ellipsize="end"
            android:scrollHorizontally="false"
            android:textColor="@color/blue"
            android:textSize="20sp"/>

        <CheckBox
            android:id="@+id/idcheck"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_marginEnd="10dip"
            android:layout_marginStart="4dip"
            android:focusable="false"
            android:focusableInTouchMode="false"
            android:gravity="center">
        </CheckBox>
    </RelativeLayout>

And now, the biggest problem, the adapter ShowChosenItensFromCategoriaAdapter. I've tried newView/bindView and getView alone. Here, I inserted the getView: method:

    class ShowChosenItensFromCategoriaAdapter extends SimpleCursorAdapter
    {
        private DBAdapter dbAdapter;
        private final Context context;

        public ShowChosenItensFromCategoriaAdapter1(Context context, int 
     layout, Cursor c, String[] from, int[] to, int flags,DBAdapter dbAdapter)
        {
            super(context, layout, c, from, to, flags);
            this.context = context;
            this.dbAdapter = dbAdapter;
     
            dbAdapter.open();
        }

        @Override
        public View getView(final int position, View convertView, ViewGroup 
    parent)
        {
            ViewHolder viewHolder;
            final int auxProdutoID; // has to be, to be used inside the 
        checkbox.method
            if (mCursor.moveToPosition(position))
           {
               if (convertView == null)
               {
                   LayoutInflater inflator = (LayoutInflater) 
        content.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                   viewHolder = new ViewHolder();
                   viewHolder.produtoID = 
    mCursor.getInt(mCursor.getColumnIndex("produtoID"));
                   auxProdutoID = viewHolder.produtoID;
                   viewHolder.textviewProduto = (TextView) 
    convertView.findViewById(R.id.idlistitem);
                   viewHolder.checkBox = (CheckBox) 
    convertView.findViewById(R.id.idcheck);
                   viewHolder.checkBox.setOnCheckedChangeListener(
                       new CompoundButton.OnCheckedChangeListener()
                       {
                        @Override
                        public void onCheckedChanged(CompoundButton 
     buttonView,boolean isChecked)
                       {
                           dbAdapter.setCheckBox(auxProdutoID, isChecked ? 1 
    : 0);
                           viewHolder.checkBox.setSelected(isChecked);
                           //int getPosition = (Integer) 
    buttonView.getTag();
                       }
                      });
        
               convertView.setTag(viewHolder);
          }
          else
          {
             viewHolder = (ViewHolder) convertView.getTag();
          } 

         viewHolder.checkBox.setTag(position)
         viewHolder.textviewProduto.setText(
         mCursor.getString(mCursor.getColumnIndex("produtoname")));
         viewHolder.checkBox.setChecked(
         mCursor.getInt(mCursor.getColumnIndex("checked")) == 1);
 
         return convertView;
        }
    }
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Heitor
  • 11
  • 4

2 Answers2

0

To determine if the view should be checked you are using the value of the column in the database:

viewHolder.checkBox.setChecked(mCursor.getInt(mCursor.getColumnIndex("checked")) == 1);

But to mark the view as checked you are using the DBAdapter class:

dbAdapter.setCheckBox(auxProdutoID, isChecked ? 1 : 0);

You should use the same code to control the checkbox state, if you are controlling the state in the DBAdapter class, to decide if the check should be checked you should check the DBAdapter class or if you prefer to use the database, in the onClickListener you should update the data in the database.

jonathanrz
  • 4,206
  • 6
  • 35
  • 58
  • Hi there. Thanks for the answer. The method dbAdapter.setCheckBox(...) does exactly this: I mark the database column as checked and it does the update. In fact, when I leave the activity and get back to it, all checkboxes checked are there. The problem relies on the scrolling, Recyclerview nature of Listview, which I am unable to quite understand. Thank you for the insigth. – Heitor May 20 '17 at 21:18
0

Sometime maintenance issues happen due to custom item layout used in the list view. Have you tried this?

mCursorAdapter = new ShowChosenItensFromCategoriaAdapter1(this,
                                                          android.R.layout.simple_list_item_checked,
                                                          mCursor, From, To, 0, dbAdapter);
mListView = (ListView) findViewById(R.id.produtos_listview);
mListView.setAdapter(mCursorAdapter);
mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Zeero0
  • 2,602
  • 1
  • 21
  • 36