Как сохранить состояние флажка Несколько с помощью нажатия кнопки - PullRequest
1 голос
/ 07 мая 2020

В моем приложении у меня есть recyclerView из ArrayList, в котором я установил checkBox рядом с textView. что я хочу сохранить состояние checkBox, нажав кнопку, и я хочу загрузить его в другом действии, и все это должно быть сделано с помощью SharedPrefrennces.

На самом деле я не понимаю, как я могу сохранить состояние checkBox в SharedPrefrennces, нажав кнопку.

ПРИМЕЧАНИЕ : Имейте в виду, я начинаю

Вот код, который показывает, что я сделал ..

public class StudentListActivity extends AppCompatActivity {
private static final String DATETIME = "dateTime";
private RecyclerView recyclerView;
private RecyclerView.Adapter rAdapter;
private RecyclerView.LayoutManager layoutManager;
ArrayList<ListOfNames> listOfNames;


SharedPreferences sharedPreferences;
SharedPreferences.Editor editor;

String t1 = "10";
String t2 = "100%";
Bundle bundle;
String subName;
Toolbar toolbar;
boolean[] items;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.student_list);

    toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    bundle = getIntent().getExtras();
    subName = bundle.getString("Subject Name");
    if (bundle != null) {
        getSupportActionBar().setTitle(subName);
    }
    loadData();


    Calendar calendar = Calendar.getInstance();
    String currentDate = DateFormat.getDateTimeInstance().format(calendar.getTime());


    toolbar.setSubtitle(currentDate);
    fabButton();
    buildRecyclerView();


}

@Override
public void onBackPressed() {
    super.onBackPressed();
    Intent intent = new Intent(this, ScrollingActivity.class);
    intent.putExtra("count", listOfNames.size());
    startActivity(intent);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.changeDateTime:
            handleDate();

            return true;
        case R.id.pastAttendances:
            Intent intent = new Intent(this, PastAttendences.class);

            intent.putExtra("Title", subName);
            sharedPreferences = getSharedPreferences("MY_PREF", MODE_PRIVATE);
            editor = sharedPreferences.edit();

            String result = sharedPreferences.getString(DATETIME, null);



            intent.putExtra("key", result);

            editor.remove(DATETIME);
            editor.apply();
            startActivity(intent);

            return true;

        case R.id.saveButton:

            final String toolbarDateTime = toolbar.getSubtitle().toString();

            sharedPreferences = getSharedPreferences("MY_PREF", MODE_PRIVATE);
            editor = sharedPreferences.edit();
            editor.putString(DATETIME, toolbarDateTime);
            editor.apply();


            return true;
    }

    return super.onOptionsItemSelected(item);
}

public void handelTime(final CharSequence charSequence) {

    final Calendar calendar = Calendar.getInstance();

    int Hour = calendar.get(Calendar.HOUR);
    int Minute = calendar.get(Calendar.MINUTE);

    boolean is24Hour = android.text.format.DateFormat.is24HourFormat(this);

    TimePickerDialog timePickerDialog = new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() {
        @Override
        public void onTimeSet(TimePicker view, int Hour, int Minute) {


            Calendar calendar1 = Calendar.getInstance();

            calendar1.set(Calendar.HOUR, Hour);
            calendar1.set(Calendar.MINUTE, Minute);

            CharSequence charSequence1 = android.text.format.DateFormat.format("HH:mm:ss a", calendar1);
            toolbar.setSubtitle(charSequence + " " + charSequence1);
        }
    }, Hour, Minute, is24Hour);
    timePickerDialog.show();
}

public void handleDate() {

    final Calendar calendar = Calendar.getInstance();

    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int date = calendar.get(Calendar.DATE);

    DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() {
        @Override
        public void onDateSet(DatePicker view, int year, int month, int date) {


            Calendar calendar1 = Calendar.getInstance();

            calendar1.set(Calendar.YEAR, year);
            calendar1.set(Calendar.MONTH, month);
            calendar1.set(Calendar.DATE, date);

            CharSequence charSequence = android.text.format.DateFormat.format("dd-MMM-yyyy", calendar1);

            handelTime(charSequence);
        }
    }, year, month, date);

    datePickerDialog.show();
}


private void buildRecyclerView() {

    recyclerView = findViewById(R.id.recyclerView2);
    recyclerView.hasFixedSize();
    layoutManager = new LinearLayoutManager(StudentListActivity.this);
    rAdapter = new AdapterForStudentList(this, listOfNames);

    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(rAdapter);

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);

}

