0

Есть БД с двумя таблицами ruseng и engrus, которые содержат русско-английский и англо-русский словари соответственно. В MainAcivity есть кнопка, которая переключает перевод с русского на английский и наоборот как в гугл переводчике. В список данные выводятся с помощью Room. Для каждой таблицы написала свой адаптер и пытаюсь при переключении языков выводить данные в список. Так вот при запуске приложения и при переключении языков список не обновляется. Подскажите почему так?

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener, RusEngWordAdapter.OnItemClickListener, EngRusWordAdapter.OnItemClickListener {

    RecyclerView rvList;
    private TextView txtLeftLang;
    private TextView txtRightLang;
    private ImageButton btnReverseLang;
    private boolean isRUSLeft;//Для переключения языков

    private RusEngWordAdapter rusEngWordAdapter;
    private EngRusWordAdapter engRusWordAdapter;

    RusEngDao rusEngDao;
    EngRusDao engRusDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();
        navigationView.setNavigationItemSelectedListener(this);

        txtLeftLang = findViewById(R.id.txtLeftLang);
        txtRightLang = findViewById(R.id.txtRightLang);
        btnReverseLang = findViewById(R.id.btnReverseLang);

        rvList = findViewById(R.id.rvList);
        rvList.setLayoutManager(new LinearLayoutManager(this));

        txtLeftLang.setText(getResources().getString(R.string.rus_lang));
        txtRightLang.setText(getResources().getString(R.string.eng_lang));

        rusEngDao = (RusEngDao) AppDatabase.createPersistentDatabase(getApplicationContext()).rusEngDao();
        engRusDao = (EngRusDao) AppDatabase.createPersistentDatabase(getApplicationContext()).engRusDao();
    }

    @Override
    protected void onResume() {
        super.onResume();

        btnReverseLang.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                txtLeftLang.setText(isRUSLeft? getResources().getString(R.string.rus_lang):getResources().getString(R.string.eng_lang));
                txtRightLang.setText(isRUSLeft? getResources().getString(R.string.eng_lang):getResources().getString(R.string.rus_lang));
                isRUSLeft = !isRUSLeft;
                System.out.println("Перевод на русский " + isRUSLeft);
            }
        });

        if (isRUSLeft) {
            engRusDao.getAllEngRusWords().observe(this, (List<EngRus> list) -> {
                engRusWordAdapter = new EngRusWordAdapter(MainActivity.this, list);
                engRusWordAdapter.setOnItemClickListener(this);
                rvList.setAdapter(engRusWordAdapter);
            });
        } else {
            rusEngDao.getAllRusEngWords().observe(this, (List<RusEng> list) -> {
                rusEngWordAdapter = new RusEngWordAdapter(MainActivity.this, list);
                rusEngWordAdapter.setOnItemClickListener(this);
                rvList.setAdapter(rusEngWordAdapter);
            });
        }
    }

    @Override
    public void onItemClick(int id) {

    }

}

Модель RusEng

@Entity(tableName = "ruseng") //Описывает таблицу в БД
public class RusEng {

    @PrimaryKey(autoGenerate = true) //Система сама будет инкрементировать это поле в БД
    @ColumnInfo(typeAffinity = INTEGER)
    private int id;
    @ColumnInfo(typeAffinity = TEXT)
    private String word;
    @ColumnInfo(typeAffinity = TEXT)
    private String translation;
    @ColumnInfo(typeAffinity = INTEGER)
    private int favorites;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public String getTranslation() {
        return translation;
    }

    public void setTranslation(String translation) {
        this.translation = translation;
    }

    public int getFavorites() {
        return favorites;
    }

    public void setFavorites(int favorites) {
        this.favorites = favorites;
    }

    public RusEng(int id, String word, String translation, int favorites) {
        this.id = id;
        this.word = word;
        this.translation = translation;
        this.favorites = favorites;
    }
}

Модель EngRus

