[Android] Database trong Android – P3 Hoàn thiện ứng dụng
Trong 2 phần trước chúng ta đã tạo được database và thiết kế xong giao diện cho ứng dụng. Phần này chúng ta tiếp tục hoàn thiện nốt
Lưu ý:
Toàn bộ code trong loạt bài này mình sẽ viết bằng tiếng Anh. Tuy nhiên thì tiếng anh này mình viết theo kiểu “dịch từ tiếng Việt” nên các bạn sẽ dễ dàng theo dõi.
IDE: Android studio 1.2.2
Android SDK 5.1.1
Min SDK: 4.0 (Android 4.0 trở lên sẽ dùng được ứng dụng)
Tuy nhiên các bạn hoàn toàn có thể làm trên Eclipse và android bản thấp hơn
Nội dung
Bước 1: Tạo Adapter cho item
Chúng ta sẽ tạo Adapter cho item của RecyclerView thông qua RecyclerViewHolder, nó sẽ giúp danh sách của chúng ta mượt hơn, tối ưu hơn.
package cachhoc.net.tut.demodatabase; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class ItemNoteAdapter extends RecyclerView.Adapter<ItemNoteAdapter.RecyclerViewHolder> { // list of note private List<Note> list = new ArrayList<Note>(); private Context context; public ItemNoteAdapter(Context context, List<Note> list) { this.context = context; this.list = list; } @Override public int getItemCount() { return list.size(); } /** * connect to item view */ @Override public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) { LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); View itemView = inflater.inflate(R.layout.item, viewGroup, false); return new RecyclerViewHolder(itemView); } /** * set data for item */ @Override public void onBindViewHolder(RecyclerViewHolder viewHolder, int position) { Note note = list.get(position); viewHolder.tvTitle.setText(note.getTitle()); viewHolder.tvContent.setText(note.getContent()); } public void addItem(int position, Note note) { list.add(position, note); notifyItemInserted(position); } public void removeItem(int position) { list.remove(position); notifyItemRemoved(position); } /** * ViewHolder for item view of list */ public class RecyclerViewHolder extends RecyclerView.ViewHolder implements OnClickListener { public LinearLayout container; public TextView tvTitle; public TextView tvContent; public RecyclerViewHolder(View itemView) { super(itemView); container = (LinearLayout) itemView.findViewById(R.id.item_container); tvTitle = (TextView) itemView.findViewById(R.id.tv_title); tvContent = (TextView) itemView.findViewById(R.id.tv_content); container.setOnClickListener(this); } // click item then display note @Override public void onClick(View v) { MainActivity.showNote(context, list.get(getPosition()).getId()); } } }
Ở trên mình có gọi phương thức showNote để hiển thị note khi click vào 1 item. Phương thức này được viết trong class MainActivity.
Bước 2: Xây dựng MainActivity
package cachhoc.net.tut.demodatabase; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private ItemNoteAdapter adapter; private List<Note> listNote = new ArrayList<>(); private Context context; private DatabaseHelper db; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this; db = new DatabaseHelper(context); connectView(); } /** * connect java with xml view */ private void connectView() { // find Float Action Button findViewById(R.id.fab).setOnClickListener(this); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_note); // If the size of views will not change as the data changes. recyclerView.setHasFixedSize(true); // Setting the LayoutManager. RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); // Setting the adapter. adapter = new ItemNoteAdapter(context, listNote); recyclerView.setAdapter(adapter); } /** * update list note when resume (open app or finish NoteActivity) */ public void onResume() { super.onResume(); updateListNote(); } /** * select all note from database and set to ls * use for loop to add into listNote. * We must add all item in ls into listNote then adapter can update * we add reverse ls to show new note at top of list */ private void updateListNote() { // clear old list listNote.clear(); // add all notes from database, reverse list ArrayList<Note> ls = db.getListNote("SELECT * FROM " + DatabaseHelper.TABLE_NOTE); // reverse list for (int i = ls.size() - 1; i >= 0; i--) { listNote.add(ls.get(i)); } adapter.notifyDataSetChanged(); } /** * display note have id */ public static void showNote(Context context, long id) { Intent intent = new Intent(context, NoteActivity.class); // send id to NoteActivity intent.putExtra(NoteActivity.ID, id); context.startActivity(intent); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.fab: showNote(context, NoteActivity.NEW_NOTE); break; default: break; } } }
Code cũng rất dễ hiểu. Chỉ có 2 phần có thể các bạn băn khoăn như sau:
- Để có thể hoạt động được thì RecyclerView cần được set một Layout. Trong code mình chọn là LinearLayoutManager khi đó RecyclerView sẽ là danh sách vuốt thẳng như ListView. Nếu các bạn dùng GridLayoutManager thì sẽ là dạng lưới.
-
Chúng ta Select ArrayList
từ database gán vào ls. Sau đó phải copy các phần tử này vào listNote chứ không được gán listNote = ls vì khi đó listNote sẽ trỏ tới một vùng nhớ khác (vùng nhớ của ls) làm cho adapter không thể update được. Chúng ta cũng đảo ngược ls để gán vào listNote vì mình muốn đưa các note được viết sau lên đầu danh sách.
Bước 3: Tạo NoteActivity soạn thảo note
package cachhoc.net.tut.demodatabase; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.widget.EditText; import android.widget.Toast; import android.support.v7.app.AlertDialog; public class NoteActivity extends AppCompatActivity { /** * if action is add new note, it will init by NEW_NOTE */ public static final long NEW_NOTE = -1; /** * key for get id note from other activity to display note */ public static final String ID = "ID"; /** * to edit, add,... note from database */ private DatabaseHelper db; /** * note curren */ private Note note; private EditText editTitle; private EditText editContent; private Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_note); context = this; db = new DatabaseHelper(context); connectView(); getInfo(); } /** * conect with xml view */ private void connectView() { editTitle = (EditText) findViewById(R.id.edit_title); editContent = (EditText) findViewById(R.id.edit_content); } /** * get info note to display */ private void getInfo() { long id = getIntent().getLongExtra(ID, NEW_NOTE); // not new note then find note from database by id of note if (id != NEW_NOTE) { String sql = "SELECT * FROM " + DatabaseHelper.TABLE_NOTE + " WHERE " + DatabaseHelper.KEY_ID_NOTE + " = " + id; note = db.getNote(sql); } if (note != null) { editTitle.setText(note.getTitle()); editContent.setText(note.getContent()); } else { editTitle.setText(""); editContent.setText(""); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_note, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_save: save(); break; case R.id.menu_delete: delete(); break; default: break; } return super.onOptionsItemSelected(item); } /** * get title and content of note, if they empty then finish * if they not empty then check note is null? * if note is null (create new note), we will create note and insert into database * if note not null then we update note * after save we finish activity */ private void save() { String title = editTitle.getText().toString().trim(); String content = editContent.getText().toString().trim(); String notify = null; if (TextUtils.isEmpty(title) && TextUtils.isEmpty(content)) { notify = "note empty, don't save!"; } else { // new note if (note == null) { Note note = new Note(); note.setTitle(title).setContent(content); if (db.insertNote(note) > 0) { notify = "add success!"; } else { notify = "add fail!"; } } else { // update note note.setTitle(title).setContent(content); if (db.updateNote(note)) { notify = "update success!"; } else { notify = "update fail!"; } } } Toast.makeText(context, notify, Toast.LENGTH_SHORT).show(); finish(); } /** * get title and content of note, if they empty then finish * if they not empty then show dialog to show question delete * if select no delete then close dialog * if select delete then delete note */ private void delete() { String title = editTitle.getText().toString().trim(); String content = editContent.getText().toString().trim(); if (TextUtils.isEmpty(title) && TextUtils.isEmpty(content)) { finish(); } else { final AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AppCompatAlertDialogStyle); builder.setTitle(R.string.delete).setIcon(R.mipmap.ic_launcher) .setMessage("Do you want delete note?"); builder.setPositiveButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); builder.setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { deleteNote(); } }); builder.show(); } } /** * check note is null? * if is null then finish * if note not null then delete in database and finish */ private void deleteNote() { if (note != null) { String where = DatabaseHelper.KEY_ID_NOTE + " = " + note.getId(); String notify = "delete success!"; if (!db.deleteNote(where)) { notify = "delete failt!"; } Toast.makeText(context, notify, Toast.LENGTH_SHORT).show(); } finish(); } /** * click back button on phone */ @Override public void onBackPressed() { save(); } }
Trong NoteActivity, khi nó được khởi tạo sẽ lấy id mà intent gửi sang. Nếu id = NEW_NOTE (là -1) thì tức là tạo note mới, chúng ta setText cho editTitle và editContent là chuỗi rỗng. Ngược lại chúng ta sẽ lấy note có id đó từ database và hiển thị lên.
Code hoàn toàn không khó hiểu. Rất mong các bạn có thể hoàn thành ứng dụng.
Các bạn cũn có thể download project DemoDatabase
Phần 4 tiếp theo mình sẽ hướng dẫn các bạn cách update database nếu có sự thay đổi mà không làm lỗi ứng dụng hiện tại.
Bài viết được thực hiện trong loạt bài hướng dẫn Database trong Android bởi nguyenvanquan7826
Mình làm theo hương dẫn tạo database trên android. nhưng mình bị lỗi như vậy nè:
10-22 09:55:29.233 12687-12687/? E/SQLiteLog: (1) no such table: tb_note
10-22 09:55:29.233 12687-12687/? D/AndroidRuntime: Shutting down VM
10-22 09:55:29.233 12687-12687/? W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0xa617c908)
10-22 09:55:29.233 12687-12687/? E/AndroidRuntime: FATAL EXCEPTION: main
10-22 09:55:29.233 12687-12687/? E/AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {nguyenduchai.cse.com.mynote/nguyenduchai.cse.com.mynote.MainActivity}: android.database.sqlite.SQLiteException: no such table: tb_note (code 1): , while compiling: SELECT * FROM tb_note
10-22 09:55:29.233 12687-12687/? E/AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2742)
Mình fix hoài mà không được ? Bạn chỉ mình biết cách fix với…
Bạn xem lại xem, nó báo là không có bảng tb_note. bạn tập các đọc lỗi nhé.
Mình biết là không tạo ra bảng tb_note, nên truy suất lỗi. nhưng mình coi kỹ code rồi, xem kỹ các bước bạn làm rồi. Nhưng chưa fix được. không biết nguyên nhân sao ?
Khoảng 10h tối bạn nhắn face cho mình nhé: fb.com/nguyenvanquan7826
ok. thanks ban.
Bạn ơi cho tớ hỏi: tớ có làm theo các bước như trong bài. Tớ đang gặp 1 vấn đề là sau khi thêm các note mới thì mỗi note hiển thị lên 1 hẳn 1 trang màn hình, cần kéo màn hình để thấy các note khác, không hiển thị được như trong video của bạn. Vậy, tớ muốn hiển thị như vậy thì làm như thế nào?
Bạn điều chỉnh lại chiều cao của item nhé. Do nó đấy.
Cảm ơn nha. Tớ chữa được lỗi này rồi.