1. 配置应用

说明: Jetpack 组件实现 (Room,ViewModel,LiveData,Repository,AsyncTack)

1.1 build.gradle 文件引用库

dependencies {def room_version = "2.4.3"implementation "androidx.room:room-runtime:$room_version"annotationProcessor "androidx.room:room-compiler:$room_version"implementation 'androidx.navigation:navigation-fragment:2.5.1'implementation 'androidx.navigation:navigation-ui:2.5.1'
}

1.2 ids.xml 文件下添加 id

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

1.3 添加矢量图标文件, 如: ic_baseline_search_24.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="24dp"android:height="24dp"android:tint="#FFFFFF"android:viewportWidth="24"android:viewportHeight="24"><pathandroid:fillColor="@android:color/white"android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>

1.4 添加其他矢量图标,依次从 Android Studio 系统自动图标中导入

1. ic_baseline_add_24.xml
      2. ic_baseline_chevron_right_24.xml
      3. ic_baseline_delete_forever_24.xml

1.5 menu 文件夹添加菜单布局文件: main_menu.xml

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

1.6 navigation 文件夹添加 Fragment 导航文件: 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"><fragmentandroid:id="@+id/wordsFragment"android:name="com.example.words.WordsFragment"android:label="Words"tools:layout="@layout/fragment_words"><actionandroid:id="@+id/action_wordsFragment_to_addFragment"app:destination="@id/addFragment"app:enterAnim="@anim/nav_default_enter_anim" /></fragment><fragmentandroid:id="@+id/addFragment"android:name="com.example.words.AddFragment"android:label="Add"tools:layout="@layout/fragment_add" />
</navigation>

2. 搭建数据库

2.1 创建实体类, Word.java

@Entity
public class Word {@PrimaryKey(autoGenerate = true)private int id; //主键@ColumnInfo(name = "english_word")private String word;@ColumnInfo(name = "chinese_meaning")private String chineseMeaning;@ColumnInfo(name = "chinese_invisible")private boolean chineseInvisible;
//    @ColumnInfo(name = "foo_data")
//    private boolean foo;
//    @ColumnInfo(name = "bar_data")
//    private boolean bar;public Word(String word, String chineseMeaning) {this.word = word;this.chineseMeaning = chineseMeaning;}public boolean isChineseInvisible() {return chineseInvisible;}public void setChineseInvisible(boolean chineseInvisible) {this.chineseInvisible = chineseInvisible;}
//    public boolean isBar() {
//        return bar;
//    }
//
//    public void setBar(boolean bar) {
//        this.bar = bar;
//    }
//
//    public boolean isFoo() {
//        return foo;
//    }
//
//    public void setFoo(boolean foo) {
//        this.foo = foo;
//    }public int getId() {return id;}public void setId(int id) {this.id = id;}public String getWord() {return word;}public String getChineseMeaning() {return chineseMeaning;}
}

2.2 操作数据库接口类, WordDao.java

@Dao //Database access object
public interface WordDao {@Insertvoid insertWords(Word... words);@Updatevoid updateWords(Word... words);@Deletevoid deleteWords(Word... words);@Query("DELETE FROM WORD")void deleteAllWords();@Query("SELECT * FROM WORD ORDER BY ID DESC")//List<Word> getAllWords();LiveData<List<Word>> getAllWordsLive();@Query("SELECT * FROM WORD WHERE english_word LIKE :pattern ORDER BY ID DESC ")LiveData<List<Word>>findWordsWithPattern(String pattern);
}

2.3 创建维护数据库与表类, WordDatabase.java

//singleton 只允许生成一个实例
@Database(entities = {Word.class}, version = 5, exportSchema = false)
public abstract class WordDatabase extends RoomDatabase {private static WordDatabase INSTANCE;//synchronized: 解决多个线程下调用,创建多个实例问题static synchronized WordDatabase getDatabase(Context context) {if (INSTANCE == null) {//fallbackToDestructiveMigration 把当前的数据清空,创建新的库//addMigrations 添加迁移的策略INSTANCE = Room.databaseBuilder(context.getApplicationContext(), WordDatabase.class, "word_database").addMigrations(migration_4_5).build();}return INSTANCE;}public abstract WordDao getWordDao();//添加字段操作static final Migration migration_2_3 = new Migration(2, 3) {@Overridepublic 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) {@Overridepublic 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");}};//添加字段操作private static final Migration migration_4_5 = new Migration(4, 5) {@Overridepublic void migrate(@NonNull SupportSQLiteDatabase database) {database.execSQL("ALTER TABLE word ADD COLUMN chinese_invisible INTEGER NOT NULL DEFAULT 0");}};
}

