CheckBox checked/unchecked state changes on scroll

2019-04-17 17:47发布

问题:

I have the list view with some pre-checked checked boxes. When I use to uncheck the checked checkbox and scroll it again, it gets checked, and if I check the unchecked the checkbox then scroll it, it changed to the unchecked state.

When I

  • check the checkbox and then scroll it, it gets unchecked

  • uncheck the pre checked checkbox and scroll it, it gets checked

Code:

public class Adapter extends BaseAdapter {

    private Context activity;
    private ArrayList<Data> data;
    private static LayoutInflater inflater = null;
    private View vi;
    TextView roll;
    TextView menu;
    CheckBox checkBox;
    CheckBox cb;
    boolean[] checkBoxState;//maintain a local boolean array to store the checked status of checkboxes positionwise
    public Adapter(Context context, ArrayList<Data> items) {
        this.activity = context;
        this.data = items;
        checkBoxState=new boolean[items.size()];//initializing the local boolean array inside the constructor
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

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

    @Override
    public Object getItem(int i) {
        return data.get(i);
    }

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

    @Override
    public View getView(final int position, View view, ViewGroup viewGroup) {
        vi = view;
        final int pos = position;
        final Data items = data.get(pos);
        vi = inflater.inflate(R.layout.list_row, null);
        checkBox = (CheckBox) vi.findViewById(R.id.cb);
        menu = (TextView) vi.findViewById(R.id.nama_menu);
        roll = (TextView) vi.findViewById(R.id.roll);
        menu.setText(items.getMenu());
        roll.setText(items.getRoll());
        if (items.getRoll().equals(items.getRollBlocked())) {
            checkBox.setChecked(true);
            checkBoxState[position] = true;
            items.setCheckbox(true);//maintaining boolean check status according to conditions
            notifyDataSetChanged();
            vi.setEnabled(false);
        }
        else if (items.getRoll().equals(items.getRollLate())) {
            checkBox.setChecked(true);
            checkBoxState[position] = true;//maintaining boolean check status according to conditions
            items.setCheckbox(true);
            notifyDataSetChanged();
            vi.setEnabled(false);
        }
        else if (items.getRoll().equals(items.getRollCheck())) {
            checkBox.setChecked(true);
            checkBoxState[position] = true;
            items.setCheckbox(true);//maintaining boolean check status according to conditions
        } else if (items.getRollBlocked().equals("-")&& items.getRollCheck().equals("-") && items.getRollLate().equals("-")) {
            checkBox.setChecked(false);
            items.setCheckbox(false);
            checkBoxState[position] = false;//maintaining boolean check status according to conditions
        }


        checkBox.setChecked(checkBoxState[position]);//checkbox is finally checked depending upon the local boolean array which u just created according to ur needs
        vi.setTag(checkBox);
        vi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                final Data items = data.get(pos);
                cb = (CheckBox) v.getTag();
                if(items.getRoll().equals(items.getRollBlocked())){

                    if(((CheckBox)v).isChecked()) {
                        checkBoxState[position] = true; //explicitly making it true

                        vi.setEnabled(false);
                        cb.setChecked(true);
                    }
                    else {
                        checkBoxState[position] = false;//explicitly making it false
                    }
                }
                else if(items.getRoll().equals(items.getRollLate())){
                    if(((CheckBox)v).isChecked()) {
                        checkBoxState[position] = true; //explicitly making it true

                        vi.setEnabled(false);
                        cb.setChecked(true);
                    }
                    else {
                        checkBoxState[position] = false;//explicitly making it false
                    }
                }
                else if(items.getRoll().equals(items.getRollCheck())){
                    if(((CheckBox)v).isChecked()) {
                        checkBoxState[position] = true; //explicitly making it true
                        cb.setChecked(false);
                        items.setCheckbox(false);
                    }
                    else {
                        cb.setChecked(true);
                        items.setCheckbox(true);
                        checkBoxState[position] = false;//explicitly making it false
                    }
                }else if (items.getRollBlocked().equals("-") && items.getRollCheck().equals("-") && items.getRollLate().equals("-")){
                    if(((CheckBox)v).isChecked()) {
                        checkBoxState[position] = true; //explicitly making it true
                        cb.setChecked(false);
                        items.setCheckbox(false);
                    }
                    else {
                        cb.setChecked(true);
                        items.setCheckbox(true);
                        checkBoxState[position] = false;//explicitly making it false
                    }
                }else{

                }
            }
        });
        return vi;
    }

    public ArrayList<Data> getAllData(){
        return data;
    }
}

