SQLite trong Android – Làm sao để sử dụng hiệu quả nhất

0
90
Bài này thuộc phần 14 của 16 phần trong series Tự học lập trình Android trong 24 giờ

Đây là một bài viết step-by-step hướng dẫn triển khai SQLite trong Android database với ngôn ngữ Kotlin. Trong bài viết này, mình sẽ code các chức năng cơ bản của một CSDL (CRUD). Chúng ta sẽ sử dụng SQLiteOpenHelper class để tạo và quản lý SQLite database.

Ví dụ minh họa trong suốt bài viết hướng dẫn là xây dựng ứng dụng PhoneBook. Ứng dụng có các  thao tác như đọc, thêm, sửa , xóa các danh bạ từ SQLite database Android.

Sau khi đọc xong bài viết này, bạn sẽ học được:

  • Làm thể nào để tạo là sử dụng SQLite database bằng Kotlin sử dụng SQLiteOpenHelper.
  • Cách tạo một custom ListView bằng Custom Adapter.
  • Bắt sự kiện click vào từng contact trên ListView
  • Cách thêm mới, chỉnh sửa, và xóa danh bạ trong SQLite database
  • Cách chuyển dữ liệu từ activity này sang activity sử dụng Intent

Ngôn ngữ được sử dụng trong bài viết này là Kotlin nhé

Chúng ta bắt đầu thôi!

SQLite trong Android - Làm sao để sử dụng hiệu quả nhất

Xây dựng ứng dụng PhoneBook bằng cơ sở dữ liệu SQLite trong Android

Để tránh lãng phí thời gian bằng lý thuyết khô khan, chúng ta bắt đầu tạo mới project nhé

#1. Tạo một project mới

Tạo mới một project trong Android Studio, sử dụng tùy chọn mặc định và đặt tên project là  KotlinDBTutorial. Đừng quên check vào checkbox với nội dung Include Kotlin support.

Mình sử dụng Empty Activity trong màn hình chọn activity để khởi tạo.

SQLite trong Android - Làm sao để sử dụng hiệu quả nhất

#2. Tạo một Model Class

Tạo mới một Kotlin class và đặt tên nó là ContactData. Đây là một model class để database quản lý các bản ghi của bảng Contact như là 1 đối tượng trong Kotlin.

class ContactData {
 
    var conID:Int?=null
    var FirstName:String?=null
    var LastName:String?=null
    var Email:String?=null
    var PhoneNumber:String?=null
 
 
    constructor(id:Int,fname:String,lname:String,email:String,pnum:String){
 
        this.conID = id
        this.FirstName = fname
        this.LastName = lname
        this.Email = email
        this.PhoneNumber = pnum
 
    }
 
}

#3. Tạo Form để Thêm/Sửa Contact

Tạo mới một activity và đặt tên nó là ContactManager.

Ở activity này, chúng ta sẽ thêm, sửa và xóa các dữ liệu contacts từ database. Mình vừa mới tạo một form để thêm/sửa một contact. Đây là form hết sức đơn giản sử dụng TextView, EditText và 2 Buttons.

Phía dưới là đoạn code layout của nó.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    android:padding="5dp"
    tools:context="com.androidpala.kotlindatabasetutorial.ContactManager">
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="Contact Details"
        android:textColor="@color/colorPrimary"
        android:textSize="20sp"
        android:textStyle="bold" />
 
    <EditText
        android:id="@+id/fnametxt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="First Name"
        android:inputType="textPersonName"
        android:text="" />
 
    <EditText
        android:id="@+id/lnametxt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Last Name"
        android:inputType="textPersonName"
        android:text="" />
 
    <EditText
        android:id="@+id/emailtxt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Email"
        android:inputType="textEmailAddress"
        android:text="" />
 
    <EditText
        android:id="@+id/phone_txt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Phone Number"
        android:inputType="phone"
        android:text="" />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">
 
 
        <Button
            android:id="@+id/save_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Add Contact"
            android:textSize="16sp" />
 
        <Button
            android:id="@+id/delete_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Delete Contact"
            android:textSize="16sp" />
 
    </LinearLayout>
 
