Android实战演练(kotlin版),词汇记录APP

简介: Android实战演练(kotlin版),词汇记录APP

效果图

以下是源代码

build.gradle文件

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
}
android {
    compileSdk 32
    defaultConfig {
        applicationId "com.example.word3"
        minSdk 27
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}
dependencies {
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation 'androidx.room:room-runtime:2.1.0'
    kapt "androidx.room:room-compiler:2.1.0"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
    implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
    implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
    implementation "androidx.navigation:navigation-ui-ktx:2.5.2"
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
}

layout

activity_main.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="match_parent"
    tools:context=".MainActivity">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/navigation" />
</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="60dp"
    android:clickable="true"
    android:orientation="vertical">
    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="MissingConstraints">
        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/constraintLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/guideline2"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="1.0"
            android:background="?selectableItemBackground">
            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/guideline2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintGuide_percent="0.8"
                app:layout_constraintHorizontal_bias="1.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/guideline4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintGuide_percent="0.15"
                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="1"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@+id/guideline4"
                app:layout_constraintHorizontal_bias="0.38"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.412" />
            <TextView
                android:id="@+id/textViewEnglish"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="200dp"
                android:paddingTop="20dp"
                android:text="TextView"
                android:textSize="24dp"
                app:layout_constraintBottom_toTopOf="@+id/textViewChinese"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="@+id/guideline4"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.333"
                app:layout_goneMarginStart="16dp" />
            <TextView
                android:id="@+id/textViewChinese"
                android:layout_width="wrap_content"
                android:layout_height="60dp"
                android:layout_marginEnd="204dp"
                android:paddingLeft="0dp"
                android:paddingTop="40dp"
                android:text="TextView"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.072"
                app:layout_constraintStart_toStartOf="@id/textViewEnglish"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="0.878" />
            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/constraintLayout2"
                android:layout_width="83dp"
                android:layout_height="80dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="@+id/guideline2"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_bias="1.0">
                <Switch
                    android:id="@+id/aSwitchChineseInvisible"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingStart="30dp"
                    android:paddingEnd="15dp"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_bias="0.444"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent"
                    app:layout_constraintVertical_bias="0.484"
                    tools:ignore="TouchTargetSizeCheck,UseSwitchCompatOrMaterialXml" />
            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>

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"
    android:clickable="true">
    <View
        android:id="@+id/divider"
        android:layout_width="2dp"
        android:layout_height="match_parent"
        android:background="?android:attr/listDivider"
        android:paddingTop="5dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@+id/constraintLayout"
        app:layout_constraintStart_toEndOf="@+id/constraintLayout"
        app:layout_constraintTop_toTopOf="parent" />
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:background="@android:drawable/list_selector_background"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/guideline2"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0">
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.15" />
        <TextView
            android:id="@+id/textViewNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="1"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/guideline4"
            app:layout_constraintHorizontal_bias="0.38"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.412" />
        <TextView
            android:id="@+id/textViewEnglish"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="200dp"
            android:text="TextView"
            android:textSize="24dp"
            app:layout_constraintBottom_toTopOf="@+id/textViewChinese"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="@+id/guideline4"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.333"
            app:layout_goneMarginStart="16dp" />
        <TextView
            android:id="@+id/textViewChinese"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="204dp"
            android:text="TextView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.072"
            app:layout_constraintStart_toStartOf="@id/textViewEnglish"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.878" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintLayout2"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="@+id/guideline2"
        app:layout_constraintTop_toTopOf="parent">
        <Switch
            android:id="@+id/aSwitchChineseInvisible"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:paddingStart="20dp"
            android:paddingEnd="15dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.444"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.484"
            tools:ignore="TouchTargetSizeCheck,UseSwitchCompatOrMaterialXml" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>

fragment_add.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:id="@+id/addLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".AddFragment">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加单词"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.485"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.135" />
    <EditText
        android:id="@+id/editTextEnglish"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="English_Word"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="parent"
        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.276" />
    <EditText
        android:id="@+id/editTextChinese"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="中文"
        android:inputType="textPersonName"
        app:layout_constraintBottom_toBottomOf="parent"
        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.406" />
    <Button
        android:id="@+id/buttonSubmit"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Add"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

fragment_words.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:id="@+id/wordsFragmentView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".WordsFragment">
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/floatingActionButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_margin="16dp"
        android:clickable="true"
        android:elevation="8dp"
        android:src="@drawable/ic_baseline_add_24"
        tools:ignore="SpeakableTextPresentCheck" />
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

menu:

main_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/clearData"
        android:title="清空数据" />
    <item
        android:id="@+id/switchViewType"
        android:title="切换视图" />
    <item
        android:id="@+id/app_bar_search"
        android:icon="@android:drawable/ic_menu_search"
        android:title="Search"
        app:actionViewClass="androidx.appcompat.widget.SearchView"
        app:showAsAction="always" />
</menu>

navigation:

navigation.xml

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/navigation"
    app:startDestination="@id/wordsFragment">
    <fragment
        android:id="@+id/addFragment"
        android:name="com.example.word3.AddFragment"
        android:label="Add"
        tools:layout="@layout/fragment_add" />
    <fragment
        android:id="@+id/wordsFragment"
        android:name="com.example.word3.WordsFragment"
        android:label="Words"
        tools:layout="@layout/fragment_words">
        <action
            android:id="@+id/action_wordsFragment_to_addFragment"
            app:destination="@id/addFragment"
            app:enterAnim="@anim/nav_default_enter_anim" />
    </fragment>
</navigation>

values

ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="word_for_view_holder" type="id"/>
</resources>

AddFragment

import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.EditText
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.Navigation
class AddFragment : Fragment() {
    private lateinit var buttonSubmit: Button
    private lateinit var editTextEnglish: EditText
    private lateinit var editTextChinese: EditText
    private lateinit var wordViewModel: WordViewModel
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        var view: View = inflater.inflate(R.layout.fragment_add, container, false)
        buttonSubmit = view.findViewById(R.id.buttonSubmit)
        editTextChinese = view.findViewById(R.id.editTextChinese)
        editTextEnglish = view.findViewById(R.id.editTextEnglish)
        return view
    }
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        buttonSubmit.isEnabled = false
        editTextEnglish.requestFocus()
        wordViewModel= ViewModelProvider(requireActivity()).get(WordViewModel::class.java)
        val imm: InputMethodManager =
            requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.showSoftInput(editTextEnglish, 0)
        editTextChinese.addTextChangedListener(mydetextWatcher())
        editTextEnglish.addTextChangedListener(mydetextWatcher())
        buttonSubmit.setOnClickListener {
            val english:String=editTextEnglish.text.toString().trim()
            val chinese:String=editTextChinese.text.toString().trim()
            val word:Word= Word(english,chinese,false)
            wordViewModel.insertWords(word)
            val navController: NavController = Navigation.findNavController(it)
            navController.navigateUp()
            val imm:InputMethodManager= requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(it.windowToken,0)
        }
    }
    inner class mydetextWatcher : TextWatcher {
        override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        }
        override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
            val english: String = editTextEnglish.text.toString().trim()
            val chinese: String = editTextChinese.text.toString().trim()
            buttonSubmit.isEnabled = english.isNotEmpty() && chinese.isNotEmpty()
        }
        override fun afterTextChanged(p0: Editable?) {
        }
    }
}

