ActiveAndroid – Tạo và thao tác database trong Android trong vòng 1 nốt nhạc

Chào các bạn, rất lâu rồi mình mới trở lại viết Blog hướng dẫn về Android. Lần trước mình đã có viết 5 bài về database trong android. Thực sự đó là một loạt bài rất tâm huyết của mình, tuy nhiên sau nhiều lần làm việc với database, mình nhận ra cách sử dụng thông thường như vậy thật là quá vất vả với 1 bảng, chứ chưa nói đến database có nhiều bảng. Vì vậy căn bệnh lười phát sinh và nghĩ rằng liệu có một thứ gì đó hoặc một cách nào đó mà chỉ cần “1 nốt nhạc” cũng tạo được một database phức tạp. Và mình đã tìm ra nó – ActiveAndroid.

Trang chủ của ActiveAndroid ở đây: http://www.activeandroid.com/
Tuy nhiên mình chỉ đọc trên github của họ thôi, các bạn có thể xem tại đây: https://github.com/pardom/ActiveAndroid

Trong bài này, mình sẽ hướng dẫn các bạn làm một ứng dụng FriendList (lưu tên bạn bè) đơn giản xây dựng database với ActiveAndroid trên Android Studio 2.0

friendlist_activeandroid

1. Cấu hình build.gradle

Chúng ta cấu hình như sau:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.nguyenvanquan7826.friendlist"
        minSdkVersion 14
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    // for activeandroid
    repositories {
        mavenCentral()
        maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:design:23.3.0'
    compile 'com.android.support:cardview-v7:23.3.0'
    compile 'com.android.support:recyclerview-v7:23.3.0'

    // for activeandroid
    compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
}

Trong đó chú ý những dòng để load thư viện activeandroid

repositories {
    mavenCentral()
    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'

2. Cấu hình manifest

Trong thẻ application của AndroidManifest.xml các bạn thêm các thuộc tính sau:

<application
    android:name="com.activeandroid.app.Application"
    ...
    
    <meta-data
        android:name="AA_DB_NAME"
        android:value="friendlist.db" />
    <meta-data
        android:name="AA_DB_VERSION"
        android:value="1" />
    <meta-data
        android:name="AA_MODELS"
        android:value="com.nguyenvanquan7826.friendlist.Friend" />
    
    ...
</application>

Trong này các bạn chú ý:

  • AA_DB_NAME là từ khóa nhận biết tên database (friendlist.db)

  • AA_DB_VERSION là từ khóa nhận biết phiên bản database (hiện tại là phiên bản 1), sau này các bạn nâng cấp database chỉ cần sửa tại đây thôi

  • AA_MODELS là từ khóa nhận biết các bảng trong database. Các bảng này chính là các class của chúng ta (có kèm package), lát nữa mình sẽ nói rõ hơn. Bình thường chúng ta không cần thẻ meta-data của AA_MODELS, tuy nhiên để quá trình load cấu trúc database nhanh hơn và nếu muốn cho Android 6 chạy được các bạn hãy cho vào. Trong trường hợp có nhiều bảng, các bạn ghi cách nhau bởi dấu phẩy như sau:
    com.nguyenvanquan7826.friendlist.Friend, com.nguyenvanquan7826.friendlist.Lover

  • android:name=”com.activeandroid.app.Application” đây là dòng khai báo cho ứng dụng biết chúng ta sử dụng Application của activeandroid. Nếu các bạn đang sử dụng một Application của một thư viện khác thì có thể dùng Application đó kế thừa com.activeandroid.app.Application. Hoặc nếu bạn không muốn kế thừa từ com.activeandroid.app.Application thì có thể custom file Application hiện tại của bạn như sau:

public class MyApplication extends SomeLibraryApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        Configuration dbConfiguration = new Configuration.Builder(this).setDatabaseName("xxx.db").create();
        ActiveAndroid.initialize(dbConfiguration);
    }
}

3. Thiết kế giao diện

Định nghĩa color trong file res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="white">#FFFFFF</color>
    <color name="black">#000000</color>
    <color name="background_activity">#F5F5F5</color>
    <color name="transparent">#00000000</color>
</resources>

Định nghĩa các chuỗi trong file res/values/strings.xml

<resources>
    <string name="app_name">FriendList</string>
    <string name="action_settings">Settings</string>

    <string name="name">Name</string>
    <string name="phone">Phone</string>
    <string name="add">Add</string>
    <string name="update">Update</string>
    <string name="edit">Edit</string>
    <string name="delete">Delete</string>

    <string name="delete_success">Delete success</string>
    <string name="delete_fail">Delete fail</string>

    <string name="save_success">Save success</string>
    <string name="save_fail">Save fail</string>

    <string name="update_success">Update success</string>
    <string name="update_fail">Update fail</string>

    <string name="please_enter_name">Please enter name</string>
    <string name="please_enter_phone">Please enter phone</string>
