简体   繁体   中英

Android: Checkbox in listview (how to create OnCheckedChangeListener in Adapter)

I'm creating a To-do list application and I have a question regarding to using checkboxes and its listeners in List Adapter. My single row in listview contains three TextViews and one Checkbox. I want to change background of single row when user "check" the checkbox. I have read that i should put checkbox listener in my adapter class and so I did it. Now is the problem - when i add few rows to my listview and left the checkbox unchecked for all of them all works fine, but when I add a row, check the checkbox and try to add another one I get error

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setBackgroundColor(int)' on a null object reference

Below is code of my adapter. Thank you for any advice. I'm just starting with Android programming so thank you for understanding in advance.

public class ToDoAdapter extends ArrayAdapter<ToDoTask> {


ArrayList<ToDoTask> objects;
Context context;
int resource;

public ToDoAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull ArrayList<ToDoTask> objects) {
    super(context, resource, objects);
    this.objects = objects;
    this.context = context;
    this.resource = resource;
}

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
    View view = convertView;
    ToDoHolder toDoHolder = null;

    if (view == null) {
        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = layoutInflater.inflate(R.layout.row, parent, false);

        toDoHolder = new ToDoHolder();
        toDoHolder.rowTitle = (TextView) view.findViewById(R.id.rowTitle);
        toDoHolder.rowDesc = (TextView) view.findViewById(R.id.rowDesc);
        toDoHolder.rowDate = (TextView) view.findViewById(R.id.rowDate);
        toDoHolder.rowIsDone = (CheckBox) view.findViewById(R.id.rowCheckBoxDone);

        toDoHolder.rowIsDone.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
                if(checked){
                    parent.getChildAt(position).setBackgroundColor(Color.parseColor("#8FE370"));
                }
                else
                    parent.getChildAt(position).setBackgroundColor(Color.WHITE);
            }
        });

        view.setTag(toDoHolder);
    } else {
        toDoHolder = (ToDoHolder) view.getTag();
    }

    ToDoTask object = objects.get(position);
    toDoHolder.rowTitle.setText(object.getTitle());
    toDoHolder.rowDesc.setText(object.getDescription());
    toDoHolder.rowDate.setText(object.getDate());
    toDoHolder.rowIsDone.setChecked(object.getDone());

    return view;
}

static class ToDoHolder {
    TextView rowTitle;
    TextView rowDesc;
    TextView rowDate;
    CheckBox rowIsDone;
}
}

Below is my MainActivity class which get details of single row element from "AddToDoTask" class.

public class MainActivity extends AppCompatActivity {
private final int requestCode = 1;
ArrayList<ToDoTask> lista = new ArrayList<>();
ToDoAdapter adapter = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button button = (Button) findViewById(R.id.buttonAdd);
    ListView listView = (ListView) findViewById(R.id.listView);

    adapter = new ToDoAdapter(this, R.layout.row, lista);
    listView.setAdapter(adapter);

    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(getApplicationContext(), AddToDoTask.class);
            startActivityForResult(intent, requestCode);
        }
    });

}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    String title, description, date;
    Boolean isDone;

    if (requestCode == 1) {
        if (null != data) {
            title = data.getStringExtra("title");
            description = data.getStringExtra("description");
            date = data.getStringExtra("date");
            isDone = data.getBooleanExtra("done", false);

            lista.add(new ToDoTask(title, description, date, isDone));
            adapter.notifyDataSetChanged();
        }
    }
}

} 在此处输入图片说明

public class ToDoAdapter extends ArrayAdapter<ToDoTask> {
    private ArrayList<ToDoTask> objects;
    private Context context;
    private int resource;
    private SparseBooleanArray checkedPositions = new SparseBooleanArray();

    public ToDoAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull ArrayList<ToDoTask> objects) {
        super(context, resource, objects);
        this.objects = objects;
        this.context = context;
        this.resource = resource;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ToDoHolder toDoHolder;
        if (convertView == null) {
            LayoutInflater layoutInflater = LayoutInflater.from(context);
            convertView = layoutInflater.inflate(R.layout.row, parent, false);
            toDoHolder = new ToDoHolder();
            toDoHolder.rowTitle = (TextView) convertView.findViewById(R.id.rowTitle);
            toDoHolder.rowDesc = (TextView) convertView.findViewById(R.id.rowDesc);
            toDoHolder.rowDate = (TextView) convertView.findViewById(R.id.rowDate);
            toDoHolder.rowIsDone = (CheckBox) convertView.findViewById(R.id.rowCheckBoxDone);
            convertView.setTag(toDoHolder);
        } else {
            toDoHolder = (ToDoHolder) convertView.getTag();
        }
        toDoHolder.rowTitle.setTag(position);
        toDoHolder.rowIsDone.setTag(convertView);
        toDoHolder.rowIsDone.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
                View view = (View) compoundButton.getTag();
                TextView title = (TextView) view.findViewById(R.id.rowTitle);
                int pos = (int) title.getTag();
                if (checked) {
                    checkedPositions.put(pos, true);
                    view.setBackgroundColor(Color.parseColor("#8FE370"));
                } else {
                    checkedPositions.put(pos, false);
                    view.setBackgroundColor(Color.WHITE);
                }
            }
        });
        ToDoTask object = objects.get(position);
        toDoHolder.rowTitle.setText(object.getTitle());
        toDoHolder.rowDesc.setText(object.getDescription());
        toDoHolder.rowDate.setText(object.getDate());
        toDoHolder.rowIsDone.setChecked(object.getDone() || checkedPositions.get(position));
        return convertView;
    }

    private class ToDoHolder {
        private TextView rowTitle;
        private TextView rowDesc;
        private TextView rowDate;
        private CheckBox rowIsDone;
    }
}

You must add a layout in your row xml file and put layout in toDoHolder and just change the layouts background color. You can access child views like

layout.findViewByID(int ID);

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