MainActivity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.NavigationUI
class MainActivity : AppCompatActivity() {
    private lateinit var navController:NavController
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      navController=supportFragmentManager.findFragmentById(R.id.fragment)?.findNavController() as NavController
       NavigationUI.setupActionBarWithNavController(this,navController)
    }
    override fun onSupportNavigateUp(): Boolean {
        navController.navigateUp()
        return super.onSupportNavigateUp()||findNavController(R.id.fragment).navigateUp()
    }
}

MyAdapter

import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Switch
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
class MyAdapter(var useCardView:Boolean,var wordViewModel: WordViewModel) :androidx.recyclerview.widget.ListAdapter<Word,MyAdapter.ViewHolder>(diffCallback) {
    companion object{
        val diffCallback=object :DiffUtil.ItemCallback<Word>(){
            override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {
                return oldItem.id==newItem.id
            }
            override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean {
                return (oldItem.word == newItem.word &&
                        oldItem.chineseMeaning == newItem.chineseMeaning &&
                        oldItem.isChineseInvisible == newItem.isChineseInvisible)
            }
        }
    }
    override fun onViewAttachedToWindow(holder: ViewHolder) {
        super.onViewAttachedToWindow(holder)
        holder.textViewNumber.text=(holder.absoluteAdapterPosition +1).toString()
    }
    inner class ViewHolder(view:View):RecyclerView.ViewHolder(view){
        val aSwitchChineseInvisible:Switch=view.findViewById(R.id.aSwitchChineseInvisible)
        var textViewNumber:TextView=view.findViewById(R.id.textViewNumber)
        var textViewEnglish:TextView=view.findViewById(R.id.textViewEnglish)
        var textViewChinese:TextView=view.findViewById(R.id.textViewChinese)
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        var layoutInflater:LayoutInflater=LayoutInflater.from(parent.context)
        var itemView:View = if(useCardView){
            layoutInflater.inflate(R.layout.cell_card,parent,false)
        }else{
            layoutInflater.inflate(R.layout.cell_normal,parent,false)
        }
        val holder: ViewHolder =ViewHolder(itemView)
        holder.itemView.setOnClickListener {
            var uri:Uri= Uri.parse("https://m.youdao.com/dict?le=eng&q="+holder.textViewEnglish.text)
            var intent:Intent= Intent(Intent.ACTION_VIEW)
            intent.setData(uri)
            holder.itemView.context.startActivities(arrayOf(intent))
        }
        holder.aSwitchChineseInvisible.setOnCheckedChangeListener { compoundButton, b ->
            var word:Word=holder.itemView.getTag(R.id.word_for_view_holder) as Word
            if (b){
                word.isChineseInvisible=true
                holder.textViewChinese.visibility=View.GONE
                wordViewModel.updateWords(word)
            }else{
                word.isChineseInvisible=false
                holder.textViewChinese.visibility=View.VISIBLE
                wordViewModel.updateWords(word)
            }
        }
        return holder
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        var word:Word=getItem(position)
        holder.itemView.setTag(R.id.word_for_view_holder,word)
        holder.textViewNumber.text= (position+1).toString()
        holder.textViewEnglish.text=word.word
        holder.textViewChinese.text=word.chineseMeaning
        if (word.isChineseInvisible){
            holder.textViewChinese.visibility=View.GONE
            holder.aSwitchChineseInvisible.isChecked=true
        }else{
            holder.textViewChinese.visibility=View.VISIBLE
            holder.aSwitchChineseInvisible.isChecked=false
        }
    }
}