2.4 创建数据库, 操作数据库类, WordRepository.java

//Repository 仓库 获取数据的意思
class WordRepository {private WordDao wordDao;private LiveData<List<Word>> allWordsLive;WordRepository(Context context) {WordDatabase wordDatabase = WordDatabase.getDatabase(context);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();}LiveData<List<Word>> getAllWordsLive() {return allWordsLive;}LiveData<List<Word>> findWordsWithPattern(String pattern) {return wordDao.findWordsWithPattern("%" + pattern + "%");}static class InsertAsyncTask extends AsyncTask<Word, Void, Void> {private WordDao wordDao;InsertAsyncTask(WordDao wordDao) {this.wordDao = wordDao;}@Overrideprotected Void doInBackground(Word... words) {wordDao.insertWords(words);return null;}}static class UpdateAsyncTask extends AsyncTask<Word, Void, Void> {private WordDao wordDao;UpdateAsyncTask(WordDao wordDao) {this.wordDao = wordDao;}@Overrideprotected Void doInBackground(Word... words) {wordDao.updateWords(words);return null;}}static class DeleteAsyncTask extends AsyncTask<Word, Void, Void> {private WordDao wordDao;DeleteAsyncTask(WordDao wordDao) {this.wordDao = wordDao;}@Overrideprotected Void doInBackground(Word... words) {wordDao.deleteWords(words);return null;}}static class DeleteAllAsyncTask extends AsyncTask<Void, Void, Void> {private WordDao wordDao;DeleteAllAsyncTask(WordDao wordDao) {this.wordDao = wordDao;}@Overrideprotected Void doInBackground(Void... voids) {this.wordDao.deleteAllWords();return null;}}
}

3. 创建 ViewModel 类, 处理数据操作, WordViewModel.java

public class WordViewModel extends AndroidViewModel {private WordRepository wordRepository = null;public WordViewModel(@NonNull Application application) {super(application);wordRepository = new WordRepository(application);}public LiveData<List<Word>> getAllWordsLive() {return wordRepository.getAllWordsLive();}public LiveData<List<Word>> findWordsWithPattern(String pattern) {return wordRepository.findWordsWithPattern(pattern);}void insertWords(Word... words) {wordRepository.insertWords(words);}void updateWords(Word... words) {wordRepository.updateWords(words);}void deleteWords(Word... words) {wordRepository.deleteWords(words);}void deleteAllWords() {wordRepository.deleteAllWords();}
}

4. 词汇列表页

4.1 创建列表内容管理器布局1: cell_card_2.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="70dp"><androidx.cardview.widget.CardViewandroid:layout_width="0dp"android:layout_height="0dp"android:layout_marginStart="8dp"android:layout_marginTop="8dp"android:layout_marginEnd="8dp"android:foreground="?attr/selectableItemBackground"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><Viewandroid:id="@+id/divider2"android:layout_width="1dp"android:layout_height="0dp"android:layout_marginTop="8dp"android:background="?android:attr/listDivider"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/guideline9"app:layout_constraintStart_toEndOf="@+id/guideline9"app:layout_constraintTop_toTopOf="parent" /><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/constraintLayout"android:layout_width="0dp"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/guideline9"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/guideline11"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintGuide_percent="0.15" /><TextViewandroid:id="@+id/tv_number"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextView"android:textSize="16sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/guideline11"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"tools:text="1" /><TextViewandroid:id="@+id/tv_english"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/tv_chinese"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="@+id/guideline11"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_chinese"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextView"android:textSize="18sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="@+id/tv_english"app:layout_constraintTop_toBottomOf="@+id/tv_english" /></androidx.constraintlayout.widget.ConstraintLayout><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/constraintLayout2"android:layout_width="0dp"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="@+id/guideline9"app:layout_constraintTop_toTopOf="parent"><Switchandroid: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.Guidelineandroid:id="@+id/guideline9"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>
</androidx.constraintlayout.widget.ConstraintLayout>

