









Preview text:
  lOMoAR cPSD| 60752940
Thực hành ngày 09/092025  
Xây dựng ứng dụng Quản lý Danh sách Ghi chú (Notes App) với Android Jetpack     Mục tiêu   • 
Làm quen với kiến trúc MVVM trong Android Jetpack.  • 
Thực hành các thành phần: Room, ViewModel, LiveData, RecyclerView.  • 
Biết cách tách biệt UI – ViewModel – Repository – Database. 
Yêu cầu chức năng   1. 
Người dùng nhập ghi chú mới (Note).  2. 
Lưu ghi chú vào Room Database.  3. 
Hiển thị toàn bộ ghi chú bằng RecyclerView.  4. 
Dữ liệu hiển thị tự động cập nhật khi thêm/sửa/xóa nhờ LiveData.  5. 
Có nút Xóa toàn bộ ghi chú. Cấu trúc Project app/ 
 ├── MainActivity.java // UI chính: nhập ghi chú, hiển thị danh sách   ├── adapter/ 
 │ └── NoteAdapter.java // Adapter cho RecyclerView   ├── data/ 
 │ ├── Note.java // Entity 
 │ ├── NoteDao.java // DAO 
 │ ├── NoteDatabase.java // Room Database 
 │ └── NoteRepository.java // Repository   └── viewmodel/ 
 └── NoteViewModel.java // ViewModel + LiveData 
1. Layouts activity_main.xml  
android:layout_width="match_parent" android:layout_height="match_parent" 
android:orientation="vertical" android:padding="16dp">        lOMoAR cPSD| 60752940
 android:hint="Nhập ghi chú..." 
android:layout_width="match_parent" 
android:layout_height="wrap_content"/>   
 android:text="Thêm ghi chú" 
android:layout_width="match_parent" 
android:layout_height="wrap_content"/>   
 android:text="Xóa tất cả" 
android:layout_width="match_parent" 
android:layout_height="wrap_content"/>   
 android:id="@+id/recyclerView" 
android:layout_width="match_parent"  android:layout_height="0dp" 
android:layout_weight="1"/> 
2. Tạo Entity (Note.java)  
@Entity(tableName = "notes") public  class Note { 
 @PrimaryKey(autoGenerate = true)   public int id;   
 @ColumnInfo(name = "content")  public String content;        lOMoAR cPSD| 60752940
 public Note(String content) {  this.content = content;   }  }   
3. Tạo DAO (NoteDao.java)   @Dao  public interface NoteDao {   @Insert void  insert(Note note);   
 @Query("SELECT * FROM notes ORDER BY id DESC")   LiveData> getAllNotes();     @Query("DELETE FROM notes")   void deleteAll();  }   
4. Room Database (NoteDatabase.java)  
@Database(entities = {Note.class}, version = 1) 
public abstract class NoteDatabase extends RoomDatabase { private 
static NoteDatabase instance; public abstract NoteDao noteDao(); 
public static synchronized NoteDatabase getInstance(Context context) { 
if (instance == null) { instance = 
Room.databaseBuilder(context.getApplicationContext(), 
 NoteDatabase.class, "note_database") 
 .fallbackToDestructiveMigration()   .build();      lOMoAR cPSD| 60752940  } return  instance;   }  }   
5. Repository (NoteRepository.java) 
public class NoteRepository { private  NoteDao noteDao; private  LiveData> allNotes;     public  NoteRepository(Application  application)  { 
NoteDatabase db = NoteDatabase.getInstance(application); 
noteDao = db.noteDao(); allNotes = noteDao.getAllNotes();   }   
 public void insert(Note note) { 
 Executors.newSingleThreadExecutor().execute(() -> noteDao.insert(note));   }     public void deleteAll() { 
 Executors.newSingleThreadExecutor().execute(() -> noteDao.deleteAll());   }   
 public LiveData> getAllNotes() {   return allNotes;   }  } 
6. ViewModel (NoteViewModel.java) public class 
NoteViewModel extends AndroidViewModel { private      lOMoAR cPSD| 60752940
NoteRepository repository; private  LiveData> allNotes;   
 public NoteViewModel(@NonNull Application application) { 
 super(application); repository = new 
NoteRepository(application); allNotes =  repository.getAllNotes();   }   
 public void insert(Note note) {  repository.insert(note);   }     public void deleteAll() {  repository.deleteAll();   }   
 public LiveData> getAllNotes() {   return allNotes;   }  }    7.  Adapter  (NoteAdapter.java)  public  class  NoteAdapter  extends 
