今天要來聊聊Android JetPack 中的 Room DataBase的部分

以及配合Stetho監視 Room資料庫內容這回事(・ωー)~☆

 

之前我曾經發過一個講述使用SQLite DataBase的部分

在這裡->碼農日常-『Android studio』SQLite資料庫建立、資料表建立與操作以及Stetho工具

 

2021/3/20更新

如果有需要知道Room資料庫遷移(新增資料欄位)的方法的話,請參考這篇

->碼農日常-『Android studio』Room database Migrate 資料庫版本遷移

此外目前本篇提供的Git連結已更新為遷移後的狀態,如果要看本篇的內容,也請點這篇文章,並拉到第4大段

文章有教學如何看到Github內舊的程式碼哦

 

當時在寫這篇的時候,就想說總有一天一定要寫一篇Room資料庫的部分

於是...我就寫了(´・з・)

 

關於這篇閒聊一下~因為我想說喔之前寫了一篇SQLite的文章

所以這次我想就直接用那篇的介面以及那篇的功能直接用Room框架實現

 

呃...當然啦,也不是說要看懂這篇文章就一定得回去看我那篇文章

介面以及載入Library都會重講哦(`・ω・´)+

 

OK,那就來看看今天要做的內容吧d(>_・ )

 

介面

Screenshot_20200419-131653_RoomDataBaseExample

新增資料

Gif_20200419131253595_by_gifguru

 

修改資料

Gif_20200419131224743_by_gifguru

 

刪除資料

Gif_20200419131154329_by_gifguru

 

GitHub

https://github.com/thumbb13555/RoomDataBaseExample


 

1.載入相關的庫

統整一下今天要使用的庫

1.Room本身要用的庫->Room資料庫官方

2.Facebook的Stetho 資料庫監視庫->Facebook stetho

3.lifecycle庫->Lifecycle官方

 

因為今天要載入的東西多,我就直接給你需要的內容就好(但是版本要自己去注意哦(・ωー)~☆)

    def room_version = "2.2.5"
    def lifecycle_version = "2.2.0"

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    androidTestImplementation "androidx.room:room-testing:$room_version"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"

    implementation 'com.facebook.stetho:stetho:1.5.1'

 

載入方法

打開build.gradle(Module: app)

截圖 2020-04-19 下午2.49.21

 

將上述內容全部載入至此(dependencies)

截圖 2020-04-19 下午2.51.42

 

按下Sync Now即完成

截圖 2020-04-19 下午2.58.42

 

2.建立畫面

這次想存入的內容跟前篇一樣,我預計存入

1.姓名(String)

2.電話(String)

3.興趣(String)

4.其他(String)

四個欄位

 

請原諒我都使用String變數QQ,做完才發現

 

我直接PO介面Xml給各位,直接複製吧!

activity.main.xml

 

3.建立需要的類別

完成了以上的功能之後,可以先試著執行一下,看看有沒有什麼奇怪的毛病(´・д・`)

這時候應該是可以順利執行,但畫面不會有東西就是

再來我們先建立需要的類別

除了原廠給的MainActivity.java之外

還要建立三個類別

分別為

1.連接主介面與DB的抽象類別

2.設置操作資料庫SQL語法的DAO-Interface

3.Getter-Setter的類別

關於使用Getter-Setter的基本觀念可以參照我這篇文章

->碼農日常-『Java』List<GetterSetter> 如何在陣列中使用Getter Setter

 

先PO建立起來會長怎麼樣

截圖 2020-04-19 下午3.26.47

 

那一個一個建立,先從資料夾開始吧(笑)

通常第二層資料夾如果在做大型專案時,是肯定會加入的東西

為了方便分類....

如果客倌是學生的話,我是不知道現在的學生會不會資料夾分類啦...至少我遇過的學生普遍沒這習慣

通常因為Room在製作時最少都要三個類別,因此我會建議放一個資料夾包起來

 

資料夾建立

在你要建立資料夾的那一層點右鍵->New->Package

就完成建立囉!

截圖 2020-04-19 下午3.31.11

 

建立類別檔大家可能沒什麼問題,建立抽象類別的話可能有些人比較陌生

首先一樣按照建立新的類別的方法到這個介面為止(右鍵->New->Java Class)

截圖 2020-04-19 下午3.37.02

 

然後注意⚠️在底下Modifiers的部分

點擊Abstract(抽象的),按下OK

截圖 2020-04-19 下午3.37.53

 

這樣就完成一個抽象類別了

再來是DAO 的Interface的部分

 

一樣是到這個畫面

截圖 2020-04-19 下午3.37.02

然後點擊Kind的下拉式選單