Logcat:

FATAL EXCEPTION: main
Process: , PID: 1366
java.lang.ArrayIndexOutOfBoundsException: length=0; index=0
    at com.mark_attendance.list.adapter.Adapter.getView(Adapter.java:78)
    at android.widget.AbsListView.obtainView(AbsListView.java:2360)
    at android.widget.ListView.measureHeightOfChildren(ListView.java:1326)
    at android.widget.ListView.onMeasure(ListView.java:1233)
    at android.view.View.measure(View.java:19731)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:715)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:461)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6120)
    at android.support.design.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:714)
    at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(HeaderScrollingViewBehavior.java:90)
    at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onMeasureChild(AppBarLayout.java:1391)
    at android.support.design.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:784)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6120)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6120)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6120)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6120)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1464)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:758)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:640)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6120)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:185)
    at com.android.internal.policy.DecorView.onMeasure(DecorView.java:687)
    at android.view.View.measure(View.java:19731)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2271)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1358)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1607)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1246)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6301)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
    at android.view.Choreographer.doCallbacks(Choreographer.java:683)
    at android.view.Choreographer.doFrame(Choreographer.java:619)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6077)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Data .java

public class Data {

    private String roll;
    private String menu;
    private boolean check;
    private String roll_check;
    private String roll_blocked;
    private String roll_late;
    private String studentID;


    private int blockedposition;
    private int blockedposition2;

    public Data() {}

    public Data(String roll,String menu, boolean check,String roll_check,int blockedposition,String roll_blocked,int blockedposition2,String roll_late,String studentID) {
        this.roll=roll;
        this.menu = menu;
        this.check = check;
        this.roll_check = roll_check;
        this.blockedposition=blockedposition;
        this.blockedposition2=blockedposition2;
        this.roll_blocked=roll_blocked;
        this.roll_late=roll_late;
        this.studentID=studentID;
    }

    public String getRoll() {
        return roll;
    }

    public void setRoll(String roll) {
        this.roll = roll;
    }

    //-----------------------Studentid---------------------------------------

    public String getStudentID() {
        return studentID;
    }

    public void setStudentID(String studentID) {
        this.studentID = studentID;
    }

    //--------------------------------------

    //------------------------------------------------------------

    public int getBlockedPosition() {
        return blockedposition;
    }

    public void setBlockedPosition(int blockedposition) {
        this.blockedposition = blockedposition;
    }

    //------------------------------------------------------------

    //------------------------------------------------------------

    public int getBlockedPosition2() {
        return blockedposition2;
    }

    public void setBlockedPosition2(int blockedposition2) {
        this.blockedposition2 = blockedposition2;
    }

    //------------------------------------------------------------

    public String getRollCheck() {
        return roll_check;
    }

    public void setRollCheck(String roll_check) {
        this.roll_check = roll_check;
    }

//------------------------------------------------------------

public String getRollBlocked() {
    return roll_blocked;
}

    public void setRollBlocked(String roll_blocked) {
        this.roll_blocked = roll_blocked;
    }

//-----------------------LATE-----------------------------------------------------

public String getRollLate() {
    return roll_late;
}

    public void setRollLate(String roll_late) {
        this.roll_late = roll_late;
    }

    //--------------------------------------------------------------------------

    public String getMenu() {
        return menu;
    }

    public void setMenu(String menu) {
        this.menu = menu;
    }

    public boolean isCheckbox() {
        return check;
    }

    public void setCheckbox(boolean check) {
        this.check = check;
    }
}

回答1:

I'm going to answer while changing your code as little as possible. Also, I started with your last revision, before you made the changes based on another answer.

However, there may be some issues with your logic. You want to control the checkbox based on both the content of the item and based on user input. That's OK but how do you know which to use? My answer uses the content until the the user clicks the item then it is manual from that point. To go back to using content after a click, there needs to be a way to tell the view to go back to using content. The may be what you are doing with vi.setEnabled(), I wasn't sure.

