RecyclerView Adapter changes the values of the objects for no reason

Viewed 85

I am listening to notifications and therefore I want to create a RecyclerView the user can use to exclude some apps. It shows the logo and the name of the app as well as a toggle button to enable/disable each app.

I created a class App containing some data of each app.

public class App {

    private String name;
    private String packageName;
    private Drawable icon;
    private boolean enabled;

    public App(String name, String packageName, Drawable icon, boolean enabled) {
        this.name = name;
        this.packageName = packageName;
        this.icon = icon;
        this.enabled = enabled;
    }

    public String getName() {
        return name;
    }

    public String getPackageName() {
        return packageName;
    }

    public Drawable getIcon() {
        return icon;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public boolean isEnabled() {
        return enabled;
    }
}

And this is the adapter:

public class AppsAdapter extends RecyclerView.Adapter<AppsAdapter.AppViewHolder> {

    private List<App> apps;

    public AppsAdapter(PackageManager packageManager) {
        apps = packageManager.getInstalledApplications(PackageManager.GET_META_DATA)
                .stream()
                .filter(applicationInfo -> packageManager.getLaunchIntentForPackage(applicationInfo.packageName) != null)
                .map(applicationInfo -> new App(applicationInfo.loadLabel(packageManager).toString(), applicationInfo.packageName, applicationInfo.loadIcon(packageManager), true))
                .collect(Collectors.toList());
    }

    @Override
    public AppViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new AppViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.app_item, parent, false));
    }

    @Override
    public void onBindViewHolder(AppsAdapter.AppViewHolder holder, int position) {
        App app = apps.get(position);

        holder.nameTextView.setText(app.getName());
        holder.appToggleButton.setChecked(app.isEnabled());
        holder.iconImageView.setImageDrawable(app.getIcon());
    
        holder.appToggleButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
            app.setEnabled(isChecked);
        });
    }

    @Override
    public int getItemCount() {
        return apps.size();
    }

    protected static class AppViewHolder extends RecyclerView.ViewHolder {

        private TextView nameTextView;
        private ToggleButton appToggleButton;
        private ImageView iconImageView;

        public AppViewHolder(View itemView) {
            super(itemView);

            nameTextView = itemView.findViewById(R.id.name_text_view);
            appToggleButton = itemView.findViewById(R.id.app_toggle_button);
            iconImageView = itemView.findViewById(R.id.icon_image_view);
        }
    }
}

When I "disable" an app by clicking the button, it seems to be working fine. But when I scroll down in the RecyclerView and then back up so the entry of the app I disabled is shown again on the screen, the app isn't disabled anymore.

Unfortunately, I have no idea how to debug this and what might be causing this issue.

Edit: If I call setOnCheckedChangeListener before setChecked on the toggle button, it seems to be working. I do not understand why.

2 Answers

I see your setting checked change listener. When it is called you are calling app.setEnabled(isChecked) but you are not updating that app in the datasource.

enabble setHasStableIds(true) in your adapter constructor.

AppsAdapter(){
setHasStableIds(true);
}
Related