MyApplication

import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
class MyApplication:Application() {
    companion object{
        @SuppressLint("StaticFieldLeak")
        lateinit var context: Context
    }
    override fun onCreate() {
        super.onCreate()
        context=applicationContext
    }
}

Word

@Entity
data class Word (var word: String,var chineseMeaning:String,var isChineseInvisible:Boolean){
    @PrimaryKey(autoGenerate = true)
    var id:Int=0
}

WordDao

import androidx.lifecycle.LiveData
import androidx.room.*
@Dao
interface WordDao {
    @Insert
    fun insertWords( vararg word: Word)
    @Update
    fun updateWords(vararg word: Word)
    @Delete
    fun deleteWords(vararg word: Word)
    @Query("DELETE FROM WORD")
    fun deleteAllWords()
    @Query("SELECT * FROM WORD ORDER BY ID DESC")
    fun getAllWordsLive():LiveData<List<Word>>
    @Query("SELECT * FROM WORD WHERE word LIKE:patten ORDER BY ID DESC")
    fun findWordsWithPatten(patten:String):LiveData<List<Word>>
}

WordDatabase

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database( version = 3, entities = [Word::class],exportSchema = false)
abstract class WordDatabase: RoomDatabase() {
    companion object {
        private var instance: WordDatabase? = null
        @Synchronized
        fun getWordDatabase(context: Context): WordDatabase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(
                context.applicationContext,
                WordDatabase::class.java, "word_database"
            ).allowMainThreadQueries().fallbackToDestructiveMigration()
                .build()
                .apply {
                    instance = this
                }
        }
    }
    abstract fun getWordDao():WordDao
}