1st, add this to the Data class. You may re-purpose the the boolean variable you already have but to be safe I just created another one.

    // add these 4 lines near the top
    public static int NO_MANUAL_CHECK = 1;
    public static int MANUAL_CHECK_TRUE = NO_MANUAL_CHECK + 1;
    public static int MANUAL_CHECK_FALSE = MANUAL_CHECK_TRUE + 1;

    int manualCheck;        

    // and the getter & setter
    public int isManualCheck() {
        return manualCheck;
    }

    public void setManualCheck(int check) {
        this.manualCheck = check;
    }

And here's the getView() method, including the OnClickListener().

@Override
public View getView(final int position, View view, ViewGroup viewGroup) {
    vi = view;

    final int pos = position;
    final Data items = data.get(pos);
    vi = inflater.inflate(R.layout.list_row, null);
    checkBox = (CheckBox) vi.findViewById(R.id.cb);
    menu = (TextView) vi.findViewById(R.id.nama_menu);
    roll = (TextView) vi.findViewById(R.id.roll);
    menu.setText(items.getMenu());
    roll.setText(items.getRoll());

    if(items.manualCheck == Data.NO_MANUAL_CHECK) {
        if (items.getRoll().equals(items.getRollBlocked())) {
            checkBox.setChecked(true);
            items.setCheckbox(true);//maintaining boolean check status according to conditions
            notifyDataSetChanged();
            //vi.setEnabled(false);
        } else if (items.getRoll().equals(items.getRollLate())) {
            checkBox.setChecked(true);
            items.setCheckbox(true);
            notifyDataSetChanged();
            //vi.setEnabled(false);
        } else if (items.getRoll().equals(items.getRollCheck())) {
            checkBox.setChecked(true);
            items.setCheckbox(true);//maintaining boolean check status according to conditions


        } else {    /*** notice here I've changed this condition to account for unknown content of item ***/
            checkBox.setChecked(false);
            items.setCheckbox(false);
        }
    } else {
        boolean manualChecked = (items.isManualCheck() == Data.MANUAL_CHECK_TRUE);
        checkBox.setChecked(manualChecked);
    }

    vi.setTag(checkBox);
    vi.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d("TAG", "pos = " + pos);
            final Data items = data.get(pos);
            cb = (CheckBox) v.getTag();

            // toggle manual check
            if(items.isManualCheck() == Data.MANUAL_CHECK_TRUE) {
                items.setManualCheck(Data.MANUAL_CHECK_FALSE);
            } else {
                items.setManualCheck(Data.MANUAL_CHECK_TRUE);
            }
            // you don't need to do all the display logic again, just tell the Adapter & getView() will be called
            notifyDataSetChanged();                     
        }
    });

    return vi;
}

In general, there are a few things that would be better to do another way. In your getView(), you inflate the view every time. See ListView for a more efficient way to do this. Also, it's more common to use OnItemClickListener() for a ListView. If you use a ViewHolder for your ListView, then onClickListener() is better. Anyway, I didn't want to change too much in your code. I hope this solves your problem.



回答2:

Try doing like this,please read the comments as i tried to explain in detail : In ur Data Class just add this Boolean Flag which will be the primary flag on which checked or unchecked will depend :

  public class Data {

        private String roll;
        private String menu;
        private boolean check;
        private String roll_check;
        private String roll_blocked;
        private String roll_late;
        private String studentID;
       public boolean isBoolStorage() {
    return boolStorage;
}

public void setBoolStorage(boolean boolStorage) {
    this.boolStorage = boolStorage;
}

private boolean boolStorage;//Boolean flag is maintained at object level with getters and setters


        private int blockedposition;
        private int blockedposition2;

        public Data() {}

        public Data(String roll,String menu, boolean check,String roll_check,int blockedposition,String roll_blocked,int blockedposition2,String roll_late,String studentID) {
            this.roll=roll;
            this.menu = menu;
            this.check = check;
            this.roll_check = roll_check;
            this.blockedposition=blockedposition;
            this.blockedposition2=blockedposition2;
            this.roll_blocked=roll_blocked;
            this.roll_late=roll_late;
            this.studentID=studentID;
        }

        public String getRoll() {
            return roll;
        }

        public void setRoll(String roll) {
            this.roll = roll;
        }

        //-----------------------Studentid---------------------------------------

        public String getStudentID() {
            return studentID;
        }

        public void setStudentID(String studentID) {
            this.studentID = studentID;
        }

        //--------------------------------------

        //------------------------------------------------------------

        public int getBlockedPosition() {
            return blockedposition;
        }

        public void setBlockedPosition(int blockedposition) {
            this.blockedposition = blockedposition;
        }

        //------------------------------------------------------------

        //------------------------------------------------------------

        public int getBlockedPosition2() {
            return blockedposition2;
        }

        public void setBlockedPosition2(int blockedposition2) {
            this.blockedposition2 = blockedposition2;
        }

        //------------------------------------------------------------

        public String getRollCheck() {
            return roll_check;
        }

        public void setRollCheck(String roll_check) {
            this.roll_check = roll_check;
        }

    //------------------------------------------------------------

    public String getRollBlocked() {
        return roll_blocked;
    }

        public void setRollBlocked(String roll_blocked) {
            this.roll_blocked = roll_blocked;
        }

    //-----------------------LATE-----------------------------------------------------

    public String getRollLate() {
        return roll_late;
    }

        public void setRollLate(String roll_late) {
            this.roll_late = roll_late;
        }

        //--------------------------------------------------------------------------

        public String getMenu() {
            return menu;
        }

        public void setMenu(String menu) {
            this.menu = menu;
        }

        public boolean isCheckbox() {
            return check;
        }

        public void setCheckbox(boolean check) {
            this.check = check;
        }
    }