</LinearLayout>

Và đây là diện mạo của layout. Cũng đơn giản phải không?

SQLite trong Android - Làm sao để sử dụng hiệu quả nhất

#4. Khởi tạo SQLite database bằng cách dùng SQLiteOpenHelper class

SQLiteOpenHelper được sử dụng để khởi tạo database. Nó cho phép quản lý cơ sở dữ liệu, nâng cấp hoặc hạ cấp các phiên bản của database.

Đơn giản là tạo một class, đặt tên là DatabaseHandler. Chúng ta sẽ triển khai toàn bộ các chức năng CRUD của database trong class này.

Giải thích thêm: CRUD là gì? CRUD là viết tắt của Create, Read, Update và Delete. Đây là 4 thao tác cơ bản của mọi CSDL.

Dưới đây là đoạn code của DatabaseHandler class.

class DatabaseHandler : SQLiteOpenHelper {
 
 
    companion object {
 
        val Tag = "DatabaseHandler"
        val DBName = "ContactDB"
        val DBVersion = 1
 
        val tableName = "phoneTable"
        val ConID = "id"
        val FirstName = "fname"
        val LastName = "lname"
        val Number = "number"
        val Email = "email"
    }
 
    var context: Context? = null
    var sqlObj: SQLiteDatabase
 
    constructor(context: Context) : super(context, DBName, null, DBVersion) {
 
        this.context = context;
        sqlObj = this.writableDatabase;
    }
 
    override fun onCreate(p0: SQLiteDatabase?) {
 
        //SQL for creating table
        var sql1: String = "CREATE TABLE IF NOT EXISTS " + tableName + " " +
                "(" + ConID + " INTEGER PRIMARY KEY," +
                FirstName + " TEXT, " + LastName + " TEXT, " + Email +
                " TEXT," + Number + " TEXT );"
 
        p0!!.execSQL(sql1);
    }
 
    override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
 
        p0!!.execSQL("Drop table IF EXISTS " + tableName)
        onCreate(p0)
 
    }
 
    fun AddContact(values: ContentValues): String {
 
        var Msg: String = "error";
        val ID = sqlObj!!.insert(tableName, "", values)
 
        if (ID > 0) {
            Msg = "ok"
        }
        return Msg
    }
 
    fun FetchContacts(keyword: String): ArrayList<ContactData> {
 
        var arraylist = ArrayList<ContactData>()
 
        val sqb = SQLiteQueryBuilder()
        sqb.tables = tableName
        val cols = arrayOf("id", "fname", "lname", "email", "number")
        val rowSelArg = arrayOf(keyword)
 
        val cur = sqb.query(sqlObj, cols, "fname like ?", rowSelArg, null, null, "fname")
 
        if (cur.moveToFirst()) {
 
            do {
                val id = cur.getInt(cur.getColumnIndex("id"))
                val fname = cur.getString(cur.getColumnIndex("fname"))
                val lname = cur.getString(cur.getColumnIndex("lname"))
                val email = cur.getString(cur.getColumnIndex("email"))
                val number = cur.getString(cur.getColumnIndex("number"))
 
                arraylist.add(ContactData(id, fname, lname, email, number))
 
            } while (cur.moveToNext())
        }
 
        var count: Int = arraylist.size
 
        return arraylist
    }
 
    fun UpdateContact(values: ContentValues, id: Int): String {
 
        var selectionArs = arrayOf(id.toString())
 
        val i = sqlObj!!.update(tableName, values, "id=?", selectionArs)
        if (i > 0) {
            return "ok";
        } else {
 
            return "error";
        }
    }
 
    fun RemoveContact(id: Int): String {
 
        var selectionArs = arrayOf(id.toString())
 
        val i = sqlObj!!.delete(tableName, "id=?", selectionArs)
        if (i > 0) {
            return "ok";
        } else {
 
            return "error";
        }
    }
}

