代码之家  ›  专栏  ›  技术社区  ›  Alexander Farber

如何使用具有相同片段的4个简单ViewModels?

  •  0
  • Alexander Farber  · 技术社区  · 6 年前

    我有一个应用程序,它在一个片段中显示4个单词列表(重用同一个类!):

    • 两个字母的单词
    • 包含俄语的单词
    • 包含俄语的单词 硬标志

    Screenshot

    目前我的 ViewModel 将所有4个列表存储为 LiveData

    public class WordsViewModel extends AndroidViewModel {
    
        private LiveData<List<Word>> mWords2;
        private LiveData<List<Word>> mWords3;
        private LiveData<List<Word>> mWordsHard;
        private LiveData<List<Word>> mWordsEh;
    
        public WordsViewModel(Application app) {
            super(app);
            mWords2    = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(2);
            mWords3    = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(3);
            mWordsHard = WordsDatabase.getInstance(app).wordsDao().fetchWordsContaining("Ъ");
            mWordsEh   = WordsDatabase.getInstance(app).wordsDao().fetchWordsContaining("Э");
        }
    
        public LiveData<List<Word>> getWords(int condition) {
            switch (condition) {
                case R.id.navi_drawer_letters_2:
                    return mWords2;
                case R.id.navi_drawer_letters_3:
                    return mWords3;
                case R.id.navi_drawer_letter_hard:
                    return mWordsHard;
                case R.id.navi_drawer_letter_eh:
                    return mWordsEh;
            }
    
            return mWords2;
        }
    }
    

    不过,我担心的是,一次获取所有4个列表是次优的,可能会导致UI延迟。

    因此,我尝试将视图模型拆分为一个基类和4个继承类-

    (现在充当基类):

    public class WordsViewModel extends AndroidViewModel {
    
        protected LiveData<List<Word>> mWords;
    
        public WordsViewModel (Application app) {
            super(app);
        }
    
        public LiveData<List<Word>> getWords() {
            return mWords;
        }
    }
    

    双视图模型

    public class TwoViewModel extends WordsViewModel {
        public TwoViewModel(Application app) {
            super(app);
            mWords = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(2);
        }
    }
    

    三视图模型 (继承类):

    public class ThreeViewModel extends WordsViewModel {
        public ThreeViewModel(Application app) {
            super(app);
            mWords = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(3);
        }
    }
    

    public class WordsFragment extends Fragment {
        private final ItemAdapter<WordItem> mItemAdapter = new ItemAdapter<>();
        private final FastAdapter<WordItem> mFastAdapter = FastAdapter.with(mItemAdapter);
    
        private WordsViewModel mViewModel;
    
        public static WordsFragment newInstance(int condition) {
            WordsFragment f = new WordsFragment();
    
            Bundle args = new Bundle();
            args.putInt(KEY_CONDITION, condition);
            f.setArguments(args);
    
            return f;
        }
    
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater,
                                 ViewGroup container,
                                 Bundle savedInstanceState) {
    
            int condition = (getArguments() == null ? -1 : getArguments().getInt(KEY_CONDITION));
            switch (condition) {
                case R.id.navi_drawer_letter_eh:
                    mViewModel = ViewModelProviders.of(this).get(EhViewModel.class);
                case R.id.navi_drawer_letter_hard:
                    mViewModel = ViewModelProviders.of(this).get(HardViewModel.class);
                case R.id.navi_drawer_letters_3:
                    mViewModel = ViewModelProviders.of(this).get(ThreeViewModel.class);
                default:
                    mViewModel = ViewModelProviders.of(this).get(TwoViewModel.class);
            }
    
            mViewModel.getWords().observe(this, words -> {
                mItemAdapter.clear();
                for (Word word: words) {
                    WordItem item = new WordItem();
                    item.word = word.word;
                    item.expl = word.expl;
                    mItemAdapter.add(item);
                }
            });
    

    不幸的是,这打破了我的应用程序总是显示两个字母的单词列表。

    我想知道,为什么会发生这种情况(因为继承?)以及如何解决这个问题?

    更新:

    下面是我的代码,用于使用 MaterialDrawer withTag() 我已经在调试器和日志(以及 Toast 可以在上面的截图中看到),即 condition 变量不同:

    private final Drawer.OnDrawerItemClickListener mFetchWordsListener = (view, position, drawerItem) -> {
        setTitle(drawerItem);
        WordsFragment f = WordsFragment.newInstance( (Integer)drawerItem.getTag() );
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.root, f)
                .commitAllowingStateLoss();
        return false;
    };
    
    mNavigationDrawer.addItems(
        ....
        new SectionDrawerItem().withName(R.string.item_dict),
        new PrimaryDrawerItem().withOnDrawerItemClickListener(mFindWordListener).withName(R.string.item_find_word).withIcon(R.drawable.magnify).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_find_word),
        new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_2).withIcon(R.drawable.letters_2).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_2).withTag(R.id.navi_drawer_letters_2),
        new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_3).withIcon(R.drawable.letters_3).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_3).withTag(R.id.navi_drawer_letters_3),
        new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_2).withIcon(R.drawable.letters_hard).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_hard).withTag(R.id.navi_drawer_letters_hard),
        new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_eh).withIcon(R.drawable.letters_eh).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_eh).withTag(R.id.navi_drawer_letters_eh)
    );
    

    更新2:

    (mViewModel instanceof TwoViewModel) 为什么总是这样?

    @Dao
    public interface WordsDao {
        @Query("SELECT * FROM table_words WHERE LENGTH(word) = :length")
        LiveData<List<Word>> fetchWordsLength(int length);
    
        @Query("SELECT * FROM table_words WHERE word LIKE '%' || :letter || '%'")
        LiveData<List<Word>> fetchWordsContaining(String letter);
    }
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Ryujin    6 年前

    您需要在每个case块的末尾加一个break,以便在找到大小写匹配的表达式时从开关中转义出来。如果没有break语句,在找到第一个匹配的case之后,控制流将“遍历”不同的case语句。在您的代码中,将始终执行默认情况,这将加载TwoViewModel。