選擇Interface就完成囉

截圖 2020-04-19 下午3.45.19

最後在建立一個一般的類別,請直接建立吧

 

完成後就如同這個圖

截圖 2020-04-19 下午3.26.47

 

4.撰寫需要的內容

首先先建立Getter-Setter的檔案好了

基本上設置方法請參照Getter-Setter的作法

這邊我的類別檔取名為MyData.java,大家自由取名就好

直接PO程式

MyData.java

 

再來是抽象類別的部分

一樣PO全部

DataBase.java

 

最後是Interface內容

DataUao.java

 

Interface基本上就是用來設置SQL語法的地方,跟SQLite大同小異

但是他提供了更簡單的方法來設置在SQLite中操作的各種語法

我已經將常用的新增、修改、刪除、撈取等四種常用方法都寫進去了,請自由使用(・ω・)b

 

5.寫功能

OK接下來是重頭戲

在剛剛上面的內容因為我覺得沒什麼特別好說的,所以我就只放註解沒特別解釋

接下來的內容將會一段一段講,當然沒興趣看我說的話直接跳去最後面複製就好(・∀・)

((其實如果不需要知道我程式在講什麼的話可以直接從我的Git將專案複製到自己的編輯器內就好....

 

將整個重點來到Main.Activity.java

製作步驟大致如下

1.連接所有的介面元件及載入資料庫監視軟體Stetho

2.製作RecyclerView的適配器Adapter以及內容方法

3.新增資料

4.撈取資料顯示於介面上

5.修改資料

6.製作左右滑動刪除

7.刪除資料

 

ㄎㄅ..七個步驟,會寫死我自己_(:3」∠)_←我說網誌

 

首先是連接介面...老掉牙的東西不再解釋

紅字的全域變數記得加入~

public class MainActivity extends AppCompatActivity {
    MyAdapter myAdapter;
    MyData nowSelectedData;//取得在畫面上顯示中的資料內容

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Stetho.initializeWithDefaults(this);//設置資料庫監視

        Button btCreate = findViewById(R.id.button_Create);
        Button btModify = findViewById(R.id.button_Modify);
        Button btClear = findViewById(R.id.button_Clear);
        EditText edName = findViewById(R.id.editText_Name);
        EditText edPhone = findViewById(R.id.editText_Phone);
        EditText edHobby = findViewById(R.id.editText_Hobby);
        EditText edElseInfo = findViewById(R.id.editText_else);
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        .
        .
        .
        }
}

 

製作RecyclerView的適配器(Adapter)

再來製作RecyclerView要用的Adapter

private static class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

        private List<MyData> myData;
        private Activity activity;
        private OnItemClickListener onItemClickListener;

        public MyAdapter(Activity activity, List<MyData> myData) {
            this.activity = activity;
            this.myData = myData;
        }
        /**建立對外接口*/
        public void setOnItemClickListener(OnItemClickListener onItemClickListener){
            this.onItemClickListener = onItemClickListener;
        }

        public class ViewHolder extends RecyclerView.ViewHolder {
            TextView tvTitle;
            View view;
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                tvTitle = itemView.findViewById(android.R.id.text1);
                view = itemView;
            }
        }
        /**更新資料*/
        public void refreshView() {
            new Thread(()->{
                List<MyData> data = DataBase.getInstance(activity).getDataUao().displayAll();
                this.myData = data;
                activity.runOnUiThread(() -> {
                    notifyDataSetChanged();
                });
            }).start();
        }
        /**刪除資料*/
        public void deleteData(int position){
            new Thread(()->{
                DataBase.getInstance(activity).getDataUao().deleteData(myData.get(position).getId());
                activity.runOnUiThread(()->{
                    notifyItemRemoved(position);
                    refreshView();
                });
            }).start();
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(android.R.layout.simple_list_item_1, null);
            return new ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.tvTitle.setText(myData.get(position).getName());
            holder.view.setOnClickListener((v)->{
                onItemClickListener.onItemClick(myData.get(position));
            });

        }
        @Override
        public int getItemCount() {
            return myData.size();
        }
        /**建立對外接口*/
        public interface OnItemClickListener {
            void onItemClick(MyData myData);
        }

    }

為了程式美觀(自認為XD)我將所有跟資料庫操作的方法都寫在Adapter內

主要就是重新撈資料與刪除資料的部分

refreshView()的這一支副程式,他的重點就是重新抓取資料庫內所有資料並顯示

藍色部分則是建立一個內部的InterFace方法,將點擊選取到的資料傳出去(Adapter)外面

新增資料

