Search code examples
androidandroid-listviewandroid-cursorcheckedtextview

Pre-fill the CheckedTextViews in a ListView


Here is my first question on StackOverFlow, I usually always find an answer by myself but I am really stuck on a weird problem that I will explain here:

I implemented a ListView in a fragment activity, this listview contains a list of categories related to the current record that I get from the SQLLite database.

All is working fine, I created a SimpleCursorAdapter to retrieve the data from the DB and I display the categories correctly in the ListView. The problem is related to the pre-fill of the checkboxes (it is a multiselection list), depending on how I try to pre-check the checkboxes, I get 2 cases:

First, the checkboxes are well pre-checked, but I cannot toggle the checkboxes anymore by clicking them. Second the click toggle well the checkboxes, but they are not pre-checked anymore...

Here is the part of the code where I have the problem:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    //super.onCreate(savedInstanceState);
    View v = inflater.inflate(R.layout.rate_fragment, container,false);

    dbCategories = "";
    displayCategories = resources.getText(R.string.no_categories).toString();


    /** INITIALIZATION */
    mViewSwitcher = (ViewSwitcher)v.findViewById(R.id.profileSwitcher);

    /** Edition view */
    rateGroup = (RadioGroup)v.findViewById(R.id.rate_group);
    rateOne = (RadioButton)v.findViewById(R.id.one_button);
    rateOne.setTag(1);
    rateTwo = (RadioButton)v.findViewById(R.id.two_button);
    rateTwo.setTag(2);
    rateThree = (RadioButton)v.findViewById(R.id.three_button);
    rateThree.setTag(3);
    rateFour = (RadioButton)v.findViewById(R.id.four_button);
    rateFour.setTag(4);
    rateFive = (RadioButton)v.findViewById(R.id.five_button);
    rateFive.setTag(5);

    descET = (EditText)v.findViewById(R.id.editdescription);
    descTextSize = descET.getTextSize();
    descET.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {

        }
    });

    categoriesTV_edit = (TextView)v.findViewById(R.id.edit_categories);
    categoriesBT = (Button) v.findViewById(R.id.select_categories);
    categoriesBT.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {

            View categoriesListTitle = getActivity().getLayoutInflater().inflate(R.layout.category_list_title, null);
            AlertDialog.Builder alt_bld = new AlertDialog.Builder(v.getContext()).setCustomTitle(categoriesListTitle);

            categories = db.getAllCategoriesByRate(currentRate);
            categoriesList = new ListView(getActivity());
            categoriesList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);                
            categoriesList.setClickable(true);

            String[] fromColumns = new String[] {
                    DatabaseHandler.CATEGORY_NAME
            };
            int[] toViews = new int[]{
                    R.id.cat_checked
            };

            //mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_multiple_choice, categories, fromColumns, toViews, 0);
            mAdapter = new SimpleCursorAdapter(getActivity(), R.layout.category_item, categories, fromColumns, toViews, 0);

            mAdapter.setViewBinder(new ViewBinder() {
                public boolean setViewValue(View view, Cursor cursor, int columnIndex) {

                    if (columnIndex == 1) {                     

                        CheckedTextView categRow = (CheckedTextView) view;

                        String catName = cursor.getString(1);
                        mAdapter.setViewText((TextView) view, catName);

                        int catChecked = cursor.getInt(2);
                        //boolean checkedCat = catChecked==1;
                        //categoriesList.setItemChecked(cursor.getPosition(),checkedCat);
                        categRow.setChecked(catChecked==1);

                        int catID = cursor.getInt(0);
                        categRow.setTag(catID);
                        return true;
                    }
                    else {
                        return false;
                    }
                }
            });

            categoriesList.setAdapter(mAdapter);

            alt_bld.setView(categoriesList);

To have one case or another, all depends on these 2 lines:

//boolean checkedCat = catChecked==1;
//categoriesList.setItemChecked(cursor.getPosition(),checkedCat);

If they are commented, the checkboxes are not pre-checked, but the toggle on the clicks is working. But if I comment these lines out, the toggle is not working anymore but the categories are prechecked.

What I also don't understand is that this line is not working:

 categRow.setChecked(catChecked==1);

But this one is working well (I succeed to retrieve the tag):

 categRow.setTag(catID);

So I hope someone will succeed to explain to me what I do wrong, I guess there is something I misunderstood here...

NOTE: I get 3 columns from the cursor "categories", first one is the ID of the category, second one is the name, and third one is the status: checked or not (1 or 0).

Thanks in advance for your time.


