0

So I have a button set in a listview to save in sharedpreferences its checked state when checked/unchecked and then when it's loading the view it'll automatically set the check state based on whether it's saved-as checked or un-checked, but some seem to un-check themselves or never toggle at all. It's fairly inconsistent.

Here's my ListViewAdapter

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View ListItem = convertView;
        Holder holder;

        if (ListItem == null) {

            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            ListItem = inflater.inflate(layoutResourceId, parent, false);

            holder = new Holder();
    //            holder.description = (TextView) ListItem.findViewById(R.id.deityDesc);
                holder.banner = (ImageView) ListItem.findViewById(R.id.deityBanner);
                holder.website = (Button) ListItem.findViewById(R.id.deityWebsite);
                holder.active = (ToggleButton) ListItem.findViewById(R.id.isActiveButtonToggle);
    //            holder.background = (RelativeLayout) ListItem.findViewById(R.id.listItemBG);
                holder.highlight =  (ImageView) ListItem.findViewById(R.id.highlight);
    //            holder.highlight2 = (ImageView) ListItem.findViewById(R.id.highlight2);
                holder.title = (TextView) ListItem.findViewById(R.id.titleText);

            ListItem.setTag(holder);

        } else {
            holder = (Holder) ListItem.getTag();
        }
        final deities deity = data[position];
        SharedPreferences sharedPref = ((Activity) context).getPreferences(Context.MODE_PRIVATE);
        deity.active = sharedPref.getBoolean(Integer.toString(deity.id), false);
        notifyDataSetChanged();

        View.OnClickListener holderListener = new View.OnClickListener() {
            public void onClick(View v) {
                WebView webView = (WebView) ((Activity) context).findViewById(R.id.webView);
                webView.setVisibility(View.VISIBLE);
                webView.loadUrl(deity.url);
            }
        };

        CompoundButton.OnCheckedChangeListener ToggleButtonListener = new CompoundButton.OnCheckedChangeListener() {
            @NonNull
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//                int position = (int) buttonView.getParent().getT();
//                deities deity = data[position];
                deity.active = isChecked;
                SharedPreferences sharedPref = ((Activity) context).getPreferences(Context.MODE_PRIVATE);
                SharedPreferences.Editor prefeditor = sharedPref.edit();
                TextView deityActiveCount = (TextView) ((Activity) context).findViewById(R.id.deitySelectedCounter);
                final CompoundButton Button = buttonView;
                final RelativeLayout r = (RelativeLayout) ((ViewGroup) Button.getParent()).getParent();
                prefeditor.putBoolean(Integer.toString(deity.id), isChecked);
                prefeditor.apply();
                setCounter(isChecked ? getCounter() + 1 : getCounter() - 1);
                ViewGroup ListItem = (ViewGroup) buttonView.getParent();
                int startColor = isChecked ? Color.parseColor("#ce5a5a") : Color.parseColor("#2e7d32");
                int endColor = isChecked ? Color.parseColor("#2e7d32") : Color.parseColor("#ce5a5a");
                ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor);
                    colorAnimation.setDuration(100);
                    colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                        @Override
                        public void onAnimationUpdate(ValueAnimator animation) {

                            ImageView sideThing1 = (ImageView) r.findViewById(R.id.highlight);
//                            ImageView sideThing2 = (ImageView) r.findViewById(R.id.highlight2);
                            sideThing1.setBackgroundColor((int) animation.getAnimatedValue());
//                            sideThing2.setBackgroundColor((int) animation.getAnimatedValue());
                            Button.setBackgroundColor((int) animation.getAnimatedValue());

                        }
                    });
                colorAnimation.start();
                notifyDataSetChanged();

            }
        };


        if (deity.active) {
            holder.active.setChecked(true);
            holder.active.setBackgroundColor(Color.parseColor("#2e7d32"));
            holder.highlight.setBackgroundColor(Color.parseColor("#2e7d32"));
//            holder.highlight2.setBackgroundColor(Color.parseColor("#2e7d32"));


        } else {
            holder.active.setChecked(false);
            holder.active.setBackgroundColor(Color.parseColor("#ce5a5a"));
            holder.highlight.setBackgroundColor(Color.parseColor("#ce5a5a"));
//            holder.highlight2.setBackgroundColor(Color.parseColor("#ce5a5a"));
        }


        holder.active.setOnCheckedChangeListener(ToggleButtonListener);
        holder.website.setOnClickListener(holderListener);
        holder.banner.setImageResource(deity.banner);
//        holder.description.setText(deity.description);
        holder.banner.setBackgroundColor(Color.parseColor("#616161"));
//        holder.description.setText(deity.url);
        holder.title.setText(deity.title + " " + deity.active);
        return ListItem;

    }