WordRepository

import android.os.AsyncTask
import androidx.lifecycle.LiveData
class WordRepository {
   lateinit var wordDao: WordDao
    lateinit var allWordsLive: LiveData<List<Word>>
    init {
        val wordDatabase:WordDatabase=WordDatabase.getWordDatabase(MyApplication.context)
        wordDao=wordDatabase.getWordDao()
        allWordsLive=wordDao.getAllWordsLive()
    }
    fun findWordsWithPatten(patten:String): LiveData<List<Word>>{
        return wordDao.findWordsWithPatten("%"+patten+"%")
    }
    fun insertWords(vararg p0: Word?){
        InsertAsyncTask(wordDao).execute(*p0)
    }
    fun updateWords(vararg p0: Word?){
        UpdateAsyncTask(wordDao).execute(*p0)
    }
    fun deleteWords(vararg p0: Word?){
        DeleteAsyncTask(wordDao).execute(*p0)
    }
    fun deleteAllWords(vararg p0: Word?){
        DeleteAllAsyncTask(wordDao).execute()
    }
    companion object {
        class InsertAsyncTask internal constructor(var wordDao: WordDao) :
            AsyncTask<Word, Void, Void?>() {
            override fun doInBackground(vararg p0: Word?): Void? {
                wordDao.insertWords(*p0 as Array<out Word>)
                return null
            }
        }
        class UpdateAsyncTask internal constructor(var wordDao: WordDao) :
            AsyncTask<Word, Void, Void?>() {
            override fun doInBackground(vararg p0: Word?): Void? {
                wordDao.updateWords(*p0 as Array<out Word>)
                return null
            }
        }
        class DeleteAsyncTask internal constructor(var wordDao: WordDao) :
            AsyncTask<Word, Void, Void?>() {
            override fun doInBackground(vararg p0: Word?): Void? {
                wordDao.deleteWords(*p0 as Array<out Word>)
                return null
            }
        }
        class DeleteAllAsyncTask internal constructor(var wordDao: WordDao) :
            AsyncTask<Void, Void, Void?>() {
            override fun doInBackground(vararg p0: Void?): Void? {
                wordDao.deleteAllWords()
                return null
            }
        }
    }
}

WordsFragment

 

