춤추는 개발자

[AOS] Room + ViewModel + LiveData + RecyclerView (4) 본문

Android/study_til

[AOS] Room + ViewModel + LiveData + RecyclerView (4)

Heon_9u 2021. 6. 28. 19:11
728x90
반응형

 해당 포스팅에서는 기존 Note를 클릭하여 편집하는 기능과 Swipe를 통해 삭제하는 기능을 구현합니다.

 

 Note를 클릭하여 Activity로 전환되는 이벤트 처리와 함께, Note를 추가하는데 활용한 AddNoteActivity를 재활용할 예정입니다. AddNoteActivity에 Edit 기능을 추가함으로써 AddEditNoteActivity로 업데이트하고, 각 작업을 REQUEST_CODE를 통해 분기하여 처리합니다.

 

1. NoteAdapter

 기존 코드와 비교하여 추가된 코드를 작성했습니다. getNoteAt을 통해 position에 따라 수정할 Note 객체를 반환하며, NoteHolder에서는 ClickListener를 구현합니다. 

 

package com.heon9u.aacproject;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.NoteHolder> {
    
    public Note getNoteAt(int position) {
        return notes.get(position);
    }

    class NoteHolder extends RecyclerView.ViewHolder {
        private TextView textViewTitle;
        private TextView textViewDescription;
        private TextView textViewPriority;
        public NoteHolder(@NonNull View itemView) {
            super(itemView);
            textViewTitle = itemView.findViewById(R.id.text_view_title);
            textViewDescription = itemView.findViewById(R.id.text_view_description);
            textViewPriority = itemView.findViewById(R.id.text_view_priority);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null && position != RecyclerView.NO_POSITION) {
                        listener.onItemClick(notes.get(position));
                    }
                }
            });
        }
    }

    public interface OnItemClickListener {
        void onItemClick(Note note);
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        this.listener = listener;
    }
}

 

2. MainActivity

 buttonAddNote의 onClick 메서드에서 기존 AddNoteActivity를 클릭하여 shift + F6을 누릅니다. 이때 해당 Activity를 변경하여 엔터를 누르면, Package에 포함된 AddNoteActivity가 모두 변경됩니다.

 

 이를 AddEditNoteActivity로 변경 후, ItemTouchHelper 객체를 생성하여 onMove와 onSwiped를 오버라이딩합니다. 이때 (ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)는 List에 담긴 View를 왼쪽 또는 오른쪽으로 Swipe할 수 있게 해줍니다.

 

package com.heon9u.aacproject;

...

public class MainActivity extends AppCompatActivity {
    public static final int ADD_NOTE_REQUEST = 1;
    public static final int EDIT_NOTE_REQUEST = 2;

    private NoteViewModel noteViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FloatingActionButton buttonAddNote = findViewById(R.id.button_add_note);
        buttonAddNote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, AddEditNoteActivity.class);
                startActivityForResult(intent, ADD_NOTE_REQUEST);
            }
        });
        
        ...
        
        new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,
                ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
            @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) {
                noteViewModel.delete(adapter.getNoteAt(viewHolder.getAdapterPosition()));
                Toast.makeText(MainActivity.this, "Note deleted", Toast.LENGTH_SHORT).show();
            }
        }).attachToRecyclerView(recyclerView);

        adapter.setOnItemClickListener(new NoteAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(Note note) {
                Intent intent = new Intent(MainActivity.this, AddEditNoteActivity.class);
                intent.putExtra(AddEditNoteActivity.EXTRA_ID, note.getId());
                intent.putExtra(AddEditNoteActivity.EXTRA_TITLE, note.getTitle());
                intent.putExtra(AddEditNoteActivity.EXTRA_DESCRIPTION, note.getDescription());
                intent.putExtra(AddEditNoteActivity.EXTRA_PRIORITY, note.getPriority());
                startActivityForResult(intent, EDIT_NOTE_REQUEST);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == ADD_NOTE_REQUEST && resultCode == RESULT_OK) {
        
            ...
            
        } else if (requestCode == EDIT_NOTE_REQUEST && resultCode == RESULT_OK) {
            int id = data.getIntExtra(AddEditNoteActivity.EXTRA_ID, -1);

            if (id == -1) {
                Toast.makeText(this, "Note can't be updated", Toast.LENGTH_SHORT).show();
                return;
            }

            String title = data.getStringExtra(AddEditNoteActivity.EXTRA_TITLE);
            String description = data.getStringExtra(AddEditNoteActivity.EXTRA_DESCRIPTION);
            int priority = data.getIntExtra(AddEditNoteActivity.EXTRA_PRIORITY, 1);

            Note note = new Note(title, description, priority);
            note.setId(id);
            noteViewModel.update(note);

            Toast.makeText(this, "Note updated", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "Note not saved", Toast.LENGTH_SHORT).show();
        }
    }
} 

 

 

코드가 길기 때문에 파트별로 알아보도록 하겠습니다.

