效果图
以下是源代码
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() } }