Mình sẽ giải thích qua về chức năng của các hàm trên nhé

Companion Objects trong Kotlin

Trước đây, khi sử dụng Java, mình hay sử dụng biến static để định nghĩa tên database, tên bảng, tên cột hay phiên bản database… Không có khái niệm biến/hàm static trong ngôn ngữ Kotlin. Thay vào đó, người ta sử dụng khái niệm “companion object”.

companion object {
 
        val Tag = "DatabaseHandler"
        val DBName = "ContactDB"
        val DBVersion = 1
 
        val tableName = "phoneTable"
        val ConID = "id"
        val FirstName = "fname"
        val LastName = "lname"
        val Number = "number"
        val Email = "email"
}

Trong hàm khởi tạo constructor(), chúng ta truyền vào tham số “context” từ activity đang hoạt động.

Database sẽ được tạo khi phương thức writableDatabase được gọi.

Hàm onCreate()được gọi khi database được khởi tạo. Chúng ta có thể tạo bảng của CSDL trong hàm này

Hàm onUpgrade()sử dụng khi chúng ta cần thay đổi cấu trúc của database hiện tại của ứng dụng như: thêm bảng, thêm cột…

Hàm AddContact()sẽ thêm 1 contact mới vào database.

Hàm FetchContacts()sẽ đọc toàn bộ records có trong bảng và trả về cho chúng ta 1 ArrayList.Nếu chúng ta truyền tham số là  “%” nó sẽ lấy toàn bộ records có trong database, ngược lại, nó sẽ tìm kiếm theo first_name.

UpdateContact() và RemoveContact()sẽ chỉnh sửa và xóa contact trong database. Chúng ta sẽ truyền contact_id vào hàm này với các contact_id là khóa chính của bảng contact.

Bây giờ, class xử lý database đã được sẵn sàng để được sửa dụng

#5. Triển khai ListView với Custom Adapter

Đây là 1 thiết kế cơ bản của ứng dụng sử dụng ListView và Button. Mở file layout của activity và thêm vào đoạn code như bên dưới:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context="com.androidpala.kotlindatabasetutorial.MainActivity">
 
    <Button
        android:id="@+id/add_contact_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="5dp"
        android:text="Add New Contact"
        android:textSize="18sp"
        android:textStyle="bold" />
 
    <ListView
        android:id="@+id/contact_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:dividerHeight="1px">
 
    </ListView>
</LinearLayout>

Chúng ta cần phải tạo layout cho mỗi ô của ListView. Đây là 1 layout đơn giản chỉ gồm 1 ImageView và 1 TextView.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:gravity="center"
    android:padding="10dp">
 
    <ImageView
        android:src="@mipmap/ic_face_black_48dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="5dp"/>
 
    <TextView
        android:id="@+id/contact_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textStyle="bold"/>
 
</LinearLayout>

Bây giờ, chúng ta có thể tạo 1 custom adapter cho ListView. Bạn hãy tạo mới 1 class và đặt tên nó là ContactAdapter và kế thừa BaseAdapter class.

Bên dưới là đoạn code hoàn chỉnh của ContactAdapter.kt

class ContactAdapter(con:Context, arrList:ArrayList<ContactData>): BaseAdapter() {
 
    var arrayList = ArrayList<ContactData>()
    var context: Context? = null
    var myInflater: LayoutInflater? = null
 
    init {
 
        this.context    = con
        this.myInflater = LayoutInflater.from(context)
        this.arrayList  = arrList
    }
 
    override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View {
 
        var myView = myInflater!!.inflate(R.layout.list_block,null)
        var ConObj = arrayList[p0]
 
        var full_name : String = ConObj.FirstName.toString() +" "+ ConObj.LastName.toString()
        myView.contact_name.text = full_name
        return myView
    }
 
