Tài trợ bài viết này và giới thiệu dịch vụ, sản phẩm, thương hiệu, nhu cầu tuyển dụng của doanh nghiệp đến với cộng đồng.
STDIO Tiếp theo bài viết 'Sử dụng hệ quản trị cơ sở dữ liệu DB4O trên Android', bài viết này sẽ hướng dẫn xây dựng một chương trình trắc nghiệm đơn giản và tập trung vào các giao thức thường gặp khi lập trình một ứng dụng di động. Phần này dành cho các bạn lập trình viên đã có kiến thức nền tảng về Android. Qua bài viết tôi sẽ cố gắng giải thích thật chi tiết cho những bạn mới bắt đầu.
Nội dung bài viết

Giới thiệu

Tiếp theo bài viết Sử dụng hệ quản trị cơ sở dữ liệu DB4O trên Android. Bài viết hướng dẫn xây dựng một chương trình trắc nghiệm đơn giản dành cho các bạn muốn dùng làm các bài tập mẫu trong các môn học.

Nếu bạn xác định bản thân là người viết code chính trong dự án thì bài viết này cung cấp các bước tối thiểu bạn phải làm trước khi phân công làm việc nhóm nhằm hoàn thiện dự án. 

Tiền đề bài viết

Bắt đầu từ bài trước tôi nhận được nhiều yêu cầu về cách hoàn thiện một chương trình Android, cũng có bạn yêu cầu cách kết bảng và truy vấn trong hệ quản trị cơ sở dữ liệu hướng đối tượng. Thực tế hệ cơ sở quản trị dữ liệu hướng đối tượng không hề có bảng, muốn hoàn thiện một chương trình sẽ khó hơn rất nhiều so với việc viết ra khung của nó. Ngoài ra, nhiều đọc giả đặt vấn đề về phương pháp làm việc nhóm để hoàn thành 1 dự án, do đó tôi hoàn thành thêm bài viết này.

Đối tượng hướng đến

  • Dành cho các bạn lập trình viên đã có kiến thức nền tảng về Android. Tôi cũng sẽ cố gắng giải thích thật chi tiết cho bạn đọc mới bắt đầu.
  • Tuy nhiên không có con đường tắt, do đó các bạn mới tiếp cận cần nắm vững kiến thức nền tảng nếu muốn phát triển tiếp.

Thực hiện dự án

Chuẩn bị

  • Một dự án cơ bản đã add thư viện DB4O (phần này đã hướng dẫn trong bài trước). Môi trường thử nghiệm được sử dụng hiện tại MacBook Pro (Retina, 13-inch, Early 2015).
  • Trong bài viết trước, chúng ta đã tạo ra class xử lý và import dữ liệu đơn giản. Trong bài viết này tôi sẽ tập trung vào các giao thức thường gặp khi lập trình một ứng dụng di động. Nó không hẳn là căn bản hay nền tảng, tuy nhiên các bạn lập trình Android cần phải sử dụng nhuần nhuyễn chúng. 

Xây dựng Layout

Đầu tiên chúng ta cần xác định giao diện chính cho ứng dụng. Ở đây tôi xây dựng một layout đơn giản phục vụ cho công việc hiển thị dữ liệu trắc nghiệm.

<?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"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1">


<TextView
    android:text="SAMPLE"
    android:layout_width="match_parent"
    android:id="@+id/txtSample"
    android:layout_height="80dp" />

<LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="150dp">

    <ImageView
        app:srcCompat="@mipmap/ic_launcher"
        android:id="@+id/imgIcon"
        android:layout_weight="1"
        android:layout_height="150dp"
        android:layout_width="60dp" />

    <TextView
        android:text="Question"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/txtQuestion"
        android:layout_weight="1" />

</LinearLayout>

<RadioGroup
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/radioGroup1">

    <RadioButton
        android:text="A"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/radioButton1"
        android:layout_weight="1" />

    <RadioButton
        android:text="B"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/radioButton2"
        android:layout_weight="1" />

    <RadioButton
        android:text="C"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/radioButton3"
        android:layout_weight="1" />

    <RadioButton
        android:text="D"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/radioButton4"
        android:layout_weight="1" />