Now Modify ur data set according to ur conditions by running a basic for-each loop and set boolean status according to ur conditions: Let's assume ur data set is ArrayList datas;

Loop through the data set and place boolean flags wherever if condition succeeds

for(Data data :datas){
 if (data.getRoll().equals(data.getRollBlocked())) {
                   data.setBoolStorage(true);//setting flags to true and false respectively according to conditions and modifying basic data set before sending it to the adapter
                }
                else if (data.getRoll().equals(data.getRollLate())) {
                     data.setBoolStorage(true);

                }

                else if (data.getRoll().equals(data.getRollCheck())) {

                    data.setBoolStorage(true);


                } else if (data.getRollBlocked().equals("-")&& data.getRollCheck().equals("-") && data.getRollLate().equals("-")) {
                     data.setBoolStorage(false);

                }



}
//Now call ur adapter with modified data set
Adapter adapter = new Adapter(MainActivity.this,datas);

And ur adapter class,U have to write like this :

private Context activity;
            private ArrayList<Data> data;
            private static LayoutInflater inflater = null;
            private View vi;
            TextView roll;
            TextView menu;
            CheckBox checkBox;
            CheckBox cb;
              boolean[] checkBoxState;//maintain a local boolean array to store the checked status of checkboxes positionwise
            public Adapter(Context context, ArrayList<Data> items) {
                this.activity = context;
                this.data = items;
     checkBoxState=new boolean[data.size()];//initializing the local boolean array inside the constructor
                inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            }

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

            @Override
            public Object getItem(int i) {
                return data.get(i);
            }

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

            @Override
            public View getView(final int position, View view, ViewGroup viewGroup) {
                vi = view;
                final int pos = position;
                final Data items = data.get(pos);
                vi = inflater.inflate(R.layout.list_row, null);
                checkBox = (CheckBox) vi.findViewById(R.id.cb);
                menu = (TextView) vi.findViewById(R.id.nama_menu);
                roll = (TextView) vi.findViewById(R.id.roll);
                menu.setText(items.getMenu());
                roll.setText(items.getRoll());
                if (items.isBoolStorage()) {
                    checkBox.setChecked(true);
                   checkBoxState[position] = true;
                    items.setCheckbox(true);//maintaining boolean check status according to conditions

                    vi.setEnabled(false);
                }
                else if (items.isBoolStorage()) {
                    checkBox.setChecked(true);
                   checkBoxState[position] = true;//maintaining boolean check status according to conditions
                    items.setCheckbox(true);

                    vi.setEnabled(false);
                }

                else if (items.isBoolStorage()) {

                    checkBox.setChecked(true);
                   checkBoxState[position] = true;
                    items.setCheckbox(true);//maintaining boolean check status according to conditions


                } else if (!items.isBoolStorage()) {
                    checkBox.setChecked(false);
                    items.setCheckbox(false);
                  checkBoxState[position] = false;//maintaining boolean check status according to conditions

                }


              checkBox.setChecked(checkBoxState[position]);//checkbox is finally checked depending upon the local boolean array which u just created according to ur needs
 vi.setTag(checkBox);
                vi.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                        final Data items = data.get(pos);
                          cb = (CheckBox) v.getTag();
                        if(items.getRoll().equals(items.getRollBlocked())){

         if(((CheckBox)v).isChecked()) {
                            checkBoxState[position] = true; //explicitly making it true
                          items.setBoolStorage(true);// changing boolean flag of the object accordingly
                           vi.setEnabled(false);
                            cb.setChecked(true);
                        }
                        else {
                          items.setBoolStorage(false);
                            checkBoxState[position] = false;//explicitly making it false
                        }

                        }
                        else if(items.getRoll().equals(items.getRollLate())){
         if(((CheckBox)v).isChecked()) {
                            checkBoxState[position] = true; //explicitly making it true
                          items.setBoolStorage(true);
                           vi.setEnabled(false);
                            cb.setChecked(true);
                        }
                        else {
                          items.setBoolStorage(false);
                            checkBoxState[position] = false;//explicitly making it false
                        }

                        }
                        else if(items.getRoll().equals(items.getRollCheck())){
             if(((CheckBox)v).isChecked()) {
                           items.setBoolStorage(false);
                            checkBoxState[position] = false; //explicitly making it true
                            cb.setChecked(false);
                                items.setCheckbox(false);
                        }
                        else {
                         items.setBoolStorage(true);
                          cb.setChecked(true);
                                items.setCheckbox(true);
                            checkBoxState[position] = true;//explicitly making it true
                        }


                        }else if (items.getRollBlocked().equals("-") && items.getRollCheck().equals("-") && items.getRollLate().equals("-")){
         if(((CheckBox)v).isChecked()) {
                            checkBoxState[position] = false; //explicitly making it true
                       items.setBoolStorage(false);
                            cb.setChecked(false);
                                items.setCheckbox(false);
                        }
                        else {
                       items.setBoolStorage(true);
                         cb.setChecked(true);
                                items.setCheckbox(true);
                            checkBoxState[position] = true;//explicitly making it false
                        }


                        }else{

                        }



                    }
                });
                return vi;
            }

            public ArrayList<Data> getAllData(){
                return data;
            }