4.2 创建列表内容管理器布局2: cell_normal_2.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="#FFFFFF"android:foreground="?attr/selectableItemBackground"><Viewandroid:id="@+id/divider"android:layout_width="1dp"android:layout_height="0dp"android:layout_marginTop="8dp"android:layout_marginBottom="8dp"android:background="?android:attr/listDivider"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="@+id/constraintLayout2"app:layout_constraintStart_toStartOf="@+id/guideline7"app:layout_constraintTop_toTopOf="parent" /><androidx.constraintlayout.widget.ConstraintLayoutandroid:id="@+id/constraintLayout2"android:layout_width="0dp"android:layout_height="match_parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/guideline7"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/guideline10"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintGuide_percent="0.15" /><TextViewandroid:id="@+id/tv_number"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextView"android:textSize="16sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/guideline10"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"tools:text="1" /><TextViewandroid:id="@+id/tv_english"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/tv_chinese"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="@+id/guideline10"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_chinese"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="TextView"android:textSize="18sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="@+id/tv_english"app:layout_constraintTop_toBottomOf="@+id/tv_english" /></androidx.constraintlayout.widget.ConstraintLayout><androidx.constraintlayout.widget.ConstraintLayoutandroid: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/guideline7"app:layout_constraintTop_toTopOf="parent"><Switchandroid: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"tools:ignore="UseSwitchCompatOrMaterialXml" /></androidx.constraintlayout.widget.ConstraintLayout><androidx.constraintlayout.widget.Guidelineandroid:id="@+id/guideline7"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintGuide_percent="0.8" />
</androidx.constraintlayout.widget.ConstraintLayout>

4.3 创建列表内容管理器, MyAdapter.java

//内容管理器
public class MyAdapter extends ListAdapter<Word, MyAdapter.MyViewHolder> {private final boolean useCardView;private final WordViewModel viewModel;public MyAdapter(boolean useCardView, WordViewModel viewModel) {super(new DiffUtil.ItemCallback<Word>() {@Overridepublic boolean areItemsTheSame(@NonNull Word oldItem, @NonNull Word newItem) {return oldItem.getId() == newItem.getId();}@Overridepublic boolean areContentsTheSame(@NonNull Word oldItem, @NonNull Word newItem) {return (oldItem.getWord().equals(newItem.getWord()) && oldItem.getChineseMeaning().equals(newItem.getChineseMeaning()) && oldItem.isChineseInvisible() == newItem.isChineseInvisible());}});this.useCardView = useCardView;this.viewModel = viewModel;}@NonNull@Overridepublic 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);}MyViewHolder holder = new MyViewHolder(itemView);holder.itemView.setOnClickListener(view -> {Uri uri = Uri.parse("https://m.youdao.com/m/result?lang=en&word=" + holder.tvEnglish.getText());Intent intent = new Intent(Intent.ACTION_VIEW);intent.setData(uri);holder.itemView.getContext().startActivity(intent);});holder.aSwitchChineseInvisible.setOnCheckedChangeListener((compoundButton, isChecked) -> {Word word = (Word) holder.itemView.getTag(R.id.word_for_view_holder);if (isChecked) {holder.tvChinese.setVisibility(View.GONE);word.setChineseInvisible(true);} else {holder.tvChinese.setVisibility(View.VISIBLE);word.setChineseInvisible(false);}viewModel.updateWords(word);});return holder;}@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {Word word = getItem(position);holder.itemView.setTag(R.id.word_for_view_holder, word);holder.tvNumber.setText(String.valueOf(position + 1));holder.tvEnglish.setText(word.getWord());holder.tvChinese.setText(word.getChineseMeaning());if (word.isChineseInvisible()) {holder.tvChinese.setVisibility(View.GONE);holder.aSwitchChineseInvisible.setChecked(true);} else {holder.tvChinese.setVisibility(View.VISIBLE);holder.aSwitchChineseInvisible.setChecked(false);}}@Overridepublic void onViewAttachedToWindow(@NonNull MyViewHolder holder) {super.onViewAttachedToWindow(holder);holder.tvNumber.setText(String.valueOf(holder.getAdapterPosition() + 1));}static class MyViewHolder extends RecyclerView.ViewHolder {TextView tvNumber, tvEnglish, tvChinese;@SuppressLint("UseSwitchCompatOrMaterialCode")Switch aSwitchChineseInvisible;public MyViewHolder(@NonNull View itemView) {super(itemView);tvNumber = itemView.findViewById(R.id.tv_number);tvEnglish = itemView.findViewById(R.id.tv_english);tvChinese = itemView.findViewById(R.id.tv_chinese);aSwitchChineseInvisible = itemView.findViewById(R.id.switchChineseInvisible);}}
}

4.4 创建词汇列表布局页, 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/words_layout"android:layout_width="match_parent"android:layout_height="match_parent"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recycle_view"android:layout_width="match_parent"android:layout_height="match_parent" /><com.google.android.material.floatingactionbutton.FloatingActionButtonandroid:id="@+id/floatingActionButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|center_horizontal"android:layout_marginBottom="20dp"android:clickable="true"android:focusable="true"android:src="@drawable/ic_baseline_add_24"tools:ignore="SpeakableTextPresentCheck" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

4.5 创建实现列表页, WordsFragment.java

public class WordsFragment extends Fragment {private WordViewModel viewModel;private RecyclerView recyclerView;private MyAdapter myAdapter1, myAdapter2;private LiveData<List<Word>> filteredWords;private static final String VIEW_TYPE_SHP = "view_type_shp";private static final String IS_USING_CARD_VIEW = "is_using_card_view";private List<Word> allWords;private int temp;private boolean undoAction;private DividerItemDecoration dividerItemDecoration;//边线@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {setHasOptionsMenu(true);return inflater.inflate(R.layout.fragment_words, container, false);}@SuppressLint("NonConstantResourceId")@Overridepublic boolean onOptionsItemSelected(@NonNull MenuItem item) {switch (item.getItemId()) {case R.id.clear_data:AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());builder.setTitle("清空数据");builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {viewModel.deleteAllWords();}});builder.setNegativeButton("取消", null);builder.create();builder.show();break;case R.id.switch_type:SharedPreferences shp = requireActivity().getSharedPreferences(VIEW_TYPE_SHP, Context.MODE_PRIVATE);boolean viewType = shp.getBoolean(IS_USING_CARD_VIEW, false);SharedPreferences.Editor editor = shp.edit();if (viewType) {recyclerView.setAdapter(myAdapter1);recyclerView.addItemDecoration(dividerItemDecoration);editor.putBoolean(IS_USING_CARD_VIEW, false);} else {recyclerView.setAdapter(myAdapter2);recyclerView.removeItemDecoration(dividerItemDecoration);editor.putBoolean(IS_USING_CARD_VIEW, true);}editor.apply();break;}return super.onOptionsItemSelected(item);}@Overridepublic void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {super.onCreateOptionsMenu(menu, inflater);inflater.inflate(R.menu.main_menu, menu);SearchView searchView = (SearchView) menu.findItem(R.id.app_bar_search).getActionView();searchView.setMaxWidth(720);searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {@Overridepublic boolean onQueryTextSubmit(String query) {return false;}@Overridepublic boolean onQueryTextChange(String newText) {String pattern = newText.trim();filteredWords.removeObservers(getViewLifecycleOwner()); //...filteredWords = viewModel.findWordsWithPattern(pattern);filteredWords.observe(getViewLifecycleOwner(), words -> {int temp = myAdapter1.getItemCount();allWords = words;if (temp != words.size()) {myAdapter1.submitList(words);myAdapter2.submitList(words);}});return true;}});}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);viewModel = new ViewModelProvider(requireActivity()).get(WordViewModel.class);recyclerView = view.findViewById(R.id.recycle_view);recyclerView.setLayoutManager(new LinearLayoutManager((requireActivity())));myAdapter1 = new MyAdapter(false, viewModel);myAdapter2 = new MyAdapter(true, viewModel);recyclerView.setItemAnimator(new DefaultItemAnimator() {@Overridepublic void onAnimationFinished(@NonNull RecyclerView.ViewHolder viewHolder) {super.onAnimationFinished(viewHolder);LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();if (linearLayoutManager != null) {int firstPosition = linearLayoutManager.findFirstVisibleItemPosition();int lastPosition = linearLayoutManager.findLastVisibleItemPosition();for (int i = firstPosition; i <= lastPosition; i++) {MyAdapter.MyViewHolder holder = (MyAdapter.MyViewHolder) recyclerView.findViewHolderForAdapterPosition(i);if (holder != null) {holder.tvNumber.setText(String.valueOf(i + 1));}}}}});SharedPreferences shp = requireActivity().getSharedPreferences(VIEW_TYPE_SHP, Context.MODE_PRIVATE);boolean viewType = shp.getBoolean(IS_USING_CARD_VIEW, false);dividerItemDecoration = new DividerItemDecoration(requireActivity(), DividerItemDecoration.VERTICAL);if (viewType) {recyclerView.setAdapter(myAdapter2);} else {recyclerView.setAdapter(myAdapter1);recyclerView.addItemDecoration(dividerItemDecoration);}filteredWords = viewModel.getAllWordsLive();filteredWords.observe(getViewLifecycleOwner(), words -> {temp = myAdapter1.getItemCount();allWords = words;if (temp != words.size()) {if (temp < allWords.size() && !undoAction) {//recyclerView.smoothScrollToPosition(0);recyclerView.smoothScrollBy(0, -200);}undoAction = false;//recyclerView.smoothScrollBy(0, -200);//提交的数据列表 会在后台进行差异化比较 根据比对结果, 来刷新界面myAdapter1.submitList(words);myAdapter2.submitList(words);}});new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.START | ItemTouchHelper.END) {@Overridepublic boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
//                Word wordFrom = allWords.get(viewHolder.getAdapterPosition());
//                Word wordTo = allWords.get(target.getAdapterPosition());
//                int idTemp = wordFrom.getId();
//                wordFrom.setId(wordTo.getId());
//                wordTo.setId(idTemp);
//                viewModel.updateWords(wordFrom, wordTo);
//                myAdapter1.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
//                myAdapter2.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());return false;}@Overridepublic void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {Word wordToData = allWords.get(viewHolder.getAdapterPosition());viewModel.deleteWords(wordToData);Snackbar.make(requireActivity().findViewById(R.id.words_layout), "删除了一个词汇", Snackbar.LENGTH_SHORT).setAction("撤销", view12 -> {undoAction = true;viewModel.insertWords(wordToData);}).show();}final Drawable icon = ContextCompat.getDrawable(requireActivity(), R.drawable.ic_baseline_delete_forever_24);final Drawable background = new ColorDrawable(Color.LTGRAY);@Overridepublic void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);View itemView = viewHolder.itemView;assert icon != null;int iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;int iconStart, iconEnd, iconTop, iconBottom;int backStart, backEnd, backTop, backBottom;backTop = itemView.getTop();backBottom = itemView.getBottom();iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;iconBottom = iconTop + icon.getIntrinsicHeight();if (dX > 0) {backStart = itemView.getLeft();backEnd = itemView.getLeft() + (int) dX;background.setBounds(backStart, backTop, backEnd, backBottom);iconStart = itemView.getLeft() + iconMargin;iconEnd = iconStart + icon.getIntrinsicWidth();icon.setBounds(iconStart, iconTop, iconEnd, iconBottom);} else if (dX < 0) {backEnd = itemView.getRight();backStart = itemView.getRight() + (int) dX;background.setBounds(backStart, backTop, backEnd, backBottom);iconEnd = itemView.getRight() - iconMargin;iconStart = iconEnd - icon.getIntrinsicWidth();icon.setBounds(iconStart, iconTop, iconEnd, iconBottom);} else {background.setBounds(0, 0, 0, 0);icon.setBounds(0, 0, 0, 0);}background.draw(c);icon.draw(c);}}).attachToRecyclerView(recyclerView);FloatingActionButton floatingActionButton = view.findViewById(R.id.floatingActionButton);floatingActionButton.setOnClickListener(view1 -> {NavController navController = Navigation.findNavController(view1);navController.navigate(R.id.action_wordsFragment_to_addFragment);});}
}

