해당 포스팅에서는 기존 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를 보게 됩니다.
해당 부분을 좀 더 부드럽게 만드는 작업을 다음 포스팅에서 진행하도록 하겠습니다.
'Android > study_til' 카테고리의 다른 글
[AOS] Room + ViewModel + LiveData + RecyclerView (추가) (0) | 2021.06.29 |
---|---|
[AOS] Room + ViewModel + LiveData + RecyclerView (5) (0) | 2021.06.28 |
[AOS] Room + ViewModel + LiveData + RecyclerView (3) (1) | 2021.06.28 |
[AOS] Room + ViewModel + LiveData + RecyclerView (2) (0) | 2021.06.28 |
[AOS] Room + ViewModel + LiveData + RecyclerView (1) (0) | 2021.06.28 |