1 初级使用:Entity + Dao + DataBase
利用 Entity + Dao + DataBase + ScrollView + TextView
1.1 添加依赖
dependencies {
...
// 添加依赖
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"
// 查看数据库内容的依赖,添加后,运行后有个网址,进入可以查看数据库中的内容
debugCompile 'com.amitshekhar.android:debug-db:1.0.0'
}
1.2 添加Entity实体类Word.java
package com.dj.roombasic;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity
public class Word {
@PrimaryKey(autoGenerate = true)
private int id;
@ColumnInfo(name = "english_word")
private String word;
@ColumnInfo(name = "chinese_meaning")
private String chineseMeaning;
public Word(String word, String chineseMeaning) {
this.word = word;
this.chineseMeaning = chineseMeaning;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public String getChineseMeaning() {
return chineseMeaning;
}
public void setChineseMeaning(String chineseMeaning) {
this.chineseMeaning = chineseMeaning;
}
}
1.3 数据库增删改查的地方 WordDao.java
package com.dj.roombasic;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import java.util.List;
@Dao // Database access object
public interface WordDao {
@Insert
void insertWords(Word... words);
@Update
void updateWords(Word... words);
@Delete
void deleteWords(Word... words);
@Query("DELETE FROM WORD")
void deleteAllWords();
@Query("SELECT * FROM WORD ORDER BY ID DESC")
List<Word> getAllWords();
}
1.4 获取数据库的地方 WordDatabase.java
package com.dj.roombasic;
import androidx.room.Database;
import androidx.room.RoomDatabase;
@Database(entities = {Word.class}, version = 1, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
public abstract WordDao getWordDao();
}
1.5 MainActivity.java
创建数据库,实现四个按钮的功能:增,删,改,清空
package com.dj.roombasic;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.room.Room;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.dj.roombasic.databinding.ActivityMainBinding;
import java.util.List;
public class MainActivity extends AppCompatActivity {
WordDatabase wordDatabase;
WordDao wordDao;
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setLifecycleOwner(this);
wordDatabase = Room.databaseBuilder(this, WordDatabase.class, "word_database")
.allowMainThreadQueries() // 强制允许在主线程中使用
.build();
wordDao = wordDatabase.getWordDao();
updateView();
binding.buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word1 = new Word("hello", "你好");
Word word2 = new Word("world", "世界");
wordDao.insertWords(word1, word2);
updateView();
}
});
binding.buttonClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
wordDao.deleteAllWords();
updateView();
}
});
binding.buttonUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word = new Word("GG", "哥哥");
word.setId(20);
wordDao.updateWords(word);
updateView();
}
});
binding.buttonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word = new Word("","");
word.setId(17);
wordDao.deleteWords(word);
updateView();
}
});
}
void updateView(){
List<Word> list = wordDao.getAllWords();
String text = "";
for (int i = 0; i < list.size(); i++) {
Word word = list.get(i);
text += word.getId() + ":"+word.getWord()+"="+word.getChineseMeaning()+"\n";
}
binding.textView.setText(text);
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/buttonClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="clear"
app:layout_constraintBottom_toBottomOf="@+id/buttonUpdate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/buttonUpdate"
app:layout_constraintTop_toTopOf="@+id/buttonUpdate" />
<Button
android:id="@+id/buttonUpdate"
android:layout_width="100dp"
android:layout_height="37dp"
android:text="update"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/buttonClear"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2"
app:layout_constraintVertical_bias="0.691" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.6" />
<ScrollView
android:layout_width="413dp"
android:layout_height="439dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.924">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
</ScrollView>
<Button
android:id="@+id/buttonInsert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="insert"
app:layout_constraintBottom_toTopOf="@+id/buttonUpdate"
app:layout_constraintEnd_toStartOf="@+id/buttonDelete"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<Button
android:id="@+id/buttonDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="delete"
app:layout_constraintBottom_toBottomOf="@+id/buttonInsert"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/buttonInsert"
app:layout_constraintTop_toTopOf="@+id/buttonInsert" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
2 进阶使用:Asynctask + Repository + ViewModel + LiveData
利用 Entity + Dao + DataBase + Asynctask + Repository + ViewModel + LiveData
2.1 使用LiveData存储得到的数据库中的全部内容,自动响应变化
WordDao.java:
@Query("SELECT * FROM WORD ORDER BY ID DESC")
//List<Word> getAllWords();
LiveData<List<Word>> getAllWordsLive(); // LiveData
MainActivity.java:
LiveData<List<Word>> allWordsLive;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setLifecycleOwner(this);
wordDatabase = Room.databaseBuilder(this, WordDatabase.class, "word_database")
.allowMainThreadQueries()
.build();
wordDao = wordDatabase.getWordDao();
// 这里获取LiveData
allWordsLive = wordDao.getAllWordsLive();
//LiveData观察到数据发生改变的时候,会自动呼叫onChanged函数,在这个函数里进行视图刷新,可以省掉刷新视图的函数
allWordsLive.observe(this, new Observer<List<Word>>() {
@Override
public void onChanged(List<Word> words) {
StringBuilder text = new StringBuilder();
for (int i = 0; i < words.size(); i++) {
Word word = words.get(i);
text.append(word.getId()).append(":").append(word.getWord()).append("=").append(word.getChineseMeaning()).append("\n");
}
binding.textView.setText(text.toString());
}
});
...
}
2.2 用单例模式生成数据库,全局唯一
WordDatabase.java
// Singleton 单例模式 多个地方呼叫数据库,只返回同一个实例
@Database(entities = {Word.class}, version = 1, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
private static WordDatabase INSTANCE;
// 加入synchronized,即使有多个线程的客户端同时申请数据库的时候,也不会碰撞,会排队等待
static synchronized WordDatabase getDatabase(Context context){
if(INSTANCE == null){
// context.getApplicationContext()返回全局唯一的context
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDatabase.class, "word_database")
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
public abstract WordDao getWordDao();
}
2.3 Asynctask
对数据库的操作应该放在工作线程,而不是主线程
MainActivity.java
public class MainActivity extends AppCompatActivity {
WordDatabase wordDatabase;
WordDao wordDao;
ActivityMainBinding binding;
LiveData<List<Word>> allWordsLive;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
binding.buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word1 = new Word("hello", "你好");
Word word2 = new Word("World", "世界");
new InsertAsyncTask(wordDao).execute(word1, word2); //创建类的实例,在后台操作数据库
}
});
binding.buttonClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new DeleteAllAsyncTask(wordDao).execute(); //创建类的实例,在后台操作数据库
}
});
binding.buttonUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word = new Word("GG", "哥哥");
word.setId(20);
new UpdateAsyncTask(wordDao).execute(word); //创建类的实例,在后台操作数据库
}
});
binding.buttonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word = new Word("","");
word.setId(17);
new DeleteAsyncTask(wordDao).execute(word); //创建类的实例,在后台操作数据库
}
});
}
}
InsertAsyncTask.java
public class InsertAsyncTask extends AsyncTask<Word, Void, Void>{
private WordDao wordDao;
public InsertAsyncTask(WordDao wordDao){
this.wordDao = wordDao;
}
// 在后台执行任务,此处只用到这么一个函数
@Override
protected Void doInBackground(Word... words) {
wordDao.insertWords(words);
return null;
}
// 后台任务执行之前会呼叫
@Override
protected void onPreExecute() {
super.onPreExecute();
}
// 任务完成时呼叫此函数,一般用来把结果带回主线程
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
// 当进度发生更新的时候会呼叫
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
}
UpdateAsyncTask.java
public class UpdateAsyncTask extends AsyncTask<Word, Void, Void> {
private WordDao wordDao;
public UpdateAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
// 在后台执行任务,此处只用到这么一个函数
@Override
protected Void doInBackground(Word... words) {
wordDao.updateWords(words);
return null;
}
}
DeleteAsyncTask.java
public class DeleteAsyncTask extends AsyncTask<Word, Void, Void> {
private WordDao wordDao;
public DeleteAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
// 在后台执行任务,此处只用到这么一个函数
@Override
protected Void doInBackground(Word... words) {
wordDao.deleteWords(words);
return null;
}
}
DeleteAllAsyncTask.java
// 此处不需要带参数,所以把第一个Word改成Void即可
public class DeleteAllAsyncTask extends AsyncTask<Void, Void, Void> {
private WordDao wordDao;
public DeleteAllAsyncTask(WordDao wordDao) {
this.wordDao = wordDao;
}
// 在后台执行任务,此处只用到这么一个函数
@Override
protected Void doInBackground(Void... voids) {
wordDao.deleteAllWords();
return null;
}
}
}
2.4 ViewModel实现数据和视图的分离
创建WordViewModel.java
package com.dj.roombasic;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import com.dj.roombasic.asynctasks.DeleteAllAsyncTask;
import com.dj.roombasic.asynctasks.DeleteAsyncTask;
import com.dj.roombasic.asynctasks.InsertAsyncTask;
import com.dj.roombasic.asynctasks.UpdateAsyncTask;
import java.util.List;
public class WordViewModel extends AndroidViewModel {
private WordDao wordDao;
private LiveData<List<Word>> allWordsLive;
public WordViewModel(@NonNull Application application) {
super(application);
WordDatabase wordDatabase = WordDatabase.getDatabase(application);
wordDao = wordDatabase.getWordDao();
allWordsLive = wordDao.getAllWordsLive();
}
void insertWords(Word... words){
new InsertAsyncTask(wordDao).execute(words);
}
void updateWords(Word... words){
new UpdateAsyncTask(wordDao).execute(words);
}
void deleteWords(Word... words){
new DeleteAsyncTask(wordDao).execute(words);
}
void deleteAllWords(){
new DeleteAllAsyncTask(wordDao).execute();
}
public LiveData<List<Word>> getAllWordsLive() {
return allWordsLive;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
WordViewModel wordViewModel;// 创建WordViewModel对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setLifecycleOwner(this);
wordViewModel = ViewModelProviders.of(this).get(WordViewModel.class); // 获得实例
// 继续观察数据wordViewModel.getAllWordsLive()
wordViewModel.getAllWordsLive().observe(this, new Observer<List<Word>>() {
@Override
public void onChanged(List<Word> words) {
StringBuilder text = new StringBuilder();
for (int i = 0; i < words.size(); i++) {
Word word = words.get(i);
text.append(word.getId()).append(":").append(word.getWord()).append("=").append(word.getChineseMeaning()).append("\n");
}
binding.textView.setText(text.toString());
}
}); //LiveData观察到数据发生改变的时候,呼叫onChanged函数
binding.buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word1 = new Word("hello", "你好");
Word word2 = new Word("World", "世界");
wordViewModel.insertWords(word1, word2); // 用wordViewModel去修改数据
}
});
binding.buttonClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
wordViewModel.deleteAllWords();
}
});
binding.buttonUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word = new Word("GG", "哥哥");
word.setId(20);
wordViewModel.updateWords(word);
}
});
binding.buttonDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Word word = new Word("","");
word.setId(17);
wordViewModel.deleteWords(word);
}
});
}
}
2.5 Repository实现数据仓库,将数据获取从ViewModel中分离出来
建立WordRepository.java
package com.dj.roombasic;
import android.content.Context;
import androidx.lifecycle.LiveData;
import com.dj.roombasic.asynctasks.DeleteAllAsyncTask;
import com.dj.roombasic.asynctasks.DeleteAsyncTask;
import com.dj.roombasic.asynctasks.InsertAsyncTask;
import com.dj.roombasic.asynctasks.UpdateAsyncTask;
import java.util.List;
public class WordRepository {
private LiveData<List<Word>> allWordsLive;
private WordDao wordDao;
public WordRepository(Context context){
WordDatabase wordDatabase = WordDatabase.getDatabase(context.getApplicationContext());
wordDao = wordDatabase.getWordDao();
allWordsLive = wordDao.getAllWordsLive();
}
public LiveData<List<Word>> getAllWordsLive() {
return allWordsLive;
}
void insertWords(Word... words){
new InsertAsyncTask(wordDao).execute(words);
}
void updateWords(Word... words){
new UpdateAsyncTask(wordDao).execute(words);
}
void deleteWords(Word... words){
new DeleteAsyncTask(wordDao).execute(words);
}
void deleteAllWords(){
new DeleteAllAsyncTask(wordDao).execute();
}
}
WordViewModel.java
package com.dj.roombasic;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import java.util.List;
public class WordViewModel extends AndroidViewModel {
private final WordRepository wordRepository;
public WordViewModel(@NonNull Application application) {
super(application);
wordRepository = new WordRepository(application);
}
// 数据来源:wordRepository
public LiveData<List<Word>> getAllWordsLive(){
return wordRepository.getAllWordsLive();
}
// 数据来源:wordRepository
void insertWords(Word... words){
wordRepository.insertWords(words);
}
// 数据来源:wordRepository
void updateWords(Word... words){
wordRepository.updateWords(words);
}
// 数据来源:wordRepository
void deleteWords(Word... words){
wordRepository.deleteWords(words);
}
// 数据来源:wordRepository
void deleteAllWords(){
wordRepository.deleteAllWords();
}
}
其他地方不需要修改。
3 进阶使用:Recycleview
利用 Entity + Dao + DataBase + Asynctask + Repository + ViewModel + LiveData + Recycleview
MyAdapter.java
package com.dj.roombasic;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{
boolean useCardView;
private WordViewModel wordViewModel;
List<Word> allWords = new ArrayList<>();
public MyAdapter(boolean useCardView, WordViewModel wordViewModel) {
this.useCardView = useCardView;
this.wordViewModel = wordViewModel;
}
public void setAllWords(List<Word> allWords) {
this.allWords = allWords;
}
/**
* 创建ViewHolder时呼叫该函数
*/
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View itemView;
if(useCardView){
itemView = layoutInflater.inflate(R.layout.cell_card_2, parent, false);
}else{
itemView = layoutInflater.inflate(R.layout.cell_normal_2, parent, false);
}
return new MyViewHolder(itemView);
}
/**
* 当ViewHolder和RecycleView绑定时呼叫
*/
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
final Word word = allWords.get(position);
holder.textViewNumber.setText(String.valueOf(position + 1));
holder.textViewEnglish.setText(word.getWord());
holder.textViewChinese.setText(word.getChineseMeaning());
holder.aSwitchChineseInvisible.setOnCheckedChangeListener(null); // 避免发生重用引起的错乱
if (word.isChineseInvisible()) {
holder.textViewChinese.setVisibility(View.GONE);
holder.aSwitchChineseInvisible.setChecked(true);
} else {
holder.textViewChinese.setVisibility(View.VISIBLE);
holder.aSwitchChineseInvisible.setChecked(false);
}
// 点击事件
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("https://m.youdao.com/dict?le=eng&q=" + holder.textViewEnglish.getText());
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
holder.itemView.getContext().startActivity(intent);
}
});
holder.aSwitchChineseInvisible.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
holder.textViewChinese.setVisibility(View.GONE);
word.setChineseInvisible(true);
wordViewModel.updateWords(word);
} else {
holder.textViewChinese.setVisibility(View.VISIBLE);
word.setChineseInvisible(false);
wordViewModel.updateWords(word);
}
}
});
}
/**
* 获得列表元素总个数
*/
@Override
public int getItemCount() {
return allWords.size();
}
/**
* 静态内部类
*/
static class MyViewHolder extends RecyclerView.ViewHolder{
TextView textViewNumber,textViewEnglish,textViewChinese;
Switch aSwitchChineseInvisible;
MyViewHolder(@NonNull View itemView) {
super(itemView);
textViewNumber = itemView.findViewById(R.id.textViewNumber);
textViewEnglish = itemView.findViewById(R.id.textViewEnglish);
textViewChinese = itemView.findViewById(R.id.textViewChinese);
aSwitchChineseInvisible = itemView.findViewById(R.id.switchChineseInvisible);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
WordViewModel wordViewModel;
MyAdapter myAdapter1;
MyAdapter myAdapter2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
binding.setLifecycleOwner(this);
wordViewModel = ViewModelProviders.of(this).get(WordViewModel.class);
myAdapter1 = new MyAdapter(false, wordViewModel);
myAdapter2 = new MyAdapter(true, wordViewModel);
binding.myrecyclerview.setLayoutManager(new LinearLayoutManager(this)); //线性的用LinearLayoutManager,行列的用GridLayoutManager
binding.myrecyclerview.setAdapter(myAdapter1);
binding.switch1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
binding.myrecyclerview.setAdapter(myAdapter2);
}else{
binding.myrecyclerview.setAdapter(myAdapter1);
}
}
});
wordViewModel.getAllWordsLive().observe(this, new Observer<List<Word>>() {
@Override
public void onChanged(List<Word> words) {
int temp = myAdapter1.getItemCount();
myAdapter1.setAllWords(words);
myAdapter2.setAllWords(words);
if(temp != words.size()){
myAdapter1.notifyDataSetChanged();
myAdapter2.notifyDataSetChanged();
}
}
}); //LiveData观察到数据发生改变的时候,呼叫onChanged函数
binding.buttonInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String[] english = {
"Hello",
"World",
"Android",
"Google",
"Studio",
"Project",
"Value"
};
String[] chinese = {
"你好",
"世界",
"安卓",
"谷歌",
"工作室",
"项目",
"价值"
};
for (int i = 0; i < english.length; i++) {
wordViewModel.insertWords(new Word(english[i], chinese[i]));
}
}
});
binding.buttonClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
wordViewModel.deleteAllWords();
}
});
}
}
cell_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="60dp"
android:background="#F9F9F9">
<View
android:id="@+id/divider"
android:layout_width="2dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline6"
app:layout_constraintStart_toStartOf="@+id/guideline6"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textViewNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1" />
<TextView
android:id="@+id/textViewEnglish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="TextView"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="@+id/textViewChinese"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/guideline7"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textViewChinese"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/textViewEnglish"
app:layout_constraintTop_toBottomOf="@+id/textViewEnglish" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="20dp"
app:layout_constraintGuide_percent="0.15" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline6"
app:layout_constraintTop_toTopOf="parent">
<Switch
android:id="@+id/switchChineseInvisible"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:paddingStart="30dp"
android:paddingEnd="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>
cell_card.xml
<?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="80dp"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/divider2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline8"
app:layout_constraintStart_toStartOf="@+id/guideline8"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/textViewNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1" />
<TextView
android:id="@+id/textViewEnglish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="TextView"
android:textSize="24sp"
app:layout_constraintBottom_toTopOf="@+id/textViewChinese"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/guideline9"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textViewChinese"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/textViewEnglish"
app:layout_constraintTop_toBottomOf="@+id/textViewEnglish" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="20dp"
app:layout_constraintGuide_percent="0.15" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline8"
app:layout_constraintTop_toTopOf="parent">
<Switch
android:id="@+id/switchChineseInvisible"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="4dp"
android:paddingStart="30dp"
android:paddingEnd="15dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/myrecyclerview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/buttonClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="clear"
app:layout_constraintBottom_toBottomOf="@+id/switch1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/switch1"
app:layout_constraintTop_toTopOf="@+id/switch1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.84" />
<Button
android:id="@+id/buttonInsert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="insert"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/switch1"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<Switch
android:id="@+id/switch1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cardview"
app:layout_constraintBottom_toBottomOf="@+id/buttonInsert"
app:layout_constraintEnd_toStartOf="@+id/buttonClear"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/buttonInsert"
app:layout_constraintTop_toTopOf="@+id/buttonInsert" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
4 进阶使用:数据库版本管理
在Word.java增加一个字段:
@ColumnInfo(name = "foo_data")
private boolean foo;
public boolean isFoo() {
return foo;
}
public void setFoo(boolean foo) {
this.foo = foo;
}
直接运行工程,报错:
Caused by: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you’ve changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
在WordDatabase.java修改数据库版本为2:
@Database(entities = {Word.class}, version = 2, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
...
}
直接运行工程,报错:
Caused by: java.lang.IllegalStateException: A migration from 1 to 2 was required but not found. Please provide the necessary Migration path via RoomDatabase.Builder.addMigration(Migration …) or allow for destructive migrations via one of the RoomDatabase.Builder.fallbackToDestructiveMigration* methods.
给数据库添加迁移策略,为破坏式迁移fallbackToDestructiveMigration() :会破坏现有数据
@Database(entities = {Word.class}, version = 2, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
private static WordDatabase INSTANCE;
// 加入synchronized,即使有多个线程的客户端同时申请数据库的时候,也不会碰撞,会排队等待
static synchronized WordDatabase getDatabase(Context context){
if(INSTANCE == null){
// context.getApplicationContext()返回全局唯一的context
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDatabase.class, "word_database")
.fallbackToDestructiveMigration()
.build();
}
return INSTANCE;
}
public abstract WordDao getWordDao();
}
不破坏原有数据:
@ColumnInfo(name = "bar_data")
private boolean bar;
public boolean isBar() {
return bar;
}
public void setBar(boolean bar) {
this.bar = bar;
}
WordDatabase.java
修改版本为3,同时增加迁移策略
// Singleton 单例模式 多个地方呼叫数据库,只返回同一个实例
@Database(entities = {Word.class}, version = 3, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
private static WordDatabase INSTANCE;
// 加入synchronized,即使有多个线程的客户端同时申请数据库的时候,也不会碰撞,会排队等待
static synchronized WordDatabase getDatabase(Context context){
if(INSTANCE == null){
// context.getApplicationContext()返回全局唯一的context
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDatabase.class, "word_database")
// .fallbackToDestructiveMigration()
.addMigrations(MIGRATION_2_3)
.build();
}
return INSTANCE;
}
public abstract WordDao getWordDao();
// 版本迁移策略
static final Migration MIGRATION_2_3 = new Migration(2,3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE word ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1");
}
};
}
删除刚刚添加的foo和bar两个字段:
// Singleton 单例模式 多个地方呼叫数据库,只返回同一个实例
@Database(entities = {Word.class}, version = 4, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {
private static WordDatabase INSTANCE;
// 加入synchronized,即使有多个线程的客户端同时申请数据库的时候,也不会碰撞,会排队等待
static synchronized WordDatabase getDatabase(Context context){
if(INSTANCE == null){
// context.getApplicationContext()返回全局唯一的context
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDatabase.class, "word_database")
// .fallbackToDestructiveMigration()
.addMigrations(MIGRATION_3_4)
.build();
}
return INSTANCE;
}
public abstract WordDao getWordDao();
// 版本迁移策略:添加字段
static final Migration MIGRATION_2_3 = new Migration(2,3) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("ALTER TABLE word ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1");
}
};
// 版本迁移策略:删除字段 增加字段较简单,删除字段很麻烦
static final Migration MIGRATION_3_4 = new Migration(3,4) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE word_temp (id INTEGER PRIMARY KEY NOT NULL , english_word TEXT, chinese_meaning TEXT)"); //创建新表
database.execSQL("INSERT INTO word_temp (id, english_word, chinese_meaning) SELECT id, english_word, chinese_meaning FROM word"); // 拷贝
database.execSQL("DROP TABLE word"); // 删除原表
database.execSQL("ALTER TABLE word_temp RENAME to word"); // 修改表名
}
};
}