</resources>

Định nghĩa layout chính. Layout chính của chúng ta gồm 2 file (Android Studio tự phân ra cho chúng ta khi tạo Project). Nếu các bạn không thích thì hoàn toàn có thể gộp thành 1 file.

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    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:fitsSystemWindows="true"
    tools:context="com.nguyenvanquan7826.friendlist.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>

res/layout/content_main.xml là file chứa nội dung chính của giao diệ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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.nguyenvanquan7826.friendlist.MainActivity"
    tools:showIn="@layout/activity_main">

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/editName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/name"
            android:singleLine="true" />

    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/editPhone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/phone"
            android:inputType="phone"
            android:singleLine="true" />

    </android.support.design.widget.TextInputLayout>

    <Button
        android:id="@+id/btnAdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="@string/add" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

res/layout/item_friend.xml là file giao diện của 1 item trong danh sách

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp"
    android:background="@color/white"
    android:foreground="?android:attr/selectableItemBackground"
    card_view:cardCornerRadius="2dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="4dp">

        <ImageView
            android:id="@+id/icFriend"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_centerVertical="true"
            android:layout_marginRight="4dp"
            android:src="@drawable/ic_friend" />

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@+id/icFriend"
            android:text="@string/name"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tvPhone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/tvName"
            android:layout_toRightOf="@+id/icFriend"
            android:text="@string/phone" />

        <ImageView
            android:id="@+id/btnMore"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:src="@drawable/ic_more" />
    </RelativeLayout>
</android.support.v7.widget.CardView>

4. Tạo lớp dữ liệu – class Friend

Việc tạo class Friend này rất quan trọng, nó không những là class để tạo các đối tượng Friend mà còn là bảng Friend của chúng ta trong database.
Chúng ta tạo class như sau:

package com.nguyenvanquan7826.friendlist;

import com.activeandroid.Model;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;

@Table(name = "tbFriend")
public class Friend extends Model {

    public static final String NAME = "name";
    public static final String PHONE = "phone";

    @Column(name = NAME)
    private String name;

    @Column(name = PHONE)
    private String phone;


    public String getName() {
        return name;
    }

    public Friend setName(String name) {
        this.name = name;
        return this;
    }

    public String getPhone() {
        return phone;
    }

    public Friend setPhone(String phone) {
        this.phone = phone;
        return this;
    }
}

Trong class này các bạn chú ý:

  • class Friend kế thừa từ class Model của com.activeandroid, chúng ta cần kế thừa nó để có thể tạo bảng dữ liệu và dùng các câu lệnh truy vấn cho class Friend này.

  • @Table(name = “tbFriend”) định nghĩa tên bảng là tbFriend trong database. Lệnh này viết ngay trên tên của class các bạn muốn nó là bảng.

  • @Column(name = PHONE) định nghĩa column phone trong bảng tbFriend

5. Tạo adapter

Trong phần này chúng ta cần tạo ra 1 adapter sao cho mỗi item đều có 1 nút menu (nút 3 chấm). Nút này để hiển thị 1 menu (edit, delete) khi click vào. Để thực hiện được điều này chúng ta cần tạo 1 PopupMenu từ file menu.

res/menu/menu_item_friend.xml

<menu 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"
    tools:context="com.nguyenvanquan7826.friendlist.MainActivity">
    <item
        android:id="@+id/menu_edit"
        android:orderInCategory="100"
        android:title="@string/edit"
        app:showAsAction="never" />
    <item
        android:id="@+id/menu_delete"
        android:orderInCategory="100"
        android:title="@string/delete"
        app:showAsAction="never" />
</menu>

Tiếp đến để bắt sự kiện cho menu có thể edit và delete được chúng ta cần tạo 1 interface để truyền thông tin về cho MainActivity xử lý.

AdapterFriend.java

package com.nguyenvanquan7826.friendlist;

import android.content.Context;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

public class AdapterFriend extends RecyclerView.Adapter<AdapterFriend.RecyclerViewHolder> {
    private List<Friend> list;
    private Context context;