</RadioGroup>

<Button
    android:text="Chọn"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/btnChoose" />

</LinearLayout>

Như chúng ta đã biết lúc trước, bước tiếp theo là ánh xạ dữ liệu này lên class xử lý. Trong trường hợp này của chúng ta là class MainActivity. Bước này rất quan trọng và chúng ta cần làm đầu tiên trừ trường hợp bất khả kháng. Vì sau khi chương trình chúng ta trở nên lớn và phức tạp thì chúng ta rất dễ sai ở bước này, và chương trình sẽ hoàn toàn không báo lỗi. Khi các giao thức vô tình gọi đến các Item chưa được ánh xạ thì chương trình không thể thực thi. 

public class MainActivity extends AppCompatActivity {

    TextView txtQuestion;
    TextView txtSample;
    RadioButton rda, rdb, rdc, rdd;
    Button btnChoose;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // mappings with interface
        txtQuestion = (TextView) findViewById(R.id.txtQuestion);
        txtSample = (TextView) findViewById(R.id.txtSample);
        rda = (RadioButton) findViewById(R.id.radioButton1);
        rdb = (RadioButton) findViewById(R.id.radioButton2);
        rdc = (RadioButton) findViewById(R.id.radioButton3);
        rdd = (RadioButton) findViewById(R.id.radioButton4);
        btnChoose = (Button) findViewById(R.id.btnChoose); 
        Log.e("mapping interface: ", "OK " );

    }
}

Xây dựng class xử lý dữ liệu

Trong phần này tôi hướng dẫn một số về thao tác cho các bạn mới bắt đầu. Nó sẽ giúp ích rất nhiều về tốc độ lập trình cũng như thể hiện sự chuyên nghiệp. 

Ở đây chúng ta xây dựng class Question với các thành phần như sau:

public class Question {
    public int ID;
    public String SAMPLE;
    public String QUESTION;
    public String ANSWER;
    public String OPTA;
    public String OPTB;
    public String OPTC;
    public String OPTD;
}

Tiếp theo chúng ta bấm chuột phải để sử dụng các giao thức (Constructor, getter and setter)  trong Generate. Sau khi sử dụng giao thức ta có class Question như sau: 

public class Question {
    public int ID;
    public String SAMPLE;
    public String QUESTION;
    public String ANSWER;
    public String OPTA;
    public String OPTB;
    public String OPTC;
    public String OPTD;

    public Question(){}

    public Question(int ID, String SAMPLE, String QUESTION, String ANSWER, String OPTA, String OPTB, String OPTC, String OPTD) {
        this.ID = ID;
        this.SAMPLE = SAMPLE;
        this.QUESTION = QUESTION;
        this.ANSWER = ANSWER;
        this.OPTA = OPTA;
        this.OPTB = OPTB;
        this.OPTC = OPTC;
        this.OPTD = OPTD;
    }

    public int getID() {
        return ID;
    }

    public void setID(int ID) {
        this.ID = ID;
    }

    public String getSAMPLE() {
        return SAMPLE;
    }

    public void setSAMPLE(String SAMPLE) {
        this.SAMPLE = SAMPLE;
    }

    public String getQUESTION() {
        return QUESTION;
    }

    public void setQUESTION(String QUESTION) {
        this.QUESTION = QUESTION;
    }

    public String getANSWER() {
        return ANSWER;
    }

    public void setANSWER(String ANSWER) {
        this.ANSWER = ANSWER;
    }

    public String getOPTA() {
        return OPTA;
    }

    public void setOPTA(String OPTA) {
        this.OPTA = OPTA;
    }

    public String getOPTB() {
        return OPTB;
    }

    public void setOPTB(String OPTB) {
        this.OPTB = OPTB;
    }

    public String getOPTC() {
        return OPTC;
    }

    public void setOPTC(String OPTC) {
        this.OPTC = OPTC;
    }

    public String getOPTD() {
        return OPTD;
    }

