android-studioandroid-recyclerviewmaterial-designmaterialcardview

How to make Material CardView, Checkable, in RecyclerView


I have made an android note app and I want to make Note Material CardView, Checkable. I want this behavior. When I will long click each note, each note will be checked/selected(checked according to the material design cardview setChecked behavior).

It is a Room persistence database with RecyclerView and implemented notes. Each note layout is a Material CardView. I have an adapter called "NotesAdapter", a Model entity "Note" and the main Activity called "MyNotesActivity". Of course i have some more files, but I havent included them because of not putting the whole project for you to read it all. If you want request them and I'll put them in question for you.

I've tried this, but it doesnt work properly. In NotesAdapter, the line "noteHolder.noteCardView.toggle();"

noteHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    //When long clicking(holding click) on note(note title, note body text, note date).
                    listener.onNoteLongClick(note);

                    noteHolder.noteCardView.toggle();
                    return true;
                }
            });

When I long click it does get selected, but when i scroll down, also does some of the rest of the notes.

ex.

https://gfycat.com/incomparablepowerfullarva

My "NotesAdapter.java" code:

//implements Filterable is for some other feature, that has nothing to do with this question.
public class NotesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {

    public ArrayList<Note> notes;
    public ArrayList<Note> notesFull;
    private Context context;
    private NoteEventListener listener;
    private DeleteThisNoteListener deleteThisNoteListener;

    public NotesAdapter(ArrayList<Note> notes, Context context, DeleteThisNoteListener deleteThisNoteListener) {
        this.notes = notes;
        this.context = context;
        this.deleteThisNoteListener = deleteThisNoteListener;
        notesFull = new ArrayList<>(notes);
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(context).inflate(R.layout.note_layout, parent, false);
        return new NoteHolder(v);
    }

    //Displays data at specified position.
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        final NoteHolder noteHolder = (NoteHolder) holder;
        final Note note = getNote(position);
        if (note != null) {
            //Entering text to each Text fields.
            noteHolder.noteTitle.setText(note.getNoteTitle());
            noteHolder.noteBodyText.setText(note.getNoteBodyText());
         noteHolder.noteDate.setText(NoteUtils.dateFromLong(note.getNoteDate()));

            noteHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    //When long clicking(holding click) on note(note title, note body text, note date).
                    listener.onNoteLongClick(note);

                    noteHolder.noteCardView.toggle();
                    return true;
                }
            });

            noteHolder.moreMenu.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    deleteThisNoteListener.onDeleteThisNoteClick(note, view, noteHolder.getBindingAdapterPosition());
                }
            });
        }
    }

    //Gets the particular note and its position.
    public Note getNote(int position) {
        return notes.get(position);
    }

    //Gets how many notes are there.
    @Override
    public int getItemCount() {
        return notes.size();
    }

    //setListener sets all listeners
    public void setListener(NoteEventListener listener, DeleteThisNoteListener deleteThisNoteListener) {
        this.listener = listener;
        this.deleteThisNoteListener = deleteThisNoteListener;
    }

    static class NoteHolder extends RecyclerView.ViewHolder {
        TextView noteTitle, noteBodyText, noteDate;
        MaterialButton moreMenu;
        MaterialCardView noteCardView;

        public NoteHolder(@NonNull View itemView) {
            super(itemView);
            noteCardView = itemView.findViewById(R.id.note);
            noteTitle = itemView.findViewById(R.id.note_title);
            noteBodyText = itemView.findViewById(R.id.note_body_text);
            noteDate = itemView.findViewById(R.id.note_date);
            moreMenu = itemView.findViewById(R.id.more_menu_button);
        }

    }

}
 

My "MyNotesActivity.java"

public class MyNotesActivity extends AppCompatActivity implements NoteEventListener, DeleteThisNoteListener {