public void insertData(String text, String t1, String t2) {
    ListOfNames lt = new ListOfNames(text, t1, t2);
    if (listOfNames.contains(lt)) {
        Toast.makeText(this, "Student Name Already exists!!", Toast.LENGTH_SHORT).show();
    } else {
        listOfNames.add(lt);
        rAdapter.notifyDataSetChanged();
    }

}

public void saveData() {
    sharedPreferences = getSharedPreferences("SHARED PREF", MODE_PRIVATE);
    editor = sharedPreferences.edit();

    Gson gson = new Gson();
    String json = gson.toJson(listOfNames);
    editor.putString(subName, json);
    editor.apply();
}

public void loadData() {


    sharedPreferences = getSharedPreferences("SHARED PREF", MODE_PRIVATE);

    Gson gson = new Gson();
    String json = sharedPreferences.getString(subName, null);
    Type type = new TypeToken<ArrayList<ListOfNames>>() {
    }.getType();

    listOfNames = gson.fromJson(json, type);

    if (listOfNames == null) {
        listOfNames = new ArrayList<>();
    }
}


ListOfNames deletedItem = null;
ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
        return false;
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        final int position = viewHolder.getAdapterPosition();
        String name = listOfNames.get(position).getStudentName();

        deletedItem = listOfNames.get(position);
        listOfNames.remove(deletedItem);

        sharedPreferences = getSharedPreferences("SHARED PREF", MODE_PRIVATE);
        editor = sharedPreferences.edit();
        editor.remove("text");


        saveData();
        editor.apply();
        rAdapter.notifyItemRemoved(position);

        Snackbar.make(recyclerView, name + "Deleted", Snackbar.LENGTH_LONG)
                .setAction("Undo", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        listOfNames.add(position, deletedItem);
                        rAdapter.notifyItemInserted(position);

                        saveData();
                    }
                }).show();
    }

    @Override
    public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        new RecyclerViewSwipeDecorator.Builder(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)

                .addSwipeLeftBackgroundColor(ContextCompat.getColor(StudentListActivity.this, R.color.my_background))
                .addSwipeLeftActionIcon(R.drawable.ic_delete_black_24dp)
                .create()
                .decorate();
        super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

    }
};

public void fabButton() {
    FloatingActionButton floatingActionButton = findViewById(R.id.fab2);
    floatingActionButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            AlertDialog.Builder b = new AlertDialog.Builder(StudentListActivity.this);
            View view = getLayoutInflater().inflate(R.layout.dialogbox_frontpage, null);

            final EditText editText = view.findViewById(R.id.editText);
            b.setView(view);
            b.setTitle("Student name");
            b.setPositiveButton("ok", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    String text = editText.getText().toString().trim();

                    if (text.isEmpty()) {
                        Toast.makeText(StudentListActivity.this, "Please add subject name", Toast.LENGTH_SHORT).show();
                    } else {
                        insertData(text, t1, t2);
                        saveData();
                    }
                }
            });
            b.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            });
            b.setCancelable(false);
            b.show();
        }
    });
}

}

Я внес изменения в класс адаптера для сохранения состояния флажка, который работает, но когда я сохраняю состояние флажка, то то, что я проверял ранее, разочаровывает, и когда я удалил код сохранения, который сохраняя мое состояние флажка, тогда он работает нормально, состояние флажка не разочаровывает

На самом деле я не хочу этого, я хочу сохранить состояния флажка при нажатии кнопки и хочу загрузить другое действие, которое это не так. Вы можете объяснить. Думаю, я хорошо описал свою проблему ...

Это AdapterClass для кода выше.

public class AdapterForStudentList extends RecyclerView.Adapter<AdapterForStudentList.StudentViewHolder> {

Context context;
private ArrayList<ListOfNames> mListOfNames;


public static class StudentViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    public TextView studentName, attendence, percentage;
    CheckBox mCheckedBox;
    private ItemClickListener itemClickListener;

    public StudentViewHolder(@NonNull View itemView) {
        super(itemView);
        studentName = itemView.findViewById(R.id.studentName);
        attendence = itemView.findViewById(R.id.attendence);
        percentage = itemView.findViewById(R.id.percentage);
        mCheckedBox = itemView.findViewById(R.id.mCheckBox);
        mCheckedBox.setOnClickListener(this);
    }

    public void setItemClickListener(ItemClickListener ic) {
        itemClickListener = ic;
    }

    @Override
    public void onClick(View v) {
        itemClickListener.onItemClick(v, getLayoutPosition());
    }

    interface ItemClickListener {
        void onItemClick(View v, int position);
    }

}