回答3:

Just try to remove checkBox.setChecked() from if clause and set it like checkBox.setChecked(items.getRoll().equals(items.getRollLate())). It should do the job.



回答4:

The issue I can see in your code is that you are not making your Adapter in standard way. Check this code , make your Adapter with this way or better copy and paste your code in this class. It will work fine.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;



import java.util.ArrayList;



 /**
 * Created by Zohaib Hassan on 11/28/2016.
 */

 public class InboxAdapter extends ArrayAdapter<InboxRow> {

 ArrayList<InboxRow> items;
 Context context;


  public InboxAdapter(Context context, int resource, ArrayList<InboxRow> items) {
super(context , resource , items);
this.context = context;
this.items = items;

}



@Override

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

// Get the data item for this position

InboxRow rowItem = getItem(position);

// Check if an existing view is being reused, otherwise inflate the view

ViewHolder viewHolder; // view lookup cache stored in tag

if (convertView == null) {

viewHolder = new ViewHolder();

LayoutInflater inflater = LayoutInflater.from(context);

convertView = inflater.inflate(R.layout.inbox_row, null);

viewHolder.tvUserName = (TextView) convertView.findViewById(R.id.tv_user_name_inbox);
viewHolder.tvMessage = (TextView) convertView.findViewById(R.id.tv_message_inbox);
viewHolder.tvTimeCount = (TextView) convertView.findViewById(R.id.tv_time_count_inbox);
viewHolder.userProfilePic = (CircleImageView) convertView.findViewById(R.id.inbox_profile_image);

convertView.setTag(viewHolder);

} else {

viewHolder = (ViewHolder) convertView.getTag();

}


// Set Your data here!


return convertView;

}

private static class ViewHolder {

TextView tvUserName , tvMessage , tvTimeCount;
CircleImageView userProfilePic;

}

}

The main essential part of this Adapter is ViewHolder class, ViewHolder class will make sure that your values of data array won't mix-up with each other.



回答5:

override this method in adapter class

@Override
public int getItemViewType(int position) {
    return position;

}