Страницы

Поиск по вопросам

вторник, 16 октября 2018 г.

PopupMenu выбрасывает WindowLeaked при повороте устройства

Реализую overflow-кнопку для элемента списка, кнопка обрабатывается в холдере адаптера:
class CustomAdapter extends RecyclerView.Adapter {
....
public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
ImageButton mMenuItem; CardView mCard;
public ItemHolder(View v) { super(v);
mCard = (CardView) v.findViewById(R.id.card); mMenuItem = (ImageButton) v.findViewById(R.id.menu_button);
mMenuItem.setOnClickListener(this); mCard.setOnClickListener(this); }
@Override public void onClick(View v) {
int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { switch (v.getId()) { case R.id.menu_button: PopupMenu popupMenu = new PopupMenu(mMenuItem.getContext(), mMenuItem); popupMenu.inflate(R.menu.popup_menu); popupMenu.show(); break; case R.id.card: // break; } } } }
Если меню раскрыто, то при повороте устройства получаю исключение:
E WindowManager: android.view.WindowLeaked: Activity com.example.android.test.MyActivity
Понятно, что менюшка теряет прошлую активити при пересоздании и, в общем то, это не приводит к падению приложения и вообще никак не проявляется, если не смотреть logcat, но хотелось бы решить проблему. Собственно решение, как таковое, известно - закрывать PopupMenu в методах закрытия активити, например onPause() - popupmenu.dissmis(). Проблема в том, что это меню создается в адаптере и ему о жизненном цикле активити ничего не известно. Можно, конечно, уведомить адаптер, что вызван метод onPause() и закрыть эту менюшку, но получается все довольно коряво - нужно переносить инициализацию меню в сам адаптер и тп. нежелательные решения.
Посмотрел большое количество примеров реализации, в том числе эталонный iosched, везде эта проблема просто игнорируется, даже гуглом.
Вопрос такой: как решить и стоит ли вообще обращать внимание на эту проблему или какие то альтернативные способы реализации меню в айтеме для RecyclerView


Ответ

Поскольку вложенный в адаптер класс ItemHolder статический, то для решения проблемы сделал следующее:
Объявил PopupMenu статическим полем класса - теперь все экземпляры меню будут "связаны", как будто это один экземпляр:
public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
ImageButton mMenuItem; CardView mCard; public static PopupMenu popupMenu; ...
@Override public void onClick(View v) { ... if (position != RecyclerView.NO_POSITION) { switch (v.getId()) { case R.id.menu_button: popupMenu = new PopupMenu(mMenuItem.getContext(), mMenuItem); ... } } } В onPause() активити, в которую выводится список, обращаюсь напрямую к этому статическому экземпляру через класс адаптера с предложением закрыться:
@Override protected void onPause() { super.onPause();
PopupMenu popupMenu = CustomAdapter.ItemHolder.popupMenu; if (popupMenu != null) popupMenu.dismiss(); popupMenu = null; // защита от утечек памяти }
При желании можно написать геттер, чтобы отдавал PopupMenu. В данном случае я не вижу в этом особой надобности.
PS: По прежнему хотел бы услышать более оптимальное решение.

Комментариев нет:

Отправить комментарий