    override fun getItem(p0: Int): Any {
        return arrayList[p0]
    }
 
    override fun getItemId(p0: Int): Long {
        return p0.toLong()
    }
 
    override fun getCount(): Int {
        return arrayList.size
    }
}

#6. Lấy dữ liệu từ database và đưa vào ListView

Tiếp theo, chúng ta sẽ sử dụng DatabaseHandler class để tương tác với database và hiển thị ra ListView thông qua Custom Adapter

class MainActivity : AppCompatActivity() {
 
 
    var ContactList = ArrayList<ContactData>()   
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
    }
 
    override fun onResume() {
        super.onResume()
 
        var DB:DatabaseHandler = DatabaseHandler(this);
        ContactList = DB.FetchContacts("%");
 
        if(ContactList.size>0) {
 
            var ContactAdapterObj = ContactAdapter(this, ContactList)
            contact_list.adapter = ContactAdapterObj
 
              contact_list.onItemClickListener = AdapterView.OnItemClickListener { 
                                    adapterView, view, position, id ->
 
                //ContactList holds ContactData object  
                var fname = ContactList[position].FirstName;
                var lname = ContactList[position].LastName;
                var email = ContactList[position].Email;
                var phone = ContactList[position].PhoneNumber;
                var id = ContactList[position].conID
 
                 //Passing data to ContactManager activity.
                 var intent = Intent(this, ContactManager::class.java)
                 intent.putExtra("id", id)
                 intent.putExtra("fname", fname)
                 intent.putExtra("lname", lname)
                 intent.putExtra("email", email)
                 intent.putExtra("phone", phone)
                 intent.putExtra("action", "edit")
                 startActivity(intent)
 
               }
        }else{
 
          Toast.makeText(this, "No Contact Found", Toast.LENGTH_SHORT).show()
        }
 
        add_contact_btn.setOnClickListener(){
            var intent= Intent(this,ContactManager::class.java)
            startActivity(intent)
        }
 
    }
}

Trong hàm onResume(), chúng ta tạo ra 1 đối tượng của DatabaseHandler class và gọi tới  hàm FetchContact(). Kết quả trả về là ArrayList

Danh sách contacts sẽ được truyền qua ContactAdapter thông qua hàm khởi tạo.

var ContactAdapterObj = ContactAdapter(this, ContactList)
 contact_list.adapter = ContactAdapterObj

Xử lý sự kiện click trong ListView

Với ngôn ngữ Kotlin, việc triển khai lắng nghe sự kiện OnClick cực kì đơn giản và ngắn gọn hơn hơn Java rất nhiều

contact_list.onItemClickListener = AdapterView.OnItemClickListener { 
                                   adapterView, view, position, id ->
 
  }

Tương tự cho bắt sự kiện click với Button.
Chúng ta sẽ đi đến activity ContactManager khi button được nhấn. Activity ContactManager sẽ chứa 1 form để thêm và chỉnh sửa liên hệ.

add_contact_btn.setOnClickListener(){
            var intent= Intent(this,ContactManager::class.java)
            startActivity(intent)
        }

#7. Cách thêm mới và chỉnh sửa danh bạ

Đây là activity thứ hai, có nhiệm vụ là thêm mới và chỉnh sửa các contact.