import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.recyclerview.widget.*
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
class WordsFragment : Fragment() {
    private lateinit var wordViewModel: WordViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var myAdapter1: MyAdapter
    private lateinit var myAdapter2: MyAdapter
    private lateinit var floatingActionButton: FloatingActionButton
    private lateinit var filteredWords: LiveData<List<Word>>
    val VIEW_TYPE_SHP:String="view_type_shp"
    val IS_USING_CARD_VIEW:String="is_using_card_view"
    private lateinit var allWords:List<Word>
    lateinit var dividerItemDecoration: DividerItemDecoration
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        wordViewModel = ViewModelProvider(this).get(WordViewModel::class.java)
        myAdapter1 = MyAdapter(false, wordViewModel)
        myAdapter2 = MyAdapter(true, wordViewModel)
    }
   inner class defaultItemAnimator:DefaultItemAnimator(){
        override fun onAnimationFinished(viewHolder: RecyclerView.ViewHolder) {
            super.onAnimationFinished(viewHolder)
            var linearLayoutManager:LinearLayoutManager=recyclerView.layoutManager as LinearLayoutManager
            if (linearLayoutManager!=null){
                var firstPosition:Int=linearLayoutManager.findFirstVisibleItemPosition()
                var lastPosition:Int=linearLayoutManager.findLastVisibleItemPosition()
                for (i in firstPosition until lastPosition+1){
                   var holder: MyAdapter.ViewHolder=recyclerView.findViewHolderForAdapterPosition(i) as MyAdapter.ViewHolder
                    if (holder!=null){
                        holder.textViewNumber.text= (i+1).toString()
                    }
                }
            }
        }
    }
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        recyclerView.itemAnimator=defaultItemAnimator()
        var shp=requireActivity().getSharedPreferences(VIEW_TYPE_SHP,Context.MODE_PRIVATE)
        var viewType:Boolean=shp.getBoolean(IS_USING_CARD_VIEW,false)
        dividerItemDecoration= DividerItemDecoration(requireActivity(),DividerItemDecoration.VERTICAL)
        if (viewType){
            recyclerView.adapter=myAdapter2
        }else{
            recyclerView.adapter=myAdapter1
            recyclerView.addItemDecoration(dividerItemDecoration)
        }
        filteredWords=wordViewModel.allWordsLive
        filteredWords.observe(requireActivity(), Observer { count ->
            var temp:Int=myAdapter1.itemCount
            allWords=count
            if (temp!=count.size){
                if (temp<count.size)
                    recyclerView.smoothScrollBy(0,-200)
                myAdapter1.submitList(count)
                myAdapter2.submitList(count)
            }
        })
        ItemTouchHelper(object :ItemTouchHelper.Callback(){
            override fun getMovementFlags(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder
            ): Int {
                return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN,ItemTouchHelper.START or ItemTouchHelper.END)
            }
            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: RecyclerView.ViewHolder,
                target: RecyclerView.ViewHolder
            ): Boolean {
                Toast.makeText(requireActivity(),"慢一点",Toast.LENGTH_SHORT).show()
                var wordFrom:Word=allWords.get(viewHolder.adapterPosition)
                var wordTo:Word=allWords.get(target.adapterPosition)
                var idTemp:Int=wordFrom.id
                wordFrom.id=wordTo.id
                wordTo.id=idTemp
                wordViewModel.updateWords(wordFrom,wordTo)
                myAdapter1.notifyItemMoved(viewHolder.adapterPosition,target.adapterPosition)
                myAdapter2.notifyItemMoved(viewHolder.adapterPosition,target.adapterPosition)
                return false
            }
            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                var wordToDelete:Word=allWords.get(viewHolder.adapterPosition)
                wordViewModel.deleteWords(wordToDelete)
                Snackbar.make(requireActivity().findViewById(R.id.wordsFragmentView),"删除了一个词汇",Snackbar.LENGTH_SHORT)
                    .setAction("撤销",View.OnClickListener {
                        wordViewModel.insertWords(wordToDelete)
                    })
                    .show()
            }
        }).attachToRecyclerView(recyclerView)
        floatingActionButton.setOnClickListener {
            var navController: NavController = Navigation.findNavController(it)
            navController.navigate(R.id.action_wordsFragment_to_addFragment)
        }
    }
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        var view:View=inflater.inflate(R.layout.fragment_words, container, false)
        recyclerView = view.findViewById(R.id.recyclerView)
        floatingActionButton=view.findViewById(R.id.floatingActionButton)
        recyclerView.layoutManager = LinearLayoutManager(activity)
        recyclerView.adapter = myAdapter1
        setHasOptionsMenu(true)
        return view
    }
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
       super.onCreateOptionsMenu(menu,inflater)
        inflater?.inflate(R.menu.main_menu,menu)
       var searchView:SearchView= menu.findItem(R.id.app_bar_search).actionView as SearchView
        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String?): Boolean {
                return false
            }
            override fun onQueryTextChange(newText: String): Boolean {
                    var patten: String = newText.trim()
                    filteredWords.removeObservers(requireActivity())
                    filteredWords = wordViewModel.findWordsWithPatten(patten)
                    filteredWords.observe(requireActivity(), Observer { count ->
                        var temp = myAdapter1.itemCount
                        allWords=count
                        if (temp != count.size) {
                            myAdapter1.submitList(count)
                            myAdapter2.submitList(count)
                        }
                    })
                return false
            }
        })
    }
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.clearData->AlertDialog.Builder(requireActivity()).apply {
                setTitle("清空数据")
                setPositiveButton("确定"){dialog,which->wordViewModel.deleteAllWords()}
                setNegativeButton("取消"){dialog,which->}
                show()
            }
            R.id.switchViewType->{ var shp=requireActivity().getSharedPreferences(VIEW_TYPE_SHP,Context.MODE_PRIVATE)
                var viewType:Boolean=shp.getBoolean(IS_USING_CARD_VIEW,false)
                var editor:SharedPreferences.Editor=shp.edit()
                if (viewType){
                    recyclerView.adapter=myAdapter1
                    recyclerView.addItemDecoration(dividerItemDecoration)
                    editor.putBoolean(IS_USING_CARD_VIEW,false)
                }else{
                    recyclerView.adapter=myAdapter2
                    recyclerView.removeItemDecoration(dividerItemDecoration)
                    editor.putBoolean(IS_USING_CARD_VIEW,true)
                }
                editor.apply()
            }
        }
        return super.onOptionsItemSelected(item)
    }
}