Solution

  • Finally I ended up creating my own custom adapter, this way I could at least understand more easily what was happening.

    I had to create actually several multiselect lists, some populated with data from the database, others from the shared preferences.

    For this one displaying data from the DB, I created the following adapter (I commented out the lines about the icons because I did not set them up yet):

    public class CategoriesLVAdapter extends BaseAdapter {
    private Context mContext;
    private LayoutInflater mInflater;
    private List<Category> categoriesList;
    
    // Constructor
    public CategoriesLVAdapter(Context c, List<Category> categories_list){
        mContext = c;
        mInflater = LayoutInflater.from(c);
        categoriesList = categories_list;
    }
    
    public List<Category> getCategoriesList(){
        return categoriesList;
    }
    
    @Override
    public int getCount() {
        return categoriesList.size();
    }
    
    @Override
    public Object getItem(int position) {
        return categoriesList.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return categoriesList.get(position).getID();
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.categories_list_row, null);
            //convertView.setLayoutParams(new ListView.LayoutParams(200, 90));
            holder = new ViewHolder();
            holder.title = (TextView) convertView.findViewById(R.id.categories_list_row_tv);
            //holder.icon = (ImageView) convertView.findViewById(R.id.categories_list_row_iv);
    
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();         
        }
    
        //holder.icon.setImageResource(categoriesList.get(position).getDrawableID());
        //holder.icon.setAdjustViewBounds(true);  
        //holder.icon.setScaleType(ImageView.ScaleType.CENTER_CROP);        
        holder.title.setText(categoriesList.get(position).getName());
    
        return convertView;
    }
    
    static class ViewHolder {
        TextView title;
        //ImageView icon;
    }
    

    }

    In my activity, I use this adapter when the AlertDialog is called to populate the ListView, then I pre-select the categories using the last ones saved in the shared preferences:

    private void categoriesFilter(){
        AlertDialog.Builder alt_bld = new AlertDialog.Builder(this);
        alt_bld.setTitle(resources.getText(R.string.select_categories).toString());
    
        LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);   
        View layout = inflater.inflate(R.layout.categories_list,(ViewGroup) findViewById(R.id.categories_layout_root));
        categoriesLV = (ListView) layout.findViewById(R.id.categories_list);
    
        alt_bld.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                String selectedCategoriesString = getSelectedValues(categoriesLV);
    
                //Update the shared preferences
                prefs.edit().putString(RateDayApplication.PREF_KEY_CATEGORIES, selectedCategoriesString).commit();
    
                updateFilterDisplay(resources.getText(R.string.cat_title).toString(), selectedCategoriesString, searchedCategoriesTV, "Category");
            }
        });
    
        alt_bld.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                dialog.cancel();
            }
        });
    
        String selectedCategoriesString = prefs.getString(RateDayApplication.PREF_KEY_CATEGORIES, new String());
        categoriesLV.setAdapter(new CategoriesLVAdapter(this, categoriesList));
    
        String[] selectedCategoriesArray = selectedCategoriesString.split(",");
    
        int categoriesLVLength = categoriesLV.getCount();
        for(int i = 0; i < categoriesLVLength; i++){
            int categoryID = ((Category) categoriesLV.getItemAtPosition(i)).getID();
            if(Arrays.asList(selectedCategoriesArray).contains(String.valueOf(categoryID))){
                categoriesLV.setItemChecked(i, true);
            }
        }
    
        alt_bld.setView(layout);
    
        AlertDialog alert = alt_bld.create();   
        alert.show();
    }
    

    Finally here is the function I call from my database handler to get the list of catagories:

    // Getting All Categories By ID desc
        public List<Category> getCategoriesList() {
            String selectQuery = "SELECT " + CATEGORY_ID + ", " + CATEGORY_NAME + " FROM " + CATEGORY_TABLE + " ORDER BY " + CATEGORY_ID + " ASC";
            SQLiteDatabase db = this.getReadableDatabase();
            Cursor cursor = db.rawQuery(selectQuery, null); 
    
            List<Category> categoriesList = new ArrayList<Category>();//String[] categoriesList = {};
    
            // looping through all rows and adding to list
            if (cursor.moveToFirst()) {
                do {
                    Category category = new Category(cursor.getInt(0), cursor.getString(1), false);
                    categoriesList.add(category);
                } while (cursor.moveToNext());
            }
    
            cursor.close();
            db.close();
            return categoriesList;
        }
    

    I think my problem before was coming from the fact that the function "setItemChecked" is a little misleading because it does not mean necessarily that anything is checked. When you use the function "setItemChecked", the item in the list view becomes selected, with or without a checkbox (my rows only contain text views).

    The rows selected in my list appear in a different color, and that's enough in my opinion for a simple multi selection list.

    The layouts I used are quite simple, "categories_list" contains a ListView in a LinearLayout and "categories_list_row" contains a TextView in a LinearLayout.

    Hope it may guide someone!