Как использовать 4 простых модели ViewModel с одним и тем же фрагментом? - PullRequest
0 голосов
/ 27 октября 2018

У меня есть приложение, которое отображает 4 списка слов во фрагменте (повторное использование одного и того же класса!):

  • двухбуквенные слова
  • 3-х буквенные слова
  • Слова, содержащие русский эх буква
  • Слова, содержащие русский жесткий знак буква

Вот снимок экрана навигационной панели (извините, не на английском языке):

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 списков одновременно неоптимальна и может вызвать задержку пользовательского интерфейса.

Итак, я попытался разделить модель представления на базовый класс, а затем 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);
    }
}

Наконец (и спасибо, что читаете Софар!) Вот мой фрагмент:

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

К сожалению, это ломает мое приложение, всегда отображая список из двух букв.

Интересно, почему это происходит (из-за наследования?) И как это решить?

UPDATE:

Здесь мой код для открытия фрагмента из основного действия с использованием 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:

Вот мой интерфейс 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 голосов
/ 27 октября 2018

Вам нужно поставить 'break' в конце каждого блока case, чтобы выйти из коммутатора, когда найдено выражение, соответствующее регистру.Без оператора break поток управления будет «проходить» через различные операторы case после того, как будет найден первый соответствующий регистр.В вашем коде всегда будет выполняться регистр по умолчанию, который загружает TwoViewModel.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...