WordViewModel

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
class WordViewModel: ViewModel() {
    lateinit var wordRepository: WordRepository
    lateinit var allWordsLive: LiveData<List<Word>>
    init {
        wordRepository= WordRepository()
        allWordsLive=wordRepository.allWordsLive
    }
    fun findWordsWithPatten(patten:String): LiveData<List<Word>>{
        return wordRepository.findWordsWithPatten("%"+patten+"%")
    }
    fun insertWords(vararg p0: Word?){
        wordRepository.insertWords(*p0)
    }
    fun updateWords(vararg p0: Word?){
        wordRepository.updateWords(*p0)
    }
    fun deleteWords(vararg p0: Word?){
        wordRepository.deleteWords(*p0)
    }
    fun deleteAllWords(vararg p0: Word?){
        wordRepository.deleteAllWords()
    }
}
目录
相关文章
|
2月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
174 0
安卓项目:app注册/登录界面设计
|
21天前
|
JSON JavaScript 前端开发
harmony-chatroom 自研纯血鸿蒙OS Next 5.0聊天APP实战案例
HarmonyOS-Chat是一个基于纯血鸿蒙OS Next5.0 API12实战开发的聊天应用程序。这个项目使用了ArkUI和ArkTS技术栈,实现了类似微信的消息UI布局、输入框光标处插入文字、emoji表情图片/GIF动图、图片预览、红包、语音/位置UI、长按语音面板等功能。
42 2
|
2月前
|
存储 前端开发 测试技术
Android kotlin MVVM 架构简单示例入门
Android kotlin MVVM 架构简单示例入门
32 1
|
2月前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
24 3
|
2月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
59 4
|
2月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
30 1
|
2月前
|
JavaScript 小程序 开发者
uni-app开发实战:利用Vue混入(mixin)实现微信小程序全局分享功能,一键发送给朋友、分享到朋友圈、复制链接
uni-app开发实战:利用Vue混入(mixin)实现微信小程序全局分享功能,一键发送给朋友、分享到朋友圈、复制链接
409 0
|
2月前
|
Android开发
Android实战之如何快速实现自动轮播图
本文介绍了在 Android 中使用 `ViewPager2` 和自定义适配器实现轮播图的方法,包括添加依赖、布局配置、创建适配器及实现自动轮播等步骤。
56 0
|
2月前
|
Android开发
Android开发显示头部Bar的需求解决方案--Android应用实战
Android开发显示头部Bar的需求解决方案--Android应用实战
23 0
|
2月前
|
Android开发 Kotlin
Android面试题之Kotlin中如何实现串行和并行任务?
本文介绍了 Kotlin 中 `async` 和 `await` 在并发编程中的应用,包括并行与串行任务的处理方法。并通过示例代码展示了如何启动并收集异步任务的结果。
32 0