    public void setOPTD(String OPTD) {
        this.OPTD = OPTD;
    }
}

Xây dựng class xử lý dữ liệu DbHelper

Đối với một chương trình việc sử dụng và xử lý dữ liệu rất phức tạp, không thể lúc nào chúng ta cũng xử lý tất cả trong 1 class. Class DbHelper được xây dựng để xử lý các dữ liệu các dữ liệu theo thư viện DB4O.

Trong Class tôi xây dựng 3 phương thức:

  • public DbHelper(String dbPath)
    • Nhận đường dẫn dữ liệu kiểu String, khởi tạo dữ file Data và gọi phương thức addQuestions().
  • addQuestions();
    • Khai báo class xử lý dữ liệu và add vào db. Trong class này tôi chỉ add mẫu 2 dữ liệu.
  • List<Question> getAllQuestions();
    • Rất quan trọng vì giao thức này trả về danh sách dữ liệu.

Class DbHelper tổng quát

import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;

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

public class DbHelper {

      ObjectContainer db;

public DbHelper(String dbPath) {
        db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(), dbPath);
        addQuestions();
    }

 private void addQuestions() { //---- Chèn dữ liệu - Insert  Database

        Question q1=new Question(1,"The CPU is large chip …………… the computer",
                "","INSIDE","ONTO","OUE","FROM","INSIDE");
        db.store(q1);
        Question q2=new Question(2,"Data always flows …………. The CPU …………The address bus",
                "","FROM/TO","FROM/TO","TO/TO","TO/FROM","ONTO/FROM");
        db.store(q2);


        Log.e(" AddQuestions: ", "OK " );

    }

 public List<Question> getAllQuestions(){ //----add questions to list

        List<Question> quesList = new ArrayList<Question>();

        try{
            //----Search all QBE----
            Question question = new Question();
            ObjectSet<Question> questions = db.queryByExample(question);
            for (Question quest:questions){
                quesList.add(quest);
                Log.e("Quest value: ", quest.getANSWER());
            }
        }finally{
            db.close();
        }
        Log.e("Test get all Questions: ", "OK " );
        return quesList;
    }

}

Xây dựng MainActivity

Class này có 3 phần chính: 1 Ánh xạ dữ liệu với Interface (Đã hoàn thành), 2 khai báo và xử lý dữ liệu, 3 viết xử lý ứng dụng. 

  • Khai báo và xử lý dữ liệu: Tạo giao thức setQuestionView( ){ } để lấy thông tin và chuyển lên interface. Trong giao thức này mình viết các hàm Log để kiểm tra dữ liệu, các bạn không sử dụng có thể xoá để code nhìn chuyên nghiệp hơn. 
  • Xử lý ứng dụng: Tại button choise gọi sự kiện setOnClickListener( ). Khai báo tính điểm đơn giản và khai báo điều kiện và intent để chuyển sang class ResultActivity. 