因為沒有資料沒辦法驗證撈取( ´_ゝ`)所以先新增一筆資料

我把它寫在btCreate的按鈕內

        /**新增資料*/
        btCreate.setOnClickListener((v -> {
            new Thread(() -> {
                String name = edName.getText().toString();
                String phone = edPhone.getText().toString();
                String hobby = edHobby.getText().toString();
                String elseInfo = edElseInfo.getText().toString();
                if (name.length() == 0) return;//如果名字欄沒填入任何東西,則不執行下面的程序
                MyData data = new MyData(name, phone, hobby, elseInfo);
                DataBase.getInstance(this).getDataUao().insertData(data);
                runOnUiThread(() -> {
                    myAdapter.refreshView();
                    edName.setText("");
                    edPhone.setText("");
                    edHobby.setText("");
                    edElseInfo.setText("");
                });
            }).start();
        }));

這左一按鈕的點擊事件

抓取四個EditText內容,存入MyData類別,最後在統一輸入到資料庫

綠色部分是更新資料,因為目前還沒寫Adapter,所以暫時先註解掉

((不然閃退的話不要來幹我.....(`Д´*)

另外紅字部分,這很重點~~~~

這是在Room操作最有名的一句話

凡是所有Room的操作都要寫在子執行緒!

凡是所有Room的操作都要寫在子執行緒!!

凡是所有Room的操作都要寫在子執行緒!!!

子執行緒是什麼?就是背景執行啦

因為撈資料的行為本身是耗時操作,所以一定得寫(其實是不寫會閃退....

這部分的話看你要用Handler、Thread、AsyneTask都可以,看你熟悉哪個,我示範用Thread

這時候可以打開Stetho,看看資料有沒有進去

Screenshot_20200418-155200_RoomDataBaseExample

截圖 2020-04-18 下午3.51.46

看到這個畫面就是資料已輸入囉(・ωー)~☆

 

撈取資料並顯示

這時候可以先把剛才綠色註解取消掉,因為要來處理建立Adapter了XD

請在onCreate內的recyclerview下方加入這些

public class MainActivity extends AppCompatActivity {
    MyAdapter myAdapter;
    MyData nowSelectedData;//取得在畫面上顯示中的資料內容

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Stetho.initializeWithDefaults(this);//設置資料庫監視

        Button btCreate = findViewById(R.id.button_Create);
        Button btModify = findViewById(R.id.button_Modify);
        Button btClear = findViewById(R.id.button_Clear);
        EditText edName = findViewById(R.id.editText_Name);
        EditText edPhone = findViewById(R.id.editText_Phone);
        EditText edHobby = findViewById(R.id.editText_Hobby);
        EditText edElseInfo = findViewById(R.id.editText_else);
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));//設置分隔線
        setRecyclerFunction(recyclerView);//設置RecyclerView左滑刪除
        new Thread(() -> {
            List<MyData> data = DataBase.getInstance(this).getDataUao().displayAll();
            myAdapter = new MyAdapter(this, data);
            runOnUiThread(() -> {
                recyclerView.setAdapter(myAdapter);
            });
        }).start();

        /**新增資料*/
        btCreate.setOnClickListener((v -> {
            new Thread(() -> {
                String name = edName.getText().toString();
                String phone = edPhone.getText().toString();
                String hobby = edHobby.getText().toString();
                String elseInfo = edElseInfo.getText().toString();
                if (name.length() == 0) return;//如果名字欄沒填入任何東西,則不執行下面的程序
                MyData data = new MyData(name, phone, hobby, elseInfo);
                DataBase.getInstance(this).getDataUao().insertData(data);
                runOnUiThread(() -> {
                    myAdapter.refreshView();
                    edName.setText("");
                    edPhone.setText("");
                    edHobby.setText("");
                    edElseInfo.setText("");
                });
            }).start();
        }));

     }//onCreate
   .
   .
   .

}

綠色部分是設置RecyclerView的部分,紅色是撈取資料庫;最後藍色是設置Adapter(別忘了他有全域變數在最頂!)

這時候在執行一次,就會有資料囉!而新增一筆資料後畫面也會同步顯示資料~

Gif_20200419131253595_by_gifguru

 

修改資料

修改資料有兩個部分

1.點擊RecyclerView內容取得資料

2.修改後並更新

首先是點擊RecyclerView後取得資料

最早在Adapter內已經寫一個如何取得資料的方法了

在上方製作RecyclerView的適配器(Adapter)內的藍字部分...請跳回去參考

接著我們再onCreate中初始化RecyclerView Adapter內寫入該點擊事件

public class MainActivity extends AppCompatActivity {
    MyAdapter myAdapter;
    MyData nowSelectedData;//取得在畫面上顯示中的資料內容

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Stetho.initializeWithDefaults(this);//設置資料庫監視
        (略)