5. 创建添加词汇页

5.1 添加词汇布局文件, 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" ><TextViewandroid: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_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.1" /><EditTextandroid:id="@+id/et_english"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginStart="16dp"android:layout_marginEnd="16dp"android:ems="10"android:hint="English Word"android:inputType="textPersonName"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.2" /><EditTextandroid:id="@+id/et_chinese"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="@+id/et_english"app:layout_constraintStart_toStartOf="@+id/et_english"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.3" /><Buttonandroid:id="@+id/bt_submit"android:layout_width="0dp"android:layout_height="wrap_content"android:text="确定"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="@+id/et_english"app:layout_constraintStart_toStartOf="@+id/et_english"app:layout_constraintTop_toTopOf="parent"app:layout_constraintVertical_bias="0.4" />
</androidx.constraintlayout.widget.ConstraintLayout>

5.2 创建添加词汇页, AddFragment.java

public class AddFragment extends Fragment {private Button btSubmit;private EditText etEnglish, etChinese;private WordViewModel viewModel;private InputMethodManager imm;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_add, container, false);}@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);FragmentActivity activity = requireActivity();btSubmit = view.findViewById(R.id.bt_submit);etEnglish = view.findViewById(R.id.et_english);etChinese = view.findViewById(R.id.et_chinese);btSubmit.setEnabled(false);etEnglish.requestFocus();imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);imm.showSoftInput(etEnglish, 0);TextWatcher textWatcher = new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}@Overridepublic void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {String english = etEnglish.getText().toString().trim();String chinese = etChinese.getText().toString().trim();btSubmit.setEnabled(!english.isEmpty() && !chinese.isEmpty());}@Overridepublic void afterTextChanged(Editable editable) {}};etEnglish.addTextChangedListener(textWatcher);etChinese.addTextChangedListener(textWatcher);viewModel = new ViewModelProvider(activity).get(WordViewModel.class);btSubmit.setOnClickListener(view1 -> {String english = etEnglish.getText().toString().trim();String chinese = etChinese.getText().toString().trim();Word word = new Word(english, chinese);viewModel.insertWords(word);imm.hideSoftInputFromWindow(view1.getWindowToken(), 0);NavController navController = Navigation.findNavController(view1);navController.navigateUp();});}
}