Class MainActivity tổng quát

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    List<Question> quesList;
    int score=0;
    int qid=0;
    Question currentQ;
    TextView txtCheck;
    TextView txtQuestion;
    TextView txtSample;
    RadioButton rda, rdb, rdc, rdd;
    Button btnChoose;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Mappings with interface
        txtQuestion = (TextView) findViewById(R.id.txtQuestion);
        txtSample = (TextView) findViewById(R.id.txtSample);
        rda = (RadioButton) findViewById(R.id.radioButton1);
        rdb = (RadioButton) findViewById(R.id.radioButton2);
        rdc = (RadioButton) findViewById(R.id.radioButton3);
        rdd = (RadioButton) findViewById(R.id.radioButton4);
        btnChoose = (Button) findViewById(R.id.btnChoose);
        Log.e("mapping interface: ", "OK " );

        // Create the DB:
        String dbPath =  "/data/data/" + getPackageName() + "/Db4oDatabase.db4o";
        Log.e("String Data Main: ",dbPath);
        //ObjectContainer db = Db4oEmbedded.openFile(Db4oEmbedded.newConfiguration(), dbPath);

        DbHelper data =new DbHelper(dbPath);
        quesList=data.getAllQuestions();
        currentQ=quesList.get(qid);
        setQuestionView();

        // Game Play
        btnChoose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {


                RadioGroup grp=(RadioGroup) findViewById(R.id.radioGroup1);
                RadioButton answer=(RadioButton)findViewById(grp.getCheckedRadioButtonId());
                Log.e("Check answer: ", currentQ.getANSWER()+" "+answer.getText());
                if(currentQ.getANSWER().equals(answer.getText()))
                {
                    score++;
                    Log.d("score", "Your score"+score);
                }
                if(qid<5){
                    currentQ=quesList.get(qid);
                    setQuestionView();
                }else{
                    Log.e("Check Intent: ", "Begin ... " );
                    Intent intent = new Intent(MainActivity.this, ResultActivity.class);
                    Log.e("Check Intent: ", "Set Intent " );
                    Bundle b = new Bundle();
                    b.putInt("score", score); //Your score
                    intent.putExtras(b); //Put your score to your next Intent
                    Log.e("Check Intent: ", "PutExtras OK " );
                    startActivity(intent);
                    Log.e("Check Intent: ", "Start Activity intent " );
                    finish();
                }
            }
        });
    }

    private void setQuestionView() { // -- set value for interface

        Log.e("Set QuestionView: ", "Begin ...  " );
        txtQuestion.setText(currentQ.getQUESTION());
        Log.e("Set QuestionView: ", "Question value: " + currentQ.getQUESTION());
        txtSample.setText(currentQ.getSAMPLE());
        Log.e("Set QuestionView: ", "Sample value: " + currentQ.getSAMPLE());
        rda.setText(currentQ.getOPTA());
        Log.e("Set QuestionView: ", "A value: " + currentQ.getOPTA());
        rdb.setText(currentQ.getOPTB());
        Log.e("Set QuestionView: ", "B value: " + currentQ.getOPTB());
        rdc.setText(currentQ.getOPTC());
        Log.e("Set QuestionView: ", "C value: " + currentQ.getOPTC());
        rdd.setText(currentQ.getOPTD());
        Log.e("Set QuestionView: ", "D value: " + currentQ.getOPTD());
        qid++;
        Log.e("Set QuestionView: ", "End -- OK " );
    }
}

Xây dựng Class ResultActivity

Trong Class này chúng ta cần xây dựng một giao diện layout đơn giản để hiển thị điểm và 1 button để trả về class MainActivity.

Layout activity_result

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:text="Score"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/txtScore" />

    <Button
        android:text="Return"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btnReturn" />
</LinearLayout>

Class ResultActivity

public class ResultActivity extends AppCompatActivity{

    TextView txtScore;
    Button btnReturn;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_result);
        // mapping to interface
        txtScore = (TextView) findViewById(R.id.txtScore);
        btnReturn = (Button) findViewById(R.id.btnReturn);
        Log.e("Check Result Activity: ", "mapping to interface OK " );
        //get score
        Bundle b = getIntent().getExtras();
        int score= b.getInt("score");
        Log.e("Check Result Activity: ", "get score: "+ score );
        // show
        txtScore.setText("score: "+ score);
        Log.e("Check Result Activity: ", "show scre: "+ score );

        btnReturn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(ResultActivity.this, MainActivity.class );
                startActivity(intent);
                finish();
            }
        });
    }
}

AndroidManifest 

Thêm thẻ

<activity android:name=".ResultActivity"></activity>

Lời kết

Trên đây ngoài việc thực thi dự án với hệ quản trị CSDL DB4O, tôi chủ yếu giới thiệu về các giao thức thường dùng khi phát triển một ứng dụng Android. Đây chỉ là một chương trình cơ sở, và cá nhân tôi vẫn chưa kiểm soát hết lỗi phát sinh. Hy vọng qua bài viết này mang lại cái nhìn khái quát cho các bạn lập trình android. Rất vui nếu tiếp tục nhận được các ý kiến đóng góp từ các bạn.

THẢO LUẬN
ĐÓNG