public AdapterForStudentList(Context context, ArrayList<ListOfNames> listOfNames) {
    this.context = context;
    mListOfNames = listOfNames;
}

@NonNull
@Override
public StudentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view = inflater.inflate(R.layout.check_box_cardview, parent, false);

    SharedPreferences preferences = context.getSharedPreferences("checkedItems", MODE_PRIVATE);
    boolean valueBoolean = preferences.getBoolean("KEY", false);
    SharedPreferences.Editor editor = preferences.edit();
    editor.putBoolean("KEY", valueBoolean);
    editor.apply();

    return new StudentViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull StudentViewHolder holder, final int position) {

    ListOfNames currentItems = mListOfNames.get(position);
    holder.studentName.setText(currentItems.getStudentName());
    holder.attendence.setText(currentItems.getAttendent());
    holder.percentage.setText(currentItems.getPercetage());
    holder.mCheckedBox.setChecked(currentItems.getChecked());


    holder.setItemClickListener(new StudentViewHolder.ItemClickListener() {
        @Override
        public void onItemClick(View v, int position) {
            CheckBox checkBox = (CheckBox) v;

            ListOfNames currentItems = mListOfNames.get(position);

            if (checkBox.isChecked()) {

                currentItems.setChecked(true);

            } else {
                currentItems.setChecked(false);
            }
        }
    });
    holder.mCheckedBox.setChecked(getFromSp("mCheckedBox"+position));
    holder.mCheckedBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            saveInSp("mCheckedBox"+position, isChecked);

        }
    });
}



@Override
public int getItemCount() {
    return mListOfNames.size();
}
private boolean getFromSp(String key){
    SharedPreferences preferences = context.getSharedPreferences("checkedItems", MODE_PRIVATE);
    return preferences.getBoolean(key,false);
}

private void saveInSp(String key, boolean value) {
    SharedPreferences preferences = context.getSharedPreferences("checkedItems", MODE_PRIVATE);
    SharedPreferences.Editor editor = preferences.edit();
    editor.putBoolean(key, value);
    editor.apply();
}

}

1 Ответ

1 голос

Вам не нужно использовать SharedPreferences для сохранения состояний флажков. Вы можете просто использовать метод setChecked внутри своего класса ListOfNames.

Кроме того, вы получаете эту ошибку при добавлении нового элемента, потому что метод onCheckedChanged слушателя CheckBox получает без необходимости вызывается , когда вы вызываете notifyDataSetChanged. Идея заключается в том, что когда вы уведомляете адаптер, он перерабатывает , другими словами, он воссоздает Представления поэтому, если флажки были отмечены, они снова стали не отмеченными, он также может вызвать onCheckedChanged, изменяющий предыдущее значение. (Примечание: я вспомнил, что термин переработать отличается от воссоздать )

Один из способов справиться с этим - использовать вместо него OnClickListener и получить CheckBox оттуда и посмотрите его значение.

Итак, вместо :

holder.mCheckedBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
        // ...
    }
});

Измените его на это:

holder.mCheckedBox.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        CheckBox checkBox = (CheckBox) view;
        boolean isChecked = checkBox.isChecked();
        // ...
    }
});

Это должно решить, что это странное явление не произошло.

Вернитесь к использованию SharedPreferences, как я уже предлагал, используйте метод setChecked вашего ListOfNames класса (см. Код ниже) . Затем удалите все, что связано с SharedPreferences внутри вашего адаптера. И поскольку вы не добавляете новые элементы и не удаляете какие-либо флажки, которые вы устанавливаете, он не будет сохранен, пока пользователь не нажмет кнопку сохранения в вашем меню. В котором вы можете просто использовать свой saveData метод вашего StudentListActivity для кнопки сохранения.

Также удалите интерфейс ItemClickListener и implements View.OnClickListener внутри вашего ViewHolder. Это довольно избыточно.

Ваш AdapterForStudentList теперь должен выглядеть так:

public class AdapterForStudentList extends RecyclerView.Adapter<AdapterForStudentList.StudentViewHolder> {

    private Context context;
    private ArrayList<ListOfNames> mListOfNames;

    public static class StudentViewHolder extends RecyclerView.ViewHolder {
        public TextView studentName, attendence, percentage;
        CheckBox mCheckedBox;

        public StudentViewHolder(@NonNull View itemView) {
            super(itemView);
            studentName = itemView.findViewById(R.id.studentName);
            attendence = itemView.findViewById(R.id.attendence);
            percentage = itemView.findViewById(R.id.percentage);
            mCheckedBox = itemView.findViewById(R.id.mCheckBox);
        }

    }