This is what I mean by inconsistent (click me, it's a video)

Notice how some revert back/don't save?

A day later, I still can't figure it out.

343N
  • 296
  • 2
  • 4
  • 19
  • The data itself does not get updated. where are you calling notifyDataSetChanged() – zombie Nov 22 '16 at 09:35
  • I would suggest to remove the OnCheckedChangeListener, make the CompoundButton clickable false and handle it through a on item click on the list – zombie Nov 22 '16 at 09:38
  • oh boy, that's what I was missing! Thanks. If you post it as a solution I'll be happy to give you the correct answer. – 343N Nov 22 '16 at 09:39

2 Answers2

2

Try this:

@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
    View ListItem = convertView;
    Holder holder;

    if (ListItem == null) {

        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        ListItem = inflater.inflate(layoutResourceId, parent, false);

        holder = new Holder();
        //            holder.description = (TextView) ListItem.findViewById(R.id.deityDesc);
        holder.banner = (ImageView) ListItem.findViewById(R.id.deityBanner);
        holder.website = (Button) ListItem.findViewById(R.id.deityWebsite);
        holder.active = (ToggleButton) ListItem.findViewById(R.id.isActiveButtonToggle);
        //            holder.background = (RelativeLayout) ListItem.findViewById(R.id.listItemBG);
        holder.highlight = (ImageView) ListItem.findViewById(R.id.highlight);
        //            holder.highlight2 = (ImageView) ListItem.findViewById(R.id.highlight2);
        holder.title = (TextView) ListItem.findViewById(R.id.titleText);

        ListItem.setTag(holder);

    } else {
        holder = (Holder) ListItem.getTag();
    }
    final deities deity = data[position];

    View.OnClickListener holderListener = new View.OnClickListener() {
        public void onClick(View v) {
            WebView webView = (WebView)((Activity) context).findViewById(R.id.webView);
            webView.setVisibility(View.VISIBLE);
            webView.loadUrl(deity.url);
        }
    };

    CompoundButton.OnCheckedChangeListener ToggleButtonListener = new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

            int position = (int) buttonView.getTag();

            final deities deity = data[position];

            SharedPreferences sharedPref = ((Activity) context).getPreferences(Context.MODE_PRIVATE);
            SharedPreferences.Editor prefeditor = sharedPref.edit();
            final CompoundButton Button = buttonView;
            final RelativeLayout r = (RelativeLayout) Button.getParent().getParent();

            deity.active = isChecked;
            prefeditor.putBoolean(Integer.toString(deity.id), isChecked);
            prefeditor.apply();

            int startColor = isChecked ? Color.parseColor("#ce5a5a") : Color.parseColor("#ce5a5a");
            int endColor = isChecked ? Color.parseColor("#2e7d32") : Color.parseColor("#2e7d32");

            ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), startColor, endColor);
            colorAnimation.setDuration(100);
            colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {

                    ImageView sideThing1 = (ImageView) r.findViewById(R.id.highlight);
                    //                            ImageView sideThing2 = (ImageView) r.findViewById(R.id.highlight2);
                    sideThing1.setBackgroundColor((int) animation.getAnimatedValue());
                    //                            sideThing2.setBackgroundColor((int) animation.getAnimatedValue());
                    Button.setBackgroundColor((int) animation.getAnimatedValue());

                }
            });
            colorAnimation.start();

            notifyDataSetChanged();
        }
    };

    SharedPreferences sharedPref = ((Activity) context).getPreferences(Context.MODE_PRIVATE);
    deity.active = sharedPref.getBoolean(Integer.toString(deity.id), false);

    holder.active.setChecked(deity.active);

    if (deity.active) {
        holder.active.setBackgroundColor(Color.parseColor("#2e7d32"));
        holder.highlight.setBackgroundColor(Color.parseColor("#2e7d32"));
    } else {
        holder.active.setBackgroundColor(Color.parseColor("#ce5a5a"));
        holder.highlight.setBackgroundColor(Color.parseColor("#ce5a5a"));
    }

    holder.active.setTag(position);

    holder.active.setOnCheckedChangeListener(ToggleButtonListener);
    holder.website.setOnClickListener(holderListener);
    holder.banner.setImageResource(deity.banner);
    //        holder.description.setText(deity.description);
    holder.banner.setBackgroundColor(Color.parseColor("#616161"));
    //        holder.description.setText(deity.url);
    holder.title.setText(deity.title + " " + deity.active);
    return ListItem;
}

PS: I tried to fix the problem without a lot of changes so this might not be the best answer

zombie
  • 5,069
  • 3
  • 25
  • 54
  • Thanks. Thanks especially for showing my obvious mistake of making an if statement for the true/false isChecked thing, I really don't know why I did that – 343N Nov 22 '16 at 10:47
  • So, by using the declaration of position/diety in the getView method I've been getting crashes for attempting to invoke Integer.intValue() on a null object reference. also for some weird reason my notifyDataSetChanged stopped fixing the problem and it's broke again. I've updated OP – 343N Nov 22 '16 at 10:55
0

tl;dr: Make sure your ToggleButton's setOnCheckedChangedListener is set before calling setChecked. This issue is talked about more here (click me).

To fix this specific issue (although thanks zombie for pointing out notifyDataSetChanged's importance), I moved

holder.active.setOnCheckedChangedListener(ToggleButtonListener);

to before

holder.active.setChecked(diety.active);

since apparently this is a common issue with ListViews, where onChecked doesn't work properly if the listener is infront of setChecked.

343N
  • 296
  • 2
  • 4
  • 19