class ContactManager : AppCompatActivity() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_contact_manager)
 
        // Getting id value pass by MainActivity via Intent
        // If no value pass, it will return 0
        var record_id = intent.getIntExtra("id", 0)
 
        if (record_id == 0) { //Add Record
 
            save_btn.text = "Add Contact"
 
        } else { //Update Record
 
            save_btn.text = "Update Contact"
            var _fname = intent.getStringExtra("fname")
            var _lname = intent.getStringExtra("lname")
            var _email = intent.getStringExtra("email")
            var _phone = intent.getStringExtra("phone")
 
            fnametxt.setText(_fname)
            lnametxt.setText(_lname)
            emailtxt.setText(_email)
            phone_txt.setText(_phone)
 
        }
 
        save_btn.setOnClickListener() {
 
            var a = fnametxt.text.toString();
            var b = lnametxt.text.toString();
            var c = emailtxt.text.toString();
            var d = phone_txt.text.toString();
 
            if(a==""){
 
                Toast.makeText(this, "Enter FirstName",
                        Toast.LENGTH_SHORT).show()
 
            }else if(d==""){
 
                Toast.makeText(this, "Enter Phone Number",
                        Toast.LENGTH_SHORT).show()
 
            }else{
 
                var values = ContentValues()
                values.put("fname", a)
                values.put("lname", b)
                values.put("email", c)
                values.put("number", d)
 
                 //Adding contact
                 if(record_id==0){
 
                    var DB: DatabaseHandler = DatabaseHandler(this);
 
                    var response = DB.AddContact(values);
                    if(response=="ok") {
 
                        Toast.makeText(this, "Contact Added",
                                Toast.LENGTH_SHORT).show()
 
                        var intent = Intent(this, MainActivity::class.java)
                        startActivity(intent)
                        finish()
 
                    }else{
                        Toast.makeText(this, "Not Added..Try again",
                                Toast.LENGTH_SHORT).show()
                    }
 
                }else{ //update contact
 
                    var DB: DatabaseHandler = DatabaseHandler(this);
 
                    var res: String = DB.UpdateContact(values, record_id)
 
                    if(res=="ok") {
                        Toast.makeText(this, "Contact Updated",
                                Toast.LENGTH_SHORT).show()
 
                        var intent = Intent(this, MainActivity::class.java)
                        startActivity(intent)
                        finish()
 
                    }else{
                        Toast.makeText(this, "Error..Try Again",
                                Toast.LENGTH_SHORT).show()
                    }
 
                }
 
            }
 
        }
 
        delete_btn.setOnClickListener() {
 
            var DB: DatabaseHandler = DatabaseHandler(this);
            var res: String = DB.RemoveContact(record_id)
 
            if(res=="ok") {
 
                Toast.makeText(this, "Contact Deleted",
                        Toast.LENGTH_SHORT).show()
            }else{
 
                Toast.makeText(this, "Error..Try Again",
                        Toast.LENGTH_SHORT).show()
            }
 
            var intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
            finish()
 
        }
 
    }
}

Trong activity này, chúng ta sẽ thêm và chỉnh sửa contact tùy thuộc vào giá trị của trường _id. Nếu nó bằng 0, chúng ta sẽ thêm liên hệ mới. Ngược lại, nếu nó khác 0 thì chúng ta sẽ chỉnh sửa contact có id tương ứng với _id được nhập vào.

Chúng ta sử dụng các hàm AddContact(), UpdateContact() and RemoveContact() của DatabaseHandler, đọc dữ liệu từ EditText và truyền nó vào form của ContentValue.

Cuối cùng, ứng dụng của chúng ta đã sẵn sàng để chạy trên máy ảo hoặc trên bất kì thiết bị android nào.

Các bạn có thể download toàn bộ source code tại đây

Ngoài ra, nếu đọc bài viết mà bạn vẫn có chỗ chưa hiểu thì có thể tham khảo video bên dưới để rõ thêm nhé

Hi vọng bài viết này sẽ giúp bạn hiểu và biết cách sử dụng SQLite trong Android. Qua đó dễ dàng xây dựng được các ứng dụng cần tương tác với database, kết nối cơ sở dữ liệu SQlite trong Android.

Đừng quên like và chia sẻ bài viết nếu thấy có ích nhé

Xem tiếp các bài trong Series
Phần trước: AsyncTask trong Android – công cụ xử lý đa luồng hữu hiệuPhần kế tiếp: Cách sử dụng SharedPreferences trong Android cực tinh tế

Bình luận. Đặt câu hỏi cũng là một cách học

avatar
  Theo dõi bình luận  
Thông báo