    public AdapterForStudentList(Context context, ArrayList<ListOfNames> listOfNames) {
        this.context = context;
        mListOfNames = listOfNames;
    }

    @NonNull
    @Override
    public StudentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View view = inflater.inflate(R.layout.check_box_cardview, parent, false);
        return new StudentViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull StudentViewHolder holder, final int position) {
        final ListOfNames currentItems = mListOfNames.get(position);
        holder.studentName.setText(currentItems.getStudentName());
        holder.attendence.setText(currentItems.getAttendent());
        holder.percentage.setText(currentItems.getPercetage());
        holder.mCheckedBox.setChecked(currentItems.getChecked());

        holder.mCheckedBox.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                CheckBox checkBox = (CheckBox) view;
                boolean isChecked = checkBox.isChecked();
                currentItems.setChecked(isChecked);
            }
        });
    }

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



Примечание: Сделайте не забудьте реализовать ограничения, которые не позволят пользователю добавлять или удалять элементы. Дело не в том, что будет грандиозное исключение, скорее оно сделает вашу функцию сохранения бесполезной .

Кроме того, у меня создалось впечатление, что вы хотели иметь возможность добавлять / удалять элементы, пока также возможность устанавливать / снимать флажки. Решение, которое я придумал, - это для создания другого списка, который будет содержать позиции отмеченных флажков , а затем использовать его, когда пользователь нажимает кнопку сохранения. (См. Обновленный пост ниже) Это должно дать вам как возможность добавлять / удалять, так и отмечать / снимать отметки, при этом еще не сохраняя проверки, пока пользователь не сделает это. (Если вы этого хотите, я могу сделать код и поместить его сюда)

И последнее, если вы поставите ограничения, я вижу без проблем, используя вместо OnCheckedChangedListener реализации, которую я сделал выше, потому что я не чувствую, что вы будете звонить notifyDataSetChanged где-либо еще, кроме того, когда элемент был добавлен / удален.

Надеюсь, я кое-что прояснил.



Обновление: реализовать как возможность изменения списка, так и возможность сохранять состояния флажков только при действии пользователя

Решение, которое я дал вам ранее для создания списка для размещения позиций отмеченных элементов, - это на самом деле не очень хорошая идея . Что если убрать из списка? Или, что еще хуже, что, если мы отсортируем список?

Я думаю, что это лучший подход: создать временную переменную, которая хранит временное состояние флажка и передает его значение, только когда пользователь нажимает при сохранении. Временные переменные не сериализуются / не включаются в строку json, когда мы конвертируем ее с помощью Gson.

Итак, внутри вашего класса ListOfNames я предлагаю создать этот переходный переменная. И используйте это для временной проверки / снятия отметки. Сохраняйте его в SharedPreferences только тогда, когда пользователь действительно этого хочет.

Ваш ListOfNames должен выглядеть так:

public class ListOfNames {
    // variables...

    // Create a transient variable to hold the temporary state of the check box
    private transient boolean tempChecked;

    // constructor and other methods...

    // Couple of methods for operations on temporary checks and actual checks
    public void setTemporaryChecked(boolean checked) {
        tempChecked = checked;
    }

    public boolean getTemporaryChecked() {
        return tempChecked;
    }

    public void checkedToTemporaryChecked() {
        tempChecked = checked;
    }

    public void temporaryCheckedToChecked() {
        checked = tempChecked;
    }
}

Внутри слушателя для CheckBox в onBindViewHolder:

// Get the temporary checked value instead of the actual one
// (Because you know, RecyclerView recycles views)
holder.mCheckedBox.setChecked(currentItems.getTemporaryChecked());

holder.mCheckedBox.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        CheckBox checkBox = (CheckBox) view;
        boolean isChecked = checkBox.isChecked();
        // Set the temporary checked instead
        currentItems.setTemporaryChecked(isChecked);
    }
});

Внутри вашего loadData метода:

public void loadData() {
    // ...

    // Set our temporary checks
    // Important because transient variables don't get included in Json string
    for (ListOfNames current: listOfNames)
        current.checkedToTemporaryChecked();
}

Наконец внутри вашего сохранить в меню код logi c ( НЕ ВНУТРИ ваш saveData метод):

case R.id.saveButton:
    // ...

    // Convert temporary checks to actual checks
    for (ListOfNames current: listOfNames)
        current.temporaryCheckedToChecked();
    saveData();

    return true;
// ...

И готово! Вы должны иметь возможность добавлять, удалять или даже сортировать список (если вы хотите добавить эту функцию позже), не затрагивая флажки, а также сохранять значения этих флажков только тогда, когда это делает пользователь.

...