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

时间:2018-10-27 09:41:19

标签: android mvvm android-architecture-components android-viewmodel android-livedata

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

  • 2个字母的单词
  • 3个字母的单词
  • 包含俄语 eh 字母的单词
  • 包含俄语硬符号字母的单词

这是导航抽屉的屏幕截图(请原谅非英语语言):

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个继承的类-

WordsViewModel (现在充当基类):

public class WordsViewModel extends AndroidViewModel {

    protected LiveData<List<Word>> mWords;

    public WordsViewModel (Application app) {
        super(app);
    }

    public LiveData<List<Word>> getWords() {
        return mWords;
    }
}

和继承类的区别仅在于DAO方法被称为-

TwoViewModel (继承类):

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

ThreeViewModel (继承类):

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

最后(感谢您阅读sofaler!)这是我的片段:

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);
            }
        });

不幸的是,这总是通过显示2个字母的单词列表来破坏我的应用。

我想知道,为什么会这样(由于继承?)以及如何解决呢?

更新:

这是我的代码,用于使用MaterialDrawerwithTag()从主要活动中打开片段,并且我已经在调试器和日志(以及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:

在这里,我的DAO界面和BTW我注意到(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 个答案:

答案 0 :(得分:2)

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