简体   繁体   中英

Android: Dynamic number of CheckBoxes for each Row in a ListView

My app shows a ListView with a TextView and checkbox for each row. The TextView contains different types of food.

Each row has one checkbox. I want to change that. Each type of food has an attribute called numberOfPortions . This number should determine the number of checkboxes in the row.

For example:

ListItem Beans numberOfPortions = 3  

This means that the row in my ListView with beans should have 3 checkboxes instead of previously 1 checkbox.

How can I achieve that? If I add three checkboxes to my row layout, three checkboxes will be shown in each row, but there are also rows were numberOfPortions is 1 or 2 so that only one or two checkboxes should be shown.

This image shows how my list should look like:

在此处输入图片说明

EDIT:

I added this piece of code to my Adapter. It should add new CheckBoxes to each row dependent on the Integer numCheckBox:

int numCheckBox = item.numCheckBox;
CheckBox[] cb = new CheckBox[numCheckBox];
for(int i = 0; i < numCheckBox; i++){
    cb[i] = new CheckBox(parent.getContext());
     holder.rl.addView(cb[i]);
}

I am getting a NullPointerException . But, I don't understand why it keeps occurring?

Process: com.example.xx.myapplication, PID: 23887
java.lang.NullPointerException
      at com.example.timofocken.myapplication.ItemListAdapter.getView(ItemListAdapter.java:103)
      at android.widget.AbsListView.obtainView(AbsListView.java:2458)
      at android.widget.ListView.makeAndAddView(ListView.java:1891)
      at android.widget.ListView.fillDown(ListView.java:792)

Here is my Adapter:

public class ItemListAdapter extends BaseAdapter {

    private LayoutInflater inflater;
    private ArrayList<Object> itemArray;
    int resIdImage;
    private static final int TYPE_ListElement = 0;
    private static final int TYPE_DIVIDER = 1;

    public ItemListAdapter(Context context, ArrayList<Object> itemArray, int resource) {
        this.itemArray = itemArray;
        this.resIdImage = resource;
        this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return itemArray.size();
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public Object getItem(int position) {
        return itemArray.get(position);
    }

    @Override
    public int getViewTypeCount() {
        // TYPE_PERSON and TYPE_DIVIDER
        return 2;
    }

    @Override
    public int getItemViewType(int position) {
        if (getItem(position) instanceof ItemList) {
            return TYPE_ListElement;
        }

        return TYPE_DIVIDER;
    }

    @Override
    public boolean isEnabled(int position) {
        return (getItemViewType(position) == TYPE_ListElement);
    }

    public class DataHolder{
        ImageView imageView;
        TextView textView;
        CheckBox cb;
        RelativeLayout rl;
    }

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

        DataHolder holder =  null;
        int type = getItemViewType(position);
        if (convertView == null) {
            switch (type) {
                case TYPE_ListElement:
                    convertView = inflater.inflate(resIdImage, parent, false);
                    holder = new DataHolder();
                    holder.imageView = (ImageView) convertView.findViewById(R.id.image);
                    holder.textView = (TextView) convertView.findViewById(R.id.nameLabel);
                    holder.cb = (CheckBox) convertView.findViewById(R.id.checkbox);
                    holder.rl = (RelativeLayout) convertView.findViewById(R.id.row_layout);
                    convertView.setTag(holder);
                    break;
                case TYPE_DIVIDER:
                    convertView = inflater.inflate(R.layout.row_header, parent, false);
                    break;
            }
        } else {
            holder = (DataHolder)convertView.getTag();
        }

        switch (type) {
            case TYPE_ListElement:
                ItemList item = (ItemList) getItem(position);
                holder.textView.setText(item.getTitle());
                holder.imageView.setImageResource(item.resIdImage);
                holder.cb.setChecked(item.checked);

                int numCheckBox = item.numCheckBox;
                CheckBox[] cb = new CheckBox[numCheckBox];
                for(int i = 0; i < numCheckBox; i++){
                    cb[i] = new CheckBox(parent.getContext());
                     holder.rl.addView(cb[i]);
                }

                break;
            case TYPE_DIVIDER:
                TextView title = (TextView)convertView.findViewById(R.id.headerTitle);
                String titleString = (String)getItem(position);
                title.setText(titleString);
                break;
        }

        return convertView;
    }
}

My row layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@mipmap/ic_launcher"
        android:layout_gravity="center"
        android:layout_margin="20dp"/>

    <TextView
        android:id="@+id/nameLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_margin="20dp"
        android:text="Name"
        android:layout_weight="1" />

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_gravity="right|center"
        android:gravity="right|center" />
</LinearLayout>

I am not quite sure if this is the graceful solution to the problem specified in your question. However, after checking the desired UI and your comment later above, I think there is a workaround which involves less trouble.

I would like to suggest keeping three checkboxes (as three is the max limit of checkboxes a row can have) in your row layout. So the final layout of each row should look like the following.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@mipmap/ic_launcher"
        android:layout_gravity="center"
        android:layout_margin="20dp"/>

    <TextView
        android:id="@+id/nameLabel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_margin="20dp"
        android:text="Name"
        android:layout_weight="1" />

    <CheckBox
        android:id="@+id/checkbox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_gravity="right|center"
        android:gravity="right|center" />

    <CheckBox
        android:id="@+id/checkbox2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_gravity="right|center"
        android:gravity="right|center" />

    <CheckBox
        android:id="@+id/checkbox3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:layout_gravity="right|center"
        android:gravity="right|center" />
</LinearLayout>

The DataHolder class inside your Adapter should look like this.

public class DataHolder{
    ImageView imageView;
    TextView textView;
    CheckBox cb1;
    CheckBox cb2;
    CheckBox cb3;
    RelativeLayout rl;
}

Then in your getView function take three different references.

holder.cb1 = (CheckBox) convertView.findViewById(R.id.checkbox1);
holder.cb2 = (CheckBox) convertView.findViewById(R.id.checkbox2);
holder.cb3 = (CheckBox) convertView.findViewById(R.id.checkbox3);

Then modify the switch case like the following.

int numCheckBox = item.numCheckBox;

if(numCheckBox == 1) {
    cb1.setVisibility(View.VISIBLE);
    cb2.setVisibility(View.GONE);
    cb3.setVisibility(View.GONE);
} else if(numCheckBox == 2) {
    cb1.setVisibility(View.VISIBLE);
    cb2.setVisibility(View.VISIBLE);
    cb3.setVisibility(View.GONE);
} else if(numCheckBox == 3) {
    cb1.setVisibility(View.VISIBLE);
    cb2.setVisibility(View.VISIBLE);
    cb3.setVisibility(View.VISIBLE);
}

Once the items are populated correctly, you can receive the actions on those checkbox items accordingly.

I hope you get the idea. Please let me know if there is anything else I can help you with.

Edit:

You do not have to loop through the checkboxes as you can explicitly set the onCheckedChangeListener to each of your checkbox which will save your desired values in SQLite. The invisible checkbox will not have any effect on their listener. Hope that helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM