У меня есть приложение, которое отображает 4 списка слов во фрагменте (повторное использование одного и того же класса!):
Вот скриншот панели навигации (прошу прощения за неанглийский язык):
В настоящее время мой 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);
}
});
К сожалению, это мешает моему приложению всегда отображать список двухбуквенных слов.
Интересно, почему это происходит (из-за наследования?) И как это решить?
Обновлено:
Вот мой код для открытия фрагмента из основного действия с использованием Материал и 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 и, кстати, я заметил, что (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);
}
Вы передаете тег в качестве аргумента вместо идентификатора.
Спасибо, но нет - тег и идентификатор совпадают (например, R.id.navi_drawer_letters_3), и я убедился, что переменная condition верна в onCreateView.
Правильно, я этого не заметил, извините.
Канон вы показываете запрос fetchWordsLength?
Вам нужно поставить «разрыв» в конце каждого блока case, чтобы выйти из переключателя, когда будет найдено выражение соответствия регистру. Без оператора break поток управления будет «проваливаться» через различные операторы case после того, как будет найден первый подходящий вариант. В вашем коде всегда будет выполняться вариант по умолчанию, который загружает TwoViewModel.
Я не буду спать сегодня вечером :)
Со мной такое случается постоянно :) Вам следует остерегаться этого, если вы когда-нибудь перейдете на Kotlin и привыкнете к выражению «когда» :)
Спасибо! Я скопировал switch (...) { return ... } из верхнего фрагмента кода и забыл добавить break (я только что отредактировал свой комментарий, подумав, почему это случилось со мной)
можешь показать, как открываешь фрагменты?