Question

I have been using a ListView with each item a CheckableLinearLayout as defined in

CheckableLinearLayout

I cannot check any row in the ListView, it does not even register the check in the UI. I do not believe there is a android:checkMark for the CheckableLinearLayout. Is it correct to nest the CheckedTextView inside my CheckableLinearLayout, would it be better if I used a CheckBox instead. Also, how can I put them in the ArrayList as soon as they are checked, instead of iterating over them in this manner.

This is my layout:

   <com.example.deltest.CheckableLinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 >
<CheckedTextView 
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
 />
<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:id="@+id/txt_a"
          />
<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:id="@+id/txt_b"
    />

   </com.example.deltest.CheckableLinearLayout>

I have implemented the listActivity as follows:

    public class DeleteList extends ListActivity 
    {
    ArrayList<Integer> idList=new ArrayList<Integer>();
    Cursor  c;
    Handler handler;
    static final int WHAT=0;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    final TumblrDB db=new TumblrDB(this);
    c=db.query();
    startManagingCursor(c);
    handler=new Handler();
    View v=getLayoutInflater().inflate(R.layout.layout_header, null,false);
    getListView().addHeaderView(v);
    getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    SimpleCursorAdapter adapter=new SimpleCursorAdapter(this,R.layout.layout_del,c,new String[] {TumblrDB.DATE,TumblrDB.DESC},new int[]{R.id.txt_a,R.id.txt_b});
    getListView().setAdapter(adapter);
    if(handler.hasMessages(WHAT))
    {
        c=db.query();
        ((SimpleCursorAdapter)getListView().getAdapter()).swapCursor(c);
    }

    Button btn_del=(Button)v.findViewById(R.id.btn_del);
    btn_del.setOnClickListener(new OnClickListener(){

        @Override
        public void onClick(View view) {
            // TODO Auto-generated method stub
            int count=getListView().getCount();
            SparseBooleanArray sparseBooleanArray=getListView().getCheckedItemPositions();
            for(int i=0;i<count;i++)
            {
                if(sparseBooleanArray.get(i))
                {
                    c.moveToPosition(i);
                    int _id=c.getInt(c.getColumnIndex(TumblrDB._ID));
                    Log.d("DeleteList", "The id has been added "+_id);
                    idList.add(_id);
                    new Thread()
                    {
                        public void run()
                        {
                            for(int i:idList)
                            {
                                db.delete(i);
                                Log.d("DeleteList", "The id has been deleted "+i);
                                handler.sendEmptyMessage(WHAT);
                            }
                        }
                    }.start();
                }
            }
        }

    });

}   

 }

EDIT Trying a different approach to solve the problem by extending SimpleCursorAdapter:

    if(row==null)
    {
        row=inflater.inflate(layout, null, false);
        holder=new ViewHolder(row,position);
        row.setTag(holder);
        holder.check_del.setTag(position);
    }
    else
    {
        holder=(ViewHolder)row.getTag();
        holder.check_del.setOnClickListener(null);
        holder.check_del.setTag(position);
        if(hitList.get(position))
            holder.check_del.setChecked(true);
        else
            holder.check_del.setChecked(false);
    }

Then I have implemented an OnClickListener for the CheckBox:

      OnClickListener cbl=new OnClickListener(){

        @Override
        public void onClick(View view) {
            // TODO Auto-generated method stub
            int position=(Integer)view.getTag();
            Log.d("CustomCursorAdapter", "The checkbox at postion "+position+" has been clicked");
            if(((CheckBox)view).isChecked())
            {
                hitList.set(position, true);
            }
            else 
            {
                hitList.set(position, false);
            }
        }
    };  

Now when I try to access the ArrayList that I am using,it throws an ArrayOutOfBoundsException:

    btn_del.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View view) {
            // TODO Auto-generated method stub
            new Thread()
            {
                public void run()
                {
                    int count=lv.getCount();
                    for(int i=0;i<count;i++)
                    {
                        if(adapter.hitList.get(i))
                        {
                            db.delete(c.getInt(c.getColumnIndex(TumblrDB._ID)));
                        }
                    }
                }
            }.start();
            adapter.notifyDataSetChanged();
        }
    });
Was it helpful?

Solution

Using an ArrayList to store the state of all the checkboxes in the list.

   public static ArrayList<Boolean> hitList;

I have initialized and set all values to false:

    hitList=new ArrayList<Boolean>();
    for(int i=0;i<this.getCount();i++)
    {
        hitList.add(i, false);
    } 

When,initializing the checkbox for a View,it looks in the array by position and checks it only if the value in the ArrayList is true.The position is held by the CheckBox using it's setTag method and retrieved within the Listener to determine the position of the checkbox which has been checked.

   @Override
public View getView(int position,View convertView,ViewGroup parent)
{
    View row=convertView;
    ViewHolder holder;

    if(row==null)
    {
        row=inflater.inflate(layout, null, false);
        holder=new ViewHolder(row);
        row.setTag(holder);
        holder.check_del.setTag(position);
    }
    else
    {
        holder=(ViewHolder)row.getTag();
        holder.check_del.setOnClickListener(null);
        holder.check_del.setTag(position);
        if(hitList.get(position))
            holder.check_del.setChecked(true);
        else
            holder.check_del.setChecked(false);
    }
    c.moveToPosition(position);
    holder.txt_a.setText(c.getString(c.getColumnIndex(ItemDB.TITLE)));
    holder.txt_b.setText(c.getString(c.getColumnIndex(ItemDB.DESCRIPTION)));

    OnClickListener cbl=new OnClickListener(){

        @Override
        public void onClick(View view) {
            // TODO Auto-generated method stub
            int position=(Integer)view.getTag();
            Log.d("CustomCursorAdapter", "The checkbox at postion "+position+" has been clicked");
            if(((CheckBox)view).isChecked())
            {
                hitList.set(position, true);
            }
            else 
            {
                hitList.set(position, false);
            }
        }
    };  

    holder.check_del.setOnClickListener(cbl);
    return row;
}

static class ViewHolder
{
    TextView txt_a;
    TextView txt_b;
    CheckBox check_del;
    ViewHolder(View row)
    {
        txt_a=(TextView)row.findViewById(R.id.txt_a);
        txt_b=(TextView)row.findViewById(R.id.txt_b);
        check_del=(CheckBox)row.findViewById(R.id.check_del);
    }
}

Currently,I have a header in the ListView which has the following onClickListener:

    @SuppressWarnings("deprecation")
        @Override
        public void onClick(View view) {
            // TODO Auto-generated method stub
            ArrayList<Boolean> killList=CustomCursorAdapter.hitList;
            int totalDeleted=0;
            for(int i=0;i<killList.size();i++)
            {
                //Log.d(TAG,i+":"+killList.get(i));
                c.moveToPosition(i);
                String title=c.getString(c.getColumnIndex(ItemDB.TITLE));
                int _id=c.getInt(c.getColumnIndex(ItemDB._ID));
                //Log.d(TAG, "Item has title "+title+" and id "+_id );
                int rowsAffected=0;
                if(killList.get(i))
                {
                    rowsAffected=db.delete(_id);
                    //Log.d(TAG, "Deleted row with id "+_id);
                    killList.set(i, false);

                }
                totalDeleted+=rowsAffected;

            }
            //Log.d(TAG, "The total number of rows deleted "+totalDeleted);
            //if(c.requery())
            //{
                //Log.d(TAG, "Requerying data,this calls notifyDataSetChanged");
            //}

        }});

Once,you have deleted data from the row,before you call requery on the Cursor(which is deprecated...need to find a better alternative),the values in the ArrayList have to be reset.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top