    public AdapterFriend(Context context, List<Friend> list) {
        this.context = context;
        this.list = list;
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
        LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
        View itemView = inflater.inflate(R.layout.item_friend, viewGroup, false);
        return new RecyclerViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(RecyclerViewHolder viewHolder, int position) {
        Friend friend = list.get(position);
        viewHolder.tvName.setText(friend.getName());
        viewHolder.tvPhone.setText(friend.getPhone());
    }


    public class RecyclerViewHolder extends RecyclerView.ViewHolder {

        public TextView tvName;
        public TextView tvPhone;

        // for show menu edit and delete
        private PopupMenu popupMenu;

        public RecyclerViewHolder(View itemView) {
            super(itemView);

            tvName = (TextView) itemView.findViewById(R.id.tvName);
            tvPhone = (TextView) itemView.findViewById(R.id.tvPhone);
            itemView.findViewById(R.id.btnMore).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    popupMenu.show();
                }
            });
            setPopup(itemView);
        }

        /*
        * when show popup menu, we can click edit or delete menu item.
        * so we need create a interface to sent position for main activity process
        * */
        private void setPopup(View view) {
            popupMenu = new PopupMenu(context, view);
            popupMenu.getMenuInflater().inflate(R.menu.menu_item_friend, popupMenu.getMenu());
            popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                public boolean onMenuItemClick(MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.menu_edit:
                            if (listener != null) {
                                listener.edit(getAdapterPosition());
                            }
                            break;
                        case R.id.menu_delete:
                            if (listener != null) {
                                listener.del(getAdapterPosition());
                            }
                            break;
                    }
                    return true;
                }
            });
        }
    }

    public interface AdapterFriendListener {
        void del(int position);

        void edit(int position);
    }

    private AdapterFriendListener listener;

    public AdapterFriend setListener(AdapterFriendListener listener) {
        this.listener = listener;
        return this;
    }
}

6. Viết xử lý trong MainActivity

Trong MainActivity chúng ta có 3 công việc chính cần xử lý gồm Thêm, Sửa, Xóa. Việc thêm và sửa thì gần giống nhau, nếu thêm chúng ta tạo mới, nếu sửa chúng ta xem xét vị trí cần sửa qua 1 biến positionEdit.

MainActivity.java

package com.nguyenvanquan7826.friendlist;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.activeandroid.query.Select;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MainActivity extends AppCompatActivity implements AdapterFriend.AdapterFriendListener {

    private Context context;

    private EditText editName;
    private EditText editPhone;
    private Button btnAdd;

    private AdapterFriend adapter;
    private ArrayList<Friend> list;

    /*
    * index of item need edit
    * if positionEdit = -1 then we not need edit item, so when click btnAdd we insert into database
    * if positionEdit >= 0  then we need eidt item, so when click btnAdd we update item inti database
    * */
    private int positionEdit = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;

        setContentView(R.layout.activity_main);
        connectView();
        reloadData();
    }

    private void connectView() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);
        recyclerView.setHasFixedSize(false);
        LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        adapter = new AdapterFriend(context, list = new ArrayList<>());
        adapter.setListener(this);
        recyclerView.setAdapter(adapter);

        editName = (EditText) findViewById(R.id.editName);
        editPhone = (EditText) findViewById(R.id.editPhone);
        btnAdd = (Button) findViewById(R.id.btnAdd);
        btnAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                add();
            }
        });
    }

    private void add() {
        String name = editName.getText().toString().trim();
        String phone = editPhone.getText().toString().trim();

        if (TextUtils.isEmpty(name)) {
            editName.requestFocus();
            Toast.makeText(context, R.string.please_enter_name, Toast.LENGTH_SHORT).show();
            return;
        }
        if (TextUtils.isEmpty(phone)) {
            editPhone.requestFocus();
            Toast.makeText(context, R.string.please_enter_phone, Toast.LENGTH_SHORT).show();
            return;
        }
        Friend friend;
        // we not need edit
        if (positionEdit < 0) {
            friend = new Friend();
        } else { // we need edit
            friend = list.get(positionEdit);
        }

        friend.setName(name).setPhone(phone).save();
        reloadData();

        editName.setText("");
        editPhone.setText("");
        btnAdd.setText(R.string.add);
        editName.requestFocus();

        Toast.makeText(context, R.string.save_success, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void del(int position) {
        Friend.delete(Friend.class, list.get(position).getId());
        reloadData();
        Toast.makeText(context, R.string.delete_success, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void edit(int position) {
        positionEdit = position;
        editName.setText(list.get(position).getName());
        editPhone.setText(list.get(position).getPhone());
        btnAdd.setText(R.string.update);
        editName.requestFocus();
    }

    private void reloadData() {
        List<Friend> ls = new Select().from(Friend.class).execute();
        list.clear();
        list.addAll(ls);
        Collections.reverse(list);
        adapter.notifyDataSetChanged();
    }
}

Ở code trên các bạn chú ý:

  • Để lưu lại hoặc insert 1 bản ghi (1 object) chúng ta chỉ cần gọi phương thức save()

  • Để lấy ra một danh sách các bản ghi chúng ta dùng new Select().from(Friend.class).execute()

  • Để xóa một bản ghi ta dùng phương thức delete.

Trên đây là những hướng dẫn hết sức cơ bản của mình để sử dụng ActiveAndroid thao tác với database trong vòng “một nốt nhạc”. Để có thể hiểu thêm và thực hiện tốt nhiều điều khác các bạn hãy xem wiki của họ

Các bạn có thể download Project FriendList tại đây

Bài viết được thực hiện trong loạt bài hướng dẫn Database trong Android bởi nguyenvanquan7826

Chúc các bạn vui vẻ.