6. 主页 Activity

6.1 布局文件, 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.FragmentContainerViewandroid:id="@+id/fragmentContainerView"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="match_parent"app:defaultNavHost="true"app:navGraph="@navigation/navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

6.2 创建主页,MainActivity.java

public class MainActivity extends AppCompatActivity {NavController navController;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragmentContainerView);assert navHostFragment != null;navController = navHostFragment.getNavController();NavigationUI.setupActionBarWithNavController(this, navController);}@Overridepublic boolean onSupportNavigateUp() {navController.navigateUp();return super.onSupportNavigateUp();}@Overridepublic void onBackPressed() {super.onBackPressed();navController.navigateUp();}
}

7. 效果图

          

          

Words 词汇记录相关推荐

  1. 【论文写作】图像分割学术论文中常用词汇记录

    养成良好的读论文记笔记习惯,记一些高级的词汇.表达,便于论文写作 学术论文用词 名词 connectivity patterns 连接模式 fashion 方式 preprocessing 预处理 p ...

  2. 如何获取公众号文章并保存有道笔记

    如何获取公众号文章并保存有道笔记 写作目的 1. 获取公众号文章链接 2. 解析公众号文章 3. 保存有道笔记 源代码 最后感想 写作目的 做这个程序是出于自己的学习目的.因为我有学习英语的习惯,每天 ...

  3. 看美剧英文字幕学英语的利器——“深蓝英文字幕助手”简介

    我从初中开始基本上就是一个英语很烂的人,数理化再好有什么用,工作了,结果发现数理化都没啥用,最有用的还是当年学的最烂的英语.于是在2011年年底开始了学习英语的课程,在学习的过程中,外教经常会放英剧美 ...

  4. 《高效阅读-20分钟读懂一本书》笔记

    参考:https://book.douban.com/subject/27199270/ 1 也就是说,所谓的"共振阅读法"就是让自己与作者所写的这本书之间产生共鸣,从而引导出自己 ...

  5. 我是如何有效进行英语听力训练的?

    我是如何有效进行英语听力训练的? 文/温佛佳 本文系作者授权"清南"发布 本文是继<我自己如何学英文>.<我如何帮助学生高效学习英语?| 以〈新概念英语〉为例&g ...

  6. .NET 6 实现敏感词过滤

    一.什么是敏感词过滤? 敏感词过滤是一种处理网络内容的技术,可以检测和过滤出网络中的敏感/违禁词汇.它通过给定的关键字或字符串,判断网络内容是否包含某些敏感信息,从而防止违反法律法规的信息流通. 通常 ...

  7. 信息系统项目文案学习,常见高级词汇整理记录

    文案学习 好的文案可以让一个普通的产品蓬荜生辉,让一个高端的产品直上云霄. 当你重拾小学语文,掌握用词要领,你就能提前升职加薪,走上人生巅峰. 一.互联网装逼词汇 1. 二字动词 复盘 赋能 沉淀 倒 ...

  8. 004_常用词汇句子翻译记录

    一. (1)embrace造句 1. 自1983年起,爱尔兰已经接受了离婚.同性婚姻以及首位同性恋领导人. me:  Since 1983, airland Ireland has already a ...

  9. 机器学习常用术语词汇表

    EOF是一个计算机术语,为End Of File的缩写 ,在操作系统中表示资料源无更多的资料可读取. 刚接触机器学习框架 TensorFlow 的新手们,这篇由 Google 官方出品的常用术语词汇表 ...