@Entity(tableName = "engrus") //Описывает таблицу в БД
public class EngRus {
    @PrimaryKey(autoGenerate = true) //Система сама будет инкрементировать это поле в БД
    @ColumnInfo(typeAffinity = INTEGER)
    private int id;
    @ColumnInfo(typeAffinity = TEXT)
    private String word;
    @ColumnInfo(typeAffinity = TEXT)
    private String translation;
    @ColumnInfo(typeAffinity = INTEGER)
    private int favorites;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }

    public String getTranslation() {
        return translation;
    }

    public void setTranslation(String translation) {
        this.translation = translation;
    }

    public int getFavorites() {
        return favorites;
    }

    public void setFavorites(int favorites) {
        this.favorites = favorites;
    }

    public EngRus(int id, String word, String translation, int favorites) {
        this.id = id;
        this.word = word;
        this.translation = translation;
        this.favorites = favorites;
    }
}

Адаптер

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemHolder> {

    private final int RUSENG = 1;
    private final int ENGRUS = 2;
    boolean isSecondItem = false;
    ArrayList<Item> items;

    public ItemAdapter (ArrayList<Item> items){
        this.items = items;
    }

    @Override
    public ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;

        switch (viewType) {
            case RUSENG:
                // разметка для первого типа
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
                break;
            case ENGRUS:
                // разметка для второго типа
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
                break;
            default:
                // разметка заголовка
                v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        }
        return new ItemHolder(v);
    }

    @Override
    public void onBindViewHolder(ItemHolder holder, final int position) {
        int type = getItemViewType(position);

        switch (type) {
            case RUSENG:
                // приводим интерфейс к первому типу
                RusEng rusEng = (RusEng)items.get(position);
                // биндим данные первого типа
                holder.txtWord.setText(rusEng.getWord());
                holder.txtTranslate.setText(rusEng.getTranslation());
                break;
            case ENGRUS:
                // приводим интерфейс ко второму типу
                EngRus engRus = (EngRus)items.get(position);
                // биндим данные второго типа
                holder.txtWord.setText(engRus.getWord());
                holder.txtTranslate.setText(engRus.getTranslation());
                isSecondItem = true;
                break;
        }
    }

    @Override
    public int getItemViewType(int position) {
        // определяем какой тип в текущей позиции
        int type = items.get(position).getItemType();
        if (type == 1) return RUSENG;
        else return ENGRUS;

    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    class ItemHolder extends RecyclerView.ViewHolder {
        public final TextView txtSimbol;
        public final TextView txtWord;
        public final TextView txtTranslate;
        public final ImageButton btnfavorite;

        ItemHolder(View itemView) {
            super(itemView);
            txtSimbol = (TextView) itemView.findViewById(R.id.txtSimbol);
            txtWord = (TextView) itemView.findViewById(R.id.txtWord);
            txtTranslate = (TextView) itemView.findViewById(R.id.txtTranslate);
            btnfavorite = (ImageButton) itemView.findViewById(R.id.btnfavorite);
        }
    }
}

2 Answers2

0

Нужно продублировать пересоздание адаптера и в onClick, для удобства лучше вынести в отдельный метод:

protected void onResume() {
    super.onResume();

    btnReverseLang.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            txtLeftLang.setText(isRUSLeft? getResources().getString(R.string.rus_lang):getResources().getString(R.string.eng_lang));
            txtRightLang.setText(isRUSLeft? getResources().getString(R.string.eng_lang):getResources().getString(R.string.rus_lang));
            isRUSLeft = !isRUSLeft;
            System.out.println("Перевод на русский " + isRUSLeft);
            createAdapter();
        }
    });
    createAdapter();
}

private void createAdapter() {
    if (isRUSLeft) {
        engRusDao.getAllEngRusWords().observe(this, (List<EngRus> list) -> {
            engRusWordAdapter = new EngRusWordAdapter(MainActivity.this, list);
            engRusWordAdapter.setOnItemClickListener(this);
            rvList.setAdapter(engRusWordAdapter);
        });
    } else {
        rusEngDao.getAllRusEngWords().observe(this, (List<RusEng> list) -> {
            rusEngWordAdapter = new RusEngWordAdapter(MainActivity.this, list);
            rusEngWordAdapter.setOnItemClickListener(this);
            rvList.setAdapter(rusEngWordAdapter);
        });
    }
}

