【JetPack-六】Room与数据库学习笔记补充(Kotlin)

学习视频来源:

  1. https://www.bilibili.com/video/BV1sJ41127EMlongway777
  2. 模型图来源也全截取来自该Up的视频
  3. 书接上文【JetPack-五】Room与数据库学习笔记,简单MVVM(Kotlin)
  4. 项目地址

一.引入adapter

  1. 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)
        }
    }
}
  1. 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. 注意
    1)条目view设置点击波纹效果
    在这里插入图片描述
    2)cardview设置前景效果
    在这里插入图片描述
    3)使用矢量图报错添加问题
    在这里插入图片描述
    在这里插入图片描述
    4)效果图
    在这里插入图片描述
    5)设置跳转Intent
    在这里插入图片描述

二.数据库版本迁移

1. 初始增加结构

  1. 新建foo字段,加入get,set方法
    在这里插入图片描述
  2. 这个会报错
    在这里插入图片描述
  3. 将版本改为2,和上架app版本号一样
    在这里插入图片描述
  4. 运行后再次报错,未进行数据库版本的迁移
    在这里插入图片描述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
}

  1. 测试结果,addMigrations()的默认值1,只在新增列补充时有效,若后续insert时不插入这个,则为0 。使用addMigrations必先要有 @ColumnInfo(name="bar_datas") private var bars :Boolean = false及对应的get/set方法。
    在这里插入图片描述

2. 删除字段结构

  1. Sqlite轻量级数据库是不存在删除字段的操作,只能 1.新建数据库 2.把原旧数据库数据迁移过来 3.删除原数据库 4.新数据库改名
  2. 样例,删除这些字段及对应get/set放
    在这里插入图片描述
  3. 增设新的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界面升级 增设点击隐藏中文意思

  1. 修改Wor段情况,增设新的字段
    在这里插入图片描述
  2. 在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. 界面设计
    1)总体constraintLayout下了个cons以间隔线分割,设定宽度为wrap_content
    2)设置总体的高度以划分每条的高度
    在这里插入图片描述在这里插入图片描述
  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)退出重进后任然保存状态
在这里插入图片描述

  1. 优化,监听器发现条目数据变化也会重新刷新,做一个排除,若是条目总数未变而是其他变了就不刷新(但update可能会有问题)
    在这里插入图片描述
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38304672/article/details/110629009
今日推荐