Bài tập 04: Tạo ứng dụng TODO LIST
Đề bài
Viết một ứng dụng android để thực hiện lưu trữ danh sách các công việc cần làm (TODO LIST). Ứng dụng dùng một ListView hiển thị danh sách các dòng được đánh số mô tả các công việc mà bạn cần thực hiện.
Ứng dụng được điều khiển bởi một Menu Option cung cấp các tính năng sau:
- Thêm mới (Add entry) một công việc tới danh sách
- Xóa (Delete entry) một công việc từ danh sách (xóa công việc đang được chọn)
- Cập nhật (Update entry) nội dung từ công việc đang được chọn.
- Lưu (Save List) TODO LIST (Có thể thực hiện lưu ra một tệp ở trên máy)
- Đóng (Close App) ứng dụng app
Màn hình chính của TODO LIST tương tự như sau:

Chức năng
- Đánh một công việc mới ở ô EditText trên đầu. Ấn nút Menu, kích nút Add entry.
- Kích trên một dòng trên ListView. Nội dung được chọn sẽ hiển thị trên EditText.
- Chỉnh sửa nội dung, kích Menu> Update entry để cập nhật giá trị của TODO LIST (sau đó xóa trắng EditText)
- Kích trên Menu> Delete entry để xóa công việc khỏi danh sách (sau đó xóa trắng EditText).
- Kích Save List để ghi danh sách này ra đĩa
- Kích nút Close để lưu danh sách và kế thúc ứng dụng.
Code tham khảo
1. Code giao diện
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:id="@+id/layControl1"
android:layout_width="409dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/btnAddEntry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
android:layout_weight="1"
android:text="Add Entry"
app:layout_constraintBottom_toBottomOf="parent" />
<Button
android:id="@+id/btnDeleteEntry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
android:layout_weight="1"
android:text="Delete Entry" />
<Button
android:id="@+id/btnUpdateEntry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
android:layout_weight="1"
android:text="Update Entry"
app:layout_constraintBottom_toBottomOf="parent" />
</LinearLayout>
<LinearLayout
android:id="@+id/layControl2"
android:layout_width="409dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/layControl1"
tools:layout_editor_absoluteX="1dp">
<Button
android:id="@+id/btnSaveList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
android:layout_weight="1"
android:text="Save List" />
<Button
android:id="@+id/btnCloseApp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_marginHorizontal="5dp"
android:layout_marginVertical="5dp"
android:layout_weight="1"
android:text="Close App" />
</LinearLayout>
<TextView
android:id="@+id/lblToDo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@android:color/holo_orange_dark"
android:text="TODO List: "
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/txtToDo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào công việc cần làm..."
android:inputType="textPersonName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/lblToDo" />
<ListView
android:id="@+id/lstToDo"
android:layout_width="409dp"
android:layout_height="0dp"
android:layout_marginTop="2dp"
app:layout_constraintBottom_toTopOf="@+id/layControl2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/txtToDo" />
</androidx.constraintlayout.widget.ConstraintLayout>
2. Code chức năng
package timoday.edu.vn.todolist;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private Button btnAddEntry;
private Button btnUpdateEntry;
private Button btnDeleteEntry;
private Button btnCloseApp;
private Button btnSaveList;
private ListView lstToDo;
private EditText txtToDo;
private TextView lblToDo;
//Khai báo mảng chứa các item
private ArrayList<String> arrayList =new ArrayList<String>();
//Định nghĩ một mảng string adapter để điều khiển dữ liệu của listview
private ArrayAdapter<String> adapter;
//Khai báo biến index chứa vị trí item được chọn trong listview
private int index = 0;
//Khai báo
public static String MYPREFSID = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Gắn code giao diện với phần code Java
btnAddEntry = (Button)findViewById(R.id.btnAddEntry);
btnUpdateEntry = (Button)findViewById(R.id.btnUpdateEntry);
btnDeleteEntry = (Button)findViewById(R.id.btnDeleteEntry);
btnCloseApp = (Button)findViewById(R.id.btnCloseApp);
btnSaveList = (Button)findViewById(R.id.btnSaveList);
lblToDo = (TextView)findViewById(R.id.lblToDo);
txtToDo = (EditText)findViewById(R.id.txtToDo);
lstToDo = (ListView)findViewById(R.id.lstToDo);
//Lấy dữ liệu ngày tháng hiển thị lên TextView của công việc theo đúng thiết kế
SimpleDateFormat sdf = new SimpleDateFormat(" EEE. MMMdd, yyyy-HH:mm a", Locale.getDefault());
String currentDateandTime = sdf.format(new Date());
lblToDo.setText("TODO List:"+ currentDateandTime);
//gắt Listview với dữ liệu mảng Array
adapter=new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_activated_1,
arrayList);
lstToDo.setAdapter(adapter);
//Gắn sự kiện cho nút Add Entry
btnAddEntry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Kiểm tra xem txtToDo có để trống không
if(txtToDo.getText().toString().trim().equals(""))
{
Toast.makeText(getApplicationContext(),"Công việc không được để trống",Toast.LENGTH_LONG).show();
//Thiết lập con trỏ trỏ vào ô txtToDo
txtToDo.requestFocus();
return;
}
//Thêm dữ liệu từ người sử dụng gõ từ EditText vào Array
arrayList.add(txtToDo.getText().toString());
//Gọi phương thức này để cập nhật lại listview
adapter.notifyDataSetChanged();
//Clear dữ liệu ở EditText
txtToDo.getText().clear();
}
});
//Gắn sự kiện cho chọn Item trên ListView
lstToDo.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
//Lấy item tại vị trí index i
txtToDo.setText(lstToDo.getItemAtPosition(i).toString());
//Lưu lại index của item được chọn
index = i;
}
});
//Sự kiến xoá item trên listview
btnDeleteEntry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Kiểm tra xem txtToDo có để trống không
if(txtToDo.getText().toString().trim().equals(""))
{
Toast.makeText(getApplicationContext(),"Cần chọn công việc để xoá!",Toast.LENGTH_LONG).show();
return;
}
//Lấy index của item cần xoá trong listview
arrayList.remove(index);
//Cập nhật lại listview
adapter.notifyDataSetChanged();
//Clear dữ liệu ở EditText
txtToDo.getText().clear();
}
});
//Sự kiện update item trên listview
btnUpdateEntry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Kiểm tra xem txtToDo có để trống không
if(txtToDo.getText().toString().trim().equals(""))
{
Toast.makeText(getApplicationContext(),"Cần chọn công việc để xoá!",Toast.LENGTH_LONG).show();
return;
}
//lấy index của item đang được chọn
arrayList.set(index,txtToDo.getText().toString());
//update lại listview
adapter.notifyDataSetChanged();
}
});
//Sự kiện button Save List
//Xem thêm nội dung lý thuyết chương 3: Vòng đời ứng dụng Android
btnSaveList.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Lưu lại listview vào SharedPreferences
saveListView();
}
});
//Sư kiện button Close App
btnCloseApp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Lưu lại listview vào SharedPreferences
saveListView();
finishAffinity();
System.exit(0);
}
});
}
//Viết bổ xung hai sự kiện trong vòng đời của Android để khôi phục lại dữ liệu của ListView
@Override
protected void onResume() {
super.onResume();
//Khôi phục lại listview
updateListView();
}
@Override
protected void onPause() {
super.onPause();
//Lưu lại ListView
saveListView();
}
//Cập nhật lại ListViewer
protected void updateListView()
{
SharedPreferences myPrefs = getSharedPreferences(MYPREFSID,MODE_PRIVATE);
for (int i = 0;; ++i){
final String str = myPrefs.getString(String.valueOf(i), "");
if (!str.equals("")){
adapter.add(str);
} else {
break; // Nếu chuỗi rỗng, là trường hợp không còn phần tử nào nữa, và kết thúc vòng lặp
}
}
}
//Lưu lại dữ liệu listview vào bộ nhớ chia sẻ
protected void saveListView()
{
SharedPreferences myPrefs = getSharedPreferences(MYPREFSID,MODE_PRIVATE);
SharedPreferences.Editor myEditor = myPrefs.edit();
myEditor.clear();
for (int i = 0; i < adapter.getCount(); ++i){
// Lưu từng item với khoá là index của item.
myEditor.putString(String.valueOf(i), adapter.getItem(i));
}
myEditor.commit();
}
protected void clearMyPreferences()
{
SharedPreferences myPrefs = getSharedPreferences(MYPREFSID,MODE_PRIVATE);
SharedPreferences.Editor myEditor = myPrefs.edit();
myEditor.clear();
myEditor.commit();
}
}
