学习视频来源:
- https://www.bilibili.com/video/BV1sJ41127EMlongway777
- 模型图来源也全截取来自该Up的视频
- 书接上文【JetPack-五】Room与数据库学习笔记,简单MVVM(Kotlin)
- 项目地址
一.引入adapter
- Myadapter,设置跳转的intent
class Myadapter(val usecardview:Boolean): RecyclerView.Adapter<Myadapter.MyViewHolder>() {
private var allWords: List<Word> = ArrayList()
fun setAllWords(allWords: List<Word>) {
this.allWords = allWords
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewNumber: TextView
var textViewEnglish: TextView
var textViewChinese: TextView
init {
textViewNumber = itemView.textView_number
textViewEnglish = itemView.textView_english
textViewChinese = itemView.textView_chinese
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val itemView: View
if (usecardview) {
itemView = layoutInflater.inflate(R.layout.cell_card, parent, false)
} else {
itemView = layoutInflater.inflate(R.layout.cell_normal, parent, false)
}
return MyViewHolder(itemView)
}
override fun getItemCount(): Int {
return allWords.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val word = allWords[position]
holder.textViewNumber.text = String.valueOf(position + 1)
holder.textViewEnglish.text = word.getWord()
holder.textViewChinese.text = word.getChineseMeaning()
holder.itemView.setOnClickListener {
val uri = Uri.parse("https://m.youdao.com/dict?le=eng&q=" + holder.textViewEnglish.text)
val intent = Intent(Intent.ACTION_VIEW)
intent.data = uri
holder.itemView.context.startActivity(intent)
}
}
}
- Mac//通过一个Switch按钮切换适配的adapter
class MainActivity : AppCompatActivity() {
//创建Viewmodel实例
lateinit var wordviewmodel: WordViewModel
lateinit var myadapter1: Myadapter
lateinit var myadapter2: Myadapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wordviewmodel=ViewModelProvider(this).get(WordViewModel::class.java)
myadapter1= Myadapter(false)
myadapter2= Myadapter(true)
recycleview1.layoutManager=LinearLayoutManager(this)
recycleview1.adapter=myadapter2
switch1.setOnCheckedChangeListener(object : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
if (isChecked){
recycleview1.adapter=myadapter2
}else{
recycleview1.adapter=myadapter1
}
}
})
wordviewmodel.getAllWordsLive().observe(this,object: Observer<List<Word>>{
override fun onChanged(t: List<Word>?) {
//设置数据源
if (t != null) {
myadapter1.setAllWords(t)
myadapter2.setAllWords(t)
myadapter1.notifyDataSetChanged()
myadapter2.notifyDataSetChanged()
}
}
})
button_insert.setOnClickListener {
val english = arrayOf(
"Hello",
"World",
"Android",
"Google",
"Studio",
"Project",
"Database",
"Recycler",
"View",
"String",
"Value",
"Integer"
)
val chinese = arrayOf(
"你好",
"世界",
"安卓系统",
"谷歌公司",
"工作室",
"项目",
"数据库",
"回收站",
"视图",
"字符串",
"价值",
"整数类型"
)
//循环插入
for (i in 0 until english.size) {
wordviewmodel.insertWords(Word(english[i], chinese[i]))
}
}
button_delete.setOnClickListener {
var word:Word = Word("Word","世界")
word.setId(40)
//worddao.deleteWords(word)
wordviewmodel.deleteWords(word)
}
button_clear.setOnClickListener{
wordviewmodel.deleteAllWords()//副线程的插入
}
button_update.setOnClickListener {
var word:Word = Word("Word","世界")
word.setId(40)
//worddao.updateWords(word)
wordviewmodel.updateWords(word)//副线程的插入
}
}
}
- 注意
1)条目view设置点击波纹效果
2)cardview设置前景效果
3)使用矢量图报错添加问题
4)效果图
5)设置跳转Intent
二.数据库版本迁移
1. 初始增加结构
- 新建foo字段,加入get,set方法
- 这个会报错
- 将版本改为2,和上架app版本号一样
- 运行后再次报错,未进行数据库版本的迁移
1)设置破坏式迁移,数据与结构全清空fallbackToDestructiveMigration()
2)addMigrations()增设置自定义迁移策略,Sqlite没有bool值类型的
,使用Migration抽象方法,该方法参数是版本x-1->x,抽象方法,使用object对象匿名内部类初始化。具体使用sql初始源语设置默认值。
@Database(entities = [Word::class],version = 4,exportSchema = false)
abstract class WordDatabase: RoomDatabase() {
//若有多个entities则返回多个Dao
companion object{
private var INSTANCE: WordDatabase? = null
@Synchronized//如果有多个客户端 且同时instance时保证不会有碰撞 只有一个instance生成
open fun getDatabase(context: Context): WordDatabase? {
//静态类的静态方法写open
if (INSTANCE==null){
INSTANCE=Room.databaseBuilder(context.applicationContext,WordDatabase::class.java,"word_database")
//.fallbackToDestructiveMigration()
.addMigrations(MIGRATION_3_4)
.build()
}
return INSTANCE
}
val MIGRATION_3_4: Migration = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE word ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1")
}
}
}
abstract fun getWordDao(): WordDao
}
- 测试结果,addMigrations()的默认值1,只在新增列补充时有效,若后续insert时不插入这个,则为0 。使用addMigrations必先要有
@ColumnInfo(name="bar_datas") private var bars :Boolean = false
及对应的get/set方法。
2. 删除字段结构
Sqlite轻量级数据库是不存在删除字段的操作,只能 1.新建数据库 2.把原旧数据库数据迁移过来 3.删除原数据库 4.新数据库改名
- 样例,删除这些字段及对应get/set放
- 增设新的Migration,分成4步
@Database(entities = [Word::class],version = 6,exportSchema = false)
abstract class WordDatabase: RoomDatabase() {
//若有多个entities则返回多个Dao
companion object{
private var INSTANCE: WordDatabase? = null
@Synchronized//如果有多个客户端 且同时instance时保证不会有碰撞 只有一个instance生成
open fun getDatabase(context: Context): WordDatabase? {
//静态类的静态方法写open
if (INSTANCE==null){
INSTANCE=Room.databaseBuilder(context.applicationContext,WordDatabase::class.java,"word_database")
//.fallbackToDestructiveMigration()
.addMigrations(MIGRATION_5_6)
.build()
}
return INSTANCE
}
val MIGRATION_4_5: Migration = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE word ADD COLUMN bar_datas INTEGER NOT NULL DEFAULT 1")
}
}
val MIGRATION_5_6: Migration = object : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) {
//text类型 不是string
//创建表
database.execSQL("CREATE TABLE word_temp (id INTEGER PRIMARY KEY NOT NULL ,english_word TEXT," +
"chinese_meaning TEXT)");
//select旧表数据进入新表
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");
}
}
}
abstract fun getWordDao(): WordDao
}
3. UI界面升级 增设点击隐藏中文意思
- 修改Wor段情况,增设新的字段
- 在WordDatabase进行版本控制
1)更改数据库版本
2)增设并指定新的Migration版MIGRATION_6_7
val MIGRATION_6_7: Migration = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE word ADD COLUMN chinese_invisible INTEGER NOT NULL DEFAULT 0")
}
}
- 界面设计
1)总体constraintLayout下了个cons以间隔线分割,设定宽度为wrap_content
2)设置总体的高度以划分每条的高度
- 来到adapter
1)设置Holder
2)onBindViewHolder方法中设定初始化呈现方式
3)动态点击切换时,底层和界面都要做相应改动,这步需要viewmodel,所以新建adapter还要viewmodel的对象
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201206154951387.png
holder.aSwitchChineseInvisible!!.setOnCheckedChangeListener {
buttonView, isChecked ->
if (isChecked) {
holder.textViewChinese.visibility = View.GONE
word.setChineseInvisible(true)
wordviewmodel.updateWords(word)
} else {
holder.textViewChinese.visibility = View.VISIBLE
word.setChineseInvisible(false)
wordviewmodel.updateWords(word)
}
}
4)这里我们遇到了之前遇到的问题,RecycleView的界面试图会重用,所以binder前想将其置空
5)退出重进后任然保存状态
- 优化,监听器发现条目数据变化也会重新刷新,做一个排除,若是条目总数未变而是其他变了就不刷新(但update可能会有问题)