0

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();
        }
    }
}

} enter image description here

Community
  • 1
  • 1
Grzegorz
  • 39
  • 1
  • 7
  • See if this helps [change checkbox background color in android](https://stackoverflow.com/questions/12889729/change-checkbox-background-color-in-android). – MikeT Aug 20 '17 at 10:26
  • please post pice of code where you add items inside your adapter – an_droid_dev Aug 20 '17 at 10:45

2 Answers2

1
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;
    }
}
prakash
  • 162
  • 3
  • Thank you, it solved problem with adding new rows but now when i add new row, the previous one with checked checkbox has a white background instead of green. I assume that i have to use getTag() in else case like it is in "toDoHolder = (ToDoHolder) view.getTag();" - am I right? How to use getTag on this element? – Grzegorz Aug 20 '17 at 11:44
  • Please move above mentioned code from with in if condition and just paste above toDoHolder.rowIsDone.setChecked(object.getDone()); – prakash Aug 20 '17 at 11:54
  • You mean whole code from your above post? I did it but the background still turns from green to white when I add new row. – Grzegorz Aug 20 '17 at 13:26
  • 1
    When we are adding new row, for updating data we have to call notifyDataSetChanged() method. This will reset all rows as per ToDoTask model data and it will set green only for object.getDone() is true. If you want to keep manual checked status, we have to persist checked positions, I have modified your adapter please have a look. – prakash Aug 21 '17 at 05:11
  • I don't know why but i had to exchange background color in if/else loop, I mean put color White in " checkedPositions.put(pos, true);" and now all works fine. Thank you for your effort and help, topic resolved. – Grzegorz Aug 21 '17 at 21:35
0

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);
Cahid Enes Keles
  • 733
  • 1
  • 8
  • 13