    private MaterialToolbar searchTopBar;
    private MaterialToolbar pageTitleTopBar;
    private MaterialToolbar selectNotesTopBar;
    private NotesDAO dao;
    private NotesAdapter adapter;
    private RecyclerView recyclerView;
    private LinearLayoutManager layoutManager;
    public static final String TAG = "MyNotie";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_notes);
        BottomAppBar bottomAppBar = findViewById(R.id.bottom_app_bar);
        setSupportActionBar(bottomAppBar);

        layoutManager = new LinearLayoutManager(this);
        recyclerView = findViewById(R.id.notes_list);
        recyclerView.setLayoutManager(layoutManager);
        dao = NotesDB.getInstance(this).notesDAO();

        FloatingActionButton add_new_note_button = findViewById(R.id.add_new_note);
        add_new_note_button.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                addNotes("This is a test note.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin et ante quis nibh blandit vehicula non sit amet dui.");
                addNotes("New note!", "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.");
                addNotes("Check this new note!", "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.");
                addNotes("Cool note!", "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
                addNotes("Wrote it all down!", "Nullam imperdiet placerat porttitor. Ut ac urna sed magna gravida eleifend. ");
                loadNotes();
            }
        });

        recyclerView.addOnScrollListener(onScrollListener);
    }

    private void addNotes(String noteTitle, String noteBodyText) {
        for (int i=0; i<2; i++) {
            Note testNote = new Note();
            long date = new Date().getTime();
            testNote.setNoteTitle(noteTitle);
            testNote.setNoteBodyText(noteBodyText);
            testNote.setNoteDate(date);
            dao.insertNote(testNote);
            adapter.notesFull.add(testNote);
            adapter.notes.add(testNote);
        }
    }

    //When resuming the app while on sleep or opening the app this onResume 
    method is called.
    @Override
    protected void onResume() {
        super.onResume();
        loadNotes(); //loads/reloads notes from database.
    }

}

My Model "Note.java":

@Entity(tableName = "notes")
public class Note {
    @PrimaryKey(autoGenerate = true)
    private int id;
    @ColumnInfo(name = "isNewNote")
    private Boolean isNewNote = true;
    @ColumnInfo(name = "noteTitle")
    private String noteTitle;
    @ColumnInfo(name = "text")
    private String noteBodyText;
    @ColumnInfo(name = "date")
    private long noteDate;

    public Note() {
    }

    public String getNoteTitle() {
        return noteTitle;
    }

    public void setNoteTitle(String noteTitle) {
        this.noteTitle = noteTitle;
    }

    public String getNoteBodyText() {
        return noteBodyText;
    }

    public void setNoteBodyText(String noteBodyText) {
        this.noteBodyText = noteBodyText;
    }

    public long getNoteDate() {
        return noteDate;
    }

    public void setNoteDate(long noteDate) {
        this.noteDate = noteDate;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @NonNull
    public Boolean getIsNewNote() {
        return isNewNote;
    }

    public void setIsNewNote(@NonNull Boolean newNote) {
        isNewNote = newNote;
    }

    @NonNull
    @Override
    public String toString() {
        return "Note{" +
                "id=" + id +
                ", noteDate=" + noteDate +
                '}';
    }
}

Please suggest me something, as i couldnt find any good solution that works... Thank you in advance!


Solution

  • Actually I got it! I got the idea from this guy: https://stackoverflow.com/a/40551984/7187975 When he showed his RecyclerViewAdapter and Model and saw this one in RecyclerViewAdapter:

    holder.view.setBackgroundColor(model.isSelected() ? Color.CYAN : Color.WHITE);
            holder.textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    model.setSelected(!model.isSelected());
                    holder.view.setBackgroundColor(model.isSelected() ? Color.CYAN : Color.WHITE);
                }
            });
    

    I did this one: "NotesAdapter.java"

    noteHolder.noteCardView.setChecked(note.isChecked());
                noteHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View view) {
                        //When long clicking(holding click) on note(note title, note body text, 
                        note date).
                        note.setChecked(!note.isChecked());
                        noteHolder.noteCardView.setChecked(note.isChecked());
    
                        return true;
                    }
                });
    

    and added getter, "isChecked" and setter "setChecked" in Model "Note.java"

    public class Note {
    //The rest that are not mentioned, remain unchanged.
    @Ignore
    private boolean isChecked;
    
        public boolean isChecked() {
            return isChecked;
        }
    
        public void setChecked(boolean checked) {
            isChecked = checked;
        }
    }
    

    And that solved it! I hope this will help others! It may not be great, but i hope it will help! Thank you for seeing this post!