最新文章

  1. 500个普通人名_2020年世界500强汽车行业排名:大众公司第一,丰田汽车公司第二...
  2. poj3666(DP+离散化)
  3. mac打开class文件
  4. [原理篇] 逻辑回归
  5. boost::hana::string_c用法的测试程序
  6. I'm genius,用游戏柄控制鼠标
  7. 编写计算表达式(X-Y+25)/Z的值得程序,要求将其商和余数分别放在A、B单元中。(设X和Y是32位无符号数,A、B和Z是16位无符号数,不考虑溢出情况。)
  8. django-rest-swagger显示接口备注内容
  9. 关于缓存穿透,缓存击穿,缓存雪崩,热点数据失效问题的解决方案(转)
  10. iPhone 12 Pro系列变贵的原因在这儿!
  11. java -jar 怎么停止_图解Java日志体系
  12. 一步一步搭建oracle 11gR2 rac+dg之环境准备(二)【转】
  13. erp系统是什么软件
  14. docsify+github/gitee搭建个人在线文档
  15. java怎么看具体被挂起的线程_Java知多少(65)线程的挂起、恢复和终止
  16. [转]Time Tracker Starter Kit 简介
  17. 《高质量程序设计指南——C++/C》(第三版)最新修订
  18. H3C Comware的作用
  19. 20172328 2018-2019《Java软件结构与数据结构》第三周学习总结
  20. UE4UE5 VR开发多人联机RPC的坑

热门文章

  1. 双十一数据背后: 电商助力实体经济数字化转型才是未来方向
  2. 【c++STL——第二讲】pair系列 (常用知识点总结)
  3. 小家电和消费者的距离,只隔着一个闲鱼
  4. 日版 Galaxy Note sc05d 涮机
  5. 我在IBM实习的日子
  6. CCNP-WLAN无线配置实验
  7. 数学分析-第一章-极限理论
  8. html 颜色纯,配色篇(6)基于纯度的配色
  9. 通过手机如何遥控电脑
  10. java dropdownlist_Html.DropDownList 使用