        /**=======================================================================================*/
        /**設置修改資料的事件*/
        btModify.setOnClickListener((v) -> {
            new Thread(() -> {
                if(nowSelectedData ==null) return;//如果目前沒前台沒有資料,則以下程序不執行
                String name = edName.getText().toString();
                String phone = edPhone.getText().toString();
                String hobby = edHobby.getText().toString();
                String elseInfo = edElseInfo.getText().toString();
                MyData data = new MyData(nowSelectedData.getId(), name, phone, hobby, elseInfo);
                DataBase.getInstance(this).getDataUao().updateData(data);
                runOnUiThread(() -> {
                    edName.setText("");
                    edPhone.setText("");
                    edHobby.setText("");
                    edElseInfo.setText("");
                    nowSelectedData = null;
                    myAdapter.refreshView();
                    Toast.makeText(this, "已更新資訊!", Toast.LENGTH_LONG).show();
                });
            }).start();

        });
       
        /**新增資料*/
        (略)

        /**=======================================================================================*/
        /**初始化RecyclerView*/
        new Thread(() -> {
            List<MyData> data = DataBase.getInstance(this).getDataUao().displayAll();
            myAdapter = new MyAdapter(this, data);
            runOnUiThread(() -> {
                recyclerView.setAdapter(myAdapter);
                /**===============================================================================*/
                myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {//原本的樣貌
                    @Override
                    public void onItemClick(MyData myData) {}
                });
                /**===============================================================================*/
                /**取得被選中的資料,並顯示於畫面*/
                myAdapter.setOnItemClickListener((myData)-> {//匿名函式(原貌在上方)
                    nowSelectedData = myData;
                    edName.setText(myData.getName());
                    edPhone.setText(myData.getPhone());
                    edHobby.setText(myData.getHobby());
                    edElseInfo.setText(myData.getElseInfo());
                });
                /**===============================================================================*/
            });
        }).start();
        /**=======================================================================================*/

    }

這時候執行看看,就會出現想要的效果囉!

Gif_20200419131224743_by_gifguru

 

撰寫左右滑動刪除

這個我就不多解釋,詳細請參考這篇...

->碼農日常-『Android studio』基本RecyclerView 用法-3 RecyclerView上下滑動排序與側滑刪除(RecyclerView Swipe)

我直接貼副程式

/**設置RecyclerView的左滑刪除行為*/
    private void setRecyclerFunction(RecyclerView recyclerView){
        ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.Callback() {//設置RecyclerView手勢功能
            @Override
            public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
                return makeMovementFlags(0,ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT);
            }

            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder       target) {
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                int position = viewHolder.getAdapterPosition();
                switch (direction){
                    case ItemTouchHelper.LEFT:
                    case ItemTouchHelper.RIGHT:
                        myAdapter.deleteData(position);
                        break;

                }
            }
        });
        helper.attachToRecyclerView(recyclerView);
    }

直接複製貼上在onCreate外部就好

 

然後在onCreate內的RecyclerView底下綁定給RecyclerView

setRecyclerFunction(recyclerView);//設置RecyclerView左滑刪除

 

紅字部分就是刪除資料的方法囉~之前已經把副程式寫在Adapter中了,直接引用就好!

這時候按下執行,就會有功能了!

Gif_20200419131154329_by_gifguru

也記得觀察一下資料庫有沒有動靜哦!(以我的程式而言如果刪沒成功這筆資料會再跑出來...)

 

最後是MainActivity.java的全部程式

另外我還有在程式內加入一點防錯機制跟清除所選中的資料顯示,再麻煩細心去加入囉!


 

結語

好累...總算打完了( ´_ゝ`)

我最近是因為覺得自己都發一些太簡單的東西,所以才想說這週來發比較麻煩的東西XD

原本像這種的我都會挑連假寫...不過事實證明一個普通假日也寫得完...

這篇文章我星期六完成程式碼,週日我從早上寫到下午六點,整整花了整個下午QQ

ok,不過這是我個人問題,也許之後能再寫更快也說不定(・∀・)

總之希望大家能在這裡學習到Room DataBase的基本使用方法

如果在這邊會了Room卻還不會SQLite的話,可以再跳回去這篇參考哦

->碼農日常-『Android studio』SQLite資料庫建立、資料表建立與操作以及Stetho工具

 

那這篇到這邊,希望文章有幫助到大家~

7a4f7a69c8e3a01d8edcdec07f291ba3

 

arrow
arrow

    碼農日常 發表在 痞客邦 留言(5) 人氣()