adapter.setOnItemClickListener(new NoteAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(Note note) {
        Intent intent = new Intent(MainActivity.this, AddEditNoteActivity.class);
        intent.putExtra(AddEditNoteActivity.EXTRA_ID, note.getId());
        intent.putExtra(AddEditNoteActivity.EXTRA_TITLE, note.getTitle());
        intent.putExtra(AddEditNoteActivity.EXTRA_DESCRIPTION, note.getDescription());
        intent.putExtra(AddEditNoteActivity.EXTRA_PRIORITY, note.getPriority());
        startActivityForResult(intent, EDIT_NOTE_REQUEST);
    }
});

 setOnItemClickListener에서는 해당 Note를 클릭하면 페이지가 전환되며, 전환된 페이지에서 나타내야할 데이터를 putExtra()로 전송합니다. buttonAddNote와의 차이점은 REQUEST_CODE입니다. 전역변수를 확인해보면 ADD_NOTE_REQUEST는 1, EDIT_NOTE_REQUEST는 2로 표현했습니다. 각 변수를 static으로 선언했기 때문에 다른 Activity에서도 사용이 가능합니다.

 

 

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ADD_NOTE_REQUEST && resultCode == RESULT_OK) {

        ...

    } else if (requestCode == EDIT_NOTE_REQUEST && resultCode == RESULT_OK) {
        int id = data.getIntExtra(AddEditNoteActivity.EXTRA_ID, -1);

        if (id == -1) {
            Toast.makeText(this, "Note can't be updated", Toast.LENGTH_SHORT).show();
            return;
        }

        String title = data.getStringExtra(AddEditNoteActivity.EXTRA_TITLE);
        String description = data.getStringExtra(AddEditNoteActivity.EXTRA_DESCRIPTION);
        int priority = data.getIntExtra(AddEditNoteActivity.EXTRA_PRIORITY, 1);

        Note note = new Note(title, description, priority);
        note.setId(id);
        noteViewModel.update(note);

        Toast.makeText(this, "Note updated", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, "Note not saved", Toast.LENGTH_SHORT).show();
    }
}

 onActivityResult는 Add와 Edit에 따라 조건문으로 분기했습니다. 이를 통해 변환된 Note를 DB에 갱신할 수 있습니다. 또한, id값에 따라 예외 처리를 하였고, 해당 부분은 AddEditNoteActivity의 수정 사항을 보며 다시 한번 확인하겠습니다.

 

 

3. AddEditNoteActivity

 기존 코드와 다르게 Add와 Edit를 구분할 수 있는 EXTRA_ID를 사용하였습니다. 이를 통해 setTitle과 saveNote() 메서드를 분기하였습니다. Note의 id는 PrimaryKey이기 때문에 변하지 않습니다.

 saveNote() 메서드에서 지역변수 id를 만들었으며 만약, 해당 Activity를 호출할 때, 사용한 Intent에서 EXTRA_ID가 없는 경우, -1을 id에 저장합니다.

 

public class AddEditNoteActivity extends AppCompatActivity {

    public static final String EXTRA_ID =
            "com.heon9u.aacproject.EXTRA_ID";
    public static final String EXTRA_TITLE =
            "com.heon9u.aacproject.EXTRA_TITLE";
    public static final String EXTRA_DESCRIPTION =
            "com.heon9u.aacproject.EXTRA_DESCRIPTION";
    public static final String EXTRA_PRIORITY =
            "com.heon9u.aacproject.EXTRA_PRIORITY";
    private EditText editTextTitle;
    private EditText editTextDescription;
    private NumberPicker numberPickerPriority;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_note);
        editTextTitle = findViewById(R.id.edit_text_title);
        editTextDescription = findViewById(R.id.edit_text_description);
        numberPickerPriority = findViewById(R.id.number_picker_priority);
        numberPickerPriority.setMinValue(1);
        numberPickerPriority.setMaxValue(10);

        getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close);
        setTitle("Add Note");

        Intent intent = getIntent();

        if (intent.hasExtra(EXTRA_ID)) {
            setTitle("Edit Note");
            editTextTitle.setText(intent.getStringExtra(EXTRA_TITLE));
            editTextDescription.setText(intent.getStringExtra(EXTRA_DESCRIPTION));
            numberPickerPriority.setValue(intent.getIntExtra(EXTRA_PRIORITY, 1));
        } else {
            setTitle("Add Note");
        }
    }

    private void saveNote() {
        String title = editTextTitle.getText().toString();
        String description = editTextDescription.getText().toString();
        int priority = numberPickerPriority.getValue();

        if (title.trim().isEmpty() || description.trim().isEmpty()) {
            Toast.makeText(this, "Please insert title and description", Toast.LENGTH_SHORT).show();
            return;
        }
        
        Intent data = new Intent();
        data.putExtra(EXTRA_TITLE, title);
        data.putExtra(EXTRA_DESCRIPTION, description);
        data.putExtra(EXTRA_PRIORITY, priority);

        int id = getIntent().getIntExtra(EXTRA_ID, -1);
        if (id != -1) {
            data.putExtra(EXTRA_ID, id);
        }

        setResult(RESULT_OK, data);
        finish();
    }
} 

 

 

 다음 포스팅에서는 NoteAdapter에서 ListAdapter와 DiffUtil를 활용한 애니메이션을 추가합니다. 기존 NoteAdapter의 setNotes 메서드에서 notifyDataSetChanged();를 사용하는데, 이는 전체 데이터 셋을 갱신하는 역할을 합니다. 이로 인해 List에서 데이터 삭제 시, 딱딱한(?) UI를 보게 됩니다.

 해당 부분을 좀 더 부드럽게 만드는 작업을 다음 포스팅에서 진행하도록 하겠습니다.

728x90
반응형