P.S. Если при переключении не меняется структура вью-элементов, то лучше не пересоздавать адаптер, а менять данные в существующем - это быстрее и экономнее.

woesss
  • 12,168
  • 1
  • 17
  • 32
  • структура вью не меняется.. А как менять данные в существующем? У меня для каждой таблицы свой адаптер создан. И можно ли в моем случае обойтись одним адаптером для двух таблиц? – Наталья Сергеевна May 29 '19 at 10:46
  • Не получится. У вас уведомление о смене данных происходит через setAdapter (т.е при смене адаптера целиком), а если используется адаптер без пересоздания, то смена данных должна идти через уведомление notifyDataSetChanged. Можно обойтись и одним адаптером если сможете их обобщить в один. – Daniel Protopopov May 29 '19 at 10:58
  • Не получится то о чем говорил woesss или использовать один адаптер для двух таблиц? – Наталья Сергеевна May 29 '19 at 11:01
  • Для этого Вам нужно в адаптере сделать метод, который обновит или заменит его внутреннюю коллекцию и вызовет notifyDataSetChanged(). Из вне Вы передадите новые данные в этот метод и адаптер обновится. Для примера, можете глянуть вопрос на en-SO: How to update RecyclerView Adapter Data? – woesss May 29 '19 at 11:12
  • Да, это стоит делать если логику Ваших адаптеров легко объединить. Если она сильно отличается - то лучше оставить как есть. – woesss May 29 '19 at 11:20
  • Если я правильно вас поняла, то мне нужно в одном из адаптеров (Например RusEngWordAdapter) написать метод update(), а нем очищать коллекцию и вызывать notifyDataSetChanged(). А в MainActivity использовать один адаптер вместо двух и передавать в него нужную коллекцию. Я свой вопрос обновила и попробовала сделать так, но адаптер требует чтобы в коллекцию я передавала объекты типа EngRus, а не RusEng. Или может я не так поняла. – Наталья Сергеевна May 29 '19 at 11:30
  • Оба адаптера абсолютно одинаковы, только таблицы разные и передаются разные типы объектов в коллекцию в адаптере. Остальное все одно и тоже. В вопрос добавила оба адаптера и оба класса, описывающие таблицы – Наталья Сергеевна May 29 '19 at 11:39
  • @НатальяСергеевна , смотрите ответ, как в одном адаптере объеденить модели разных типов. если разметка айтемов в обоих случаях одинакова, то код сильно упростится – pavlofff May 29 '19 at 11:43
  • Адаптер вроде бы сделала (добавила в конец вопроса). Теперь мне нужно при переключении языков выводить данные из разных таблиц. Если isRUSLeft == true, то выводить данные из таблицы engrus, а если false то из таблицы ruseng. А это как сделать непонятно мне. – Наталья Сергеевна May 29 '19 at 12:03
0

У вас, насколько я вижу, используется Reactive Extensions (RxJava). Можно так:

private Subject<Integer> OnLanguageChanged = PublishSubject.create();


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // ...
    OnLanguageChanged.subscribeWith(new DisposableObserver<Integer>() {

       @Override
       public void onNext(Integer NewLanguage) {
          // Доставайте данные из нужного DAO и уведомляйте адаптер
       }

       @Override
       public void onError(Throwable t) {
       }

       @Override
       public void onComplete() {
       }
    });

    btnReverseLang.setOnClickListener(() -> {
        // Выудить новый язык и передать его подписчику(ам) наблюдателя
        OnLanguageChanged.onNext(NewLanguage);
    });
}
  • Подскажите пожалуйста как передать новый язык подписчику? У меня реализовано сейчас так как выше писал woesss если isRUSLeft = true достаю из engRusDao иначе из rusEngDao. В метод onNext я так понимаю можно вставить метод createAdapter(), который выбирает данные из нужного Dao и выводит в список – Наталья Сергеевна May 29 '19 at 18:02
  • @НатальяСергеевна вам не нужно создавать новый адаптер, а только передать в уже существующий новые данные и обновить его, как ,например, здесь – pavlofff May 30 '19 at 00:38