RecyclerView.Adapter { private List notes = new  ArrayList<>();   
 public void setNotes(List notes) {  this.notes = notes;  notifyDataSetChanged();      lOMoAR cPSD| 60752940  }   
 @NonNull @Override public NoteViewHolder onCreateViewHolder(@NonNull 
ViewGroup parent, int viewType) { 
 View v = LayoutInflater.from(parent.getContext()).inflate(android.R.layout.simple_list_item_1, 
parent, false); return new NoteViewHolder(v);   }   
 @Override public void onBindViewHolder(@NonNull NoteViewHolder holder, 
int position) { holder.textView.setText(notes.get(position).content);   }     @Override public int  getItemCount() { return  notes.size();   }   
 static class NoteViewHolder extends RecyclerView.ViewHolder {   TextView textView; 
 NoteViewHolder(View itemView) {  super(itemView); textView = 
itemView.findViewById(android.R.id.text1);   }   }  }    
8. MainActivity.java public class MainActivity extends 
AppCompatActivity { private NoteViewModel      lOMoAR cPSD| 60752940
noteViewModel; private EditText edtNote; private  NoteAdapter adapter;   
 @Override protected void onCreate(Bundle  savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.activity_main);   
 edtNote = findViewById(R.id.edtNote); 
 Button btnAdd = findViewById(R.id.btnAdd); 
 Button btnClear = findViewById(R.id.btnClear); 
 RecyclerView recyclerView = findViewById(R.id.recyclerView);     adapter = new NoteAdapter(); 
recyclerView.setLayoutManager(new LinearLayoutManager(this)); 
recyclerView.setAdapter(adapter);   
 noteViewModel = new ViewModelProvider(this).get(NoteViewModel.class);   
 noteViewModel.getAllNotes().observe(this, notes -> adapter.setNotes(notes));   
 btnAdd.setOnClickListener(v -> { 
 String content = edtNote.getText().toString().trim();  if (!content.isEmpty()) { 
noteViewModel.insert(new Note(content));  edtNote.setText("");   }   });      lOMoAR cPSD| 60752940  
 btnClear.setOnClickListener(v -> noteViewModel.deleteAll());   }  }   
Chú ý: Các thư viện cần thiết   1. 
Room (Database) def room_version = "2.6.1" implementation 
"androidx.room:room-runtime:$room_version"  annotationProcessor 
"androidx.room:room-compiler:$room_version" // Nếu dùng Kotlin: kapt 
"androidx.room:room-compiler:$room_version"  implementation 
"androidx.room:room-ktx:$room_version"  2. 
Lifecycle (ViewModel + LiveData) def lifecycle_version =  "2.6.2"  implementation  "androidx.lifecycle:lifecycle- viewmodel:$lifecycle_version"  implementation 
"androidx.lifecycle:lifecycle-livedata:$lifecycle_version"  implementation  "androidx.lifecycle:lifecycle- runtime:$lifecycle_version"  3.  RecyclerView  implementation 
"androidx.recyclerview:recyclerview:1.3.2" 4. Material Design (UI,  FloatingActionButton,  Dialog…)  implementation 
'com.google.android.material:material:1.11.0' 
5. AppCompat + ConstraintLayout implementation 
'androidx.appcompat:appcompat:1.6.1' implementation 
'androidx.constraintlayout:constraintlayout:2.1.4'   
  Tổng hợp ví dụ build.gradle (app)   …      lOMoAR cPSD| 60752940
dependencies { // AppCompat + UI implementation 
'androidx.appcompat:appcompat:1.6.1' implementation 
'com.google.android.material:material:1.11.0' implementation 
'androidx.constraintlayout:constraintlayout:2.1.4'   
 // RecyclerView implementation 
"androidx.recyclerview:recyclerview:1.3.2"   
 // Lifecycle (ViewModel + LiveData) def lifecycle_version = "2.6.2" 
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" 
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" 
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"     // Room def  room_version  =  "2.6.1"  implementation  "androidx.room:room- runtime:$room_version"  annotationProcessor  "androidx.room:room- compiler:$room_version"  implementation  "androidx.room:room- ktx:$room_version"  }   
BÀI 2 – Xây dựng ứng dụng NoteApp gồm:   • 
Màn hình chính hiển thị danh sách ghi chú bằng RecyclerView.  • 
Nút FloatingActionButton (FAB) để thêm ghi chú mới (qua Dialog hoặc Activity).      lOMoAR cPSD| 60752940 • 
Dữ liệu được lưu trữ trong Room Database.   Toolbar:  • 
Thay thế ActionBar mặc định bằng Toolbar tùy chỉnh.  • 
Hiển thị tiêu đề: “Note App”.   Menu trên Toolbar:  • 
Tạo file menu_main.xml trong res/menu/.  •  Bổ sung các chức năng:  o 
Tìm kiếm (Search): lọc danh sách ghi chú theo từ khóa.  o 
Thêm mới (Add Note): mở form thêm ghi chú (tương tự FAB).  o 
Xóa tất cả (Clear All): xóa toàn bộ ghi chú (có AlertDialog xác nhận). 
 Cập nhật dữ liệu realtime:  • 
Khi thêm/xóa/sửa, RecyclerView tự động cập nhật nhờ LiveData.   Xử lý   • 
Thêm SearchView vào action_search để lọc ghi chú trực tiếp.  • 
Cho phép sửa ghi chú bằng cách click vào item trong RecyclerView.  • 
Hiển thị ngày giờ tạo ghi chú.