Как я могу получить уведомление о том, что снэк-бар закрылся?

Я использую снэк-бар из com.android.support:design:22.2.0 библиотека. Я использую это, чтобы отменить удаление. Чтобы упростить мою жизнь, я собираюсь сделать так, чтобы пользовательский интерфейс выглядел так, как будто вещи действительно удалены из источника данных, и, если кнопка отмены в снэк-баре не нажата, фактически выполните удаления из источника данных. Итак, я хочу знать, когда Снэк-бар больше не виден, поэтому можно безопасно удалять элементы.

Я могу вызвать getView() на панели Snackbar, но я не уверен, какой слушатель мне следует использовать. Я старался setOnSystemUiVisibilityChangeListener() но это не сработало, я думаю, что это только для системной строки состояния.

Кроме того, Snackbar не может быть расширен, так как имеет собственный конструктор.

13 ответов

Решение

Библиотека дизайна Google поддерживает обратные вызовы Snackbar в версии 23. См. Документы Snackbar и документы Callback. Затем вы получите уведомление, когда снэк-бар удаляется (а также когда отображается), а также тип увольнения, если это полезно для вас:

snackbar.addCallback(new Snackbar.Callback() {

    @Override
    public void onDismissed(Snackbar snackbar, int event) {
      //see Snackbar.Callback docs for event details
      ...  
    }

    @Override
    public void onShown(Snackbar snackbar) {
       ...
    }
  });
    snackbar.setCallback(new Snackbar.Callback() {

        @Override
        public void onDismissed(Snackbar snackbar, int event) {
            if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT) {
                // Snackbar closed on its own
            }
        }

        @Override
        public void onShown(Snackbar snackbar) {
            ...
        }
    });

Snackbar.addCallback в котлине

val snackBar = Snackbar
                .make(view, "Text Snackbar", Snackbar.LENGTH_LONG)
                .addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
                    override fun onShown(transientBottomBar: Snackbar?) {
                        super.onShown(transientBottomBar)
                    }

                    override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
                        super.onDismissed(transientBottomBar, event)
                    }
                })

        val snackBarView = snackBar.view
        snackBarView.setBackgroundColor(Color.RED)
        snackBar.show()

Недавно я сам наткнулся на этот вопрос, когда для прокрутки и показа Снекбека было показано слишком много, прежде чем первый даже исчез. Я должен был найти способ узнать, должно ли приложение установить Snackbar.

Я лично нашел это решение.

Это правда, что Snackbar сам по себе не предлагает какого-либо прослушивателя для его состояния / видимости, но вы все равно можете получить View Object из Snackbar ( getView();). Из объекта просмотра у вас есть возможность использовать самые разные методы для добавления слушателей.

Чтобы реализовать это, вы должны выйти из общего использования Toast/Snackbar "все в одной строке", потому что добавление слушателей возвращает void.

Я лично нашел OnAttachStateChangeListener для удовлетворения моих потребностей.

Бросьте сниппер с моим кодом на случай, если он окажется полезным для вас.

Snackbar snack = Snackbar.make(getView(), "My Placeholder Text", Snackbar.LENGTH_LONG);

snack.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
    @Override
        public void onViewAttachedToWindow(View v) {
            canDisplaySnackbar = false;
        }

    @Override
    public void onViewDetachedFromWindow(View v) {

        Handler h = new Handler();
        h.postDelayed(new Runnable() {
            @Override
            public void run() {
                canDisplaySnackbar = true;

                }
        }, 1000);
    }
});
snack.show();

Обратите внимание, что это только моя реализация для моей собственной проблемы. Обработчик с PostDelayed Runnable может даже не соответствовать вашему случаю. Это было только для того, чтобы дать общее представление о реализации, которую я предложил использовать фрагмент, который у меня уже есть.

Чтобы получать уведомления, когда снэк-бар был показан или отклонен, вы можете предоставить Snackbar.Callback через setCallback(Callback).

В Котлине это сработало для меня:

      Snackbar.make(
    binding.root,
    getString(R.string.ticket_posted),
    LENGTH_LONG)
    .setAction(getString(R.string.undo)) {
        Log.d(TAG, "Try to undo ticket $key")
    }
    .addCallback(object : BaseCallback<Snackbar>() {
        override fun onDismissed(
            transientBottomBar: Snackbar?,
            event: Int
        ) {
            super.onDismissed(transientBottomBar, event)
            if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT) {
                Log.d(TAG, "Try to reset")
            }
        }
    })
    .show()
}

onDismissed также вызывается, когда нажимается текст действия, по этой причине необходимо поставить одно условие, как

event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT

А теперь новый код выглядит так, как показано ниже.

final Snackbar snackBar = Snackbar.make(findViewById(R.id.root_layout), result, Snackbar.LENGTH_LONG);

snackbar.addCallback(new Snackbar.Callback() {

@Override
public void onDismissed(Snackbar snackbar, int event) {
 if (event == Snackbar.Callback.DISMISS_EVENT_TIMEOUT) {
        // Snackbar closed on its own
    }  
}

@Override
public void onShown(Snackbar snackbar) {
...
}
});
snackBar.show();

setCallback сейчас устарела и addCallback должен быть использован

https://developer.android.com/reference/android/support/design/widget/BaseTransientBottomBar.html)

В настоящее время нет способа получить уведомление, когда снэк-бар завершил отображение.

В этой теме обсуждается обходной путь, основанный на таймере на время отображения Snackbar. Snackbar в библиотеке поддержки не включает OnDismissListener()?

Одна проблема, которую следует рассмотреть с этим обходным путем, - это возможно, что продолжительность Snackbar перезапускается. Спецификация дизайна материала для Snackbar говорит, что это произойдет, если отобразится не связанный диалог или всплывающее окно.

Благодаря @SergeyMilakov в Котлине это:

@SuppressLint("WrongConstant") // Suppress an error when set duration.
private fun showSnackbar() {
    val DURATION = 5000

    Snackbar.make(view!!, "Remove item?"), DURATION).apply {
        setAction("Undo") {
            // Your action when a button is clicked.
        }
        addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
            /* override fun onShown(transientBottomBar: Snackbar?) {
                super.onShown(transientBottomBar)
                Timber.d("*** onShown")
            }*/

            override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
                super.onDismissed(transientBottomBar, event)
                if (event != Snackbar.Callback.DISMISS_EVENT_ACTION) {
                    // Your action when the Snackbar is closed and the button was not clicked.
                }

            }
        })
        view.setBackgroundColor(ContextCompat.getColor(context, R.color.black))
        setActionTextColor(ContextCompat.getColor(context, R.color.yellow))
        show()
    }
}

Обратите внимание, что Snackbarотображается, даже если вы перейдете на другие экраны (например, назад). Так что не забывайте проверять, выполняете ли вы действия на правом экране.

Также Snackbar не восстанавливается после поворота экрана.

У меня точно такая же проблема, как и у вас. Мое решение...

              final Snackbar povrati_obrisani_unos = Snackbar.make (coordinatorLayout, "Ponisti brisanje", Snackbar.LENGTH_INDEFINITE)
                    .addCallback (new Snackbar.Callback (){
                        @Override
                        public void onDismissed(Snackbar transientBottomBar, int event) {
                            super.onDismissed (transientBottomBar, event);
                            if(event==DISMISS_EVENT_SWIPE){//here we detect if snackbar is swipped away and not cliked (which is undo in my case)
                                Uri uriDelete = Uri.parse (obrisano.getImageuri ());
                                ContentResolver contentResolver = getContentResolver();
                                contentResolver.delete (uriDelete, null, null);
                                Toast.makeText (MainActivity.this,
                                        "Ocitavanje i slika su trajno obrisani", Toast.LENGTH_SHORT).show ();
                            }

Вот и все, не нужно копировать и вставлять snackbar.action, который отменяется. Хочу пояснить, насколько я могу здесь.

С уважением Ненад

В настоящее время вы не можете этого достичь.

Нет слушателя, вызванного, когда закусочная закрыта.

Самый простой способ сделать это - временно сохранить запись в другом месте (даже локальную переменную), а затем снова вставить ее, если они случайно нажали кнопку отмены.

    snackbar.addCallback(new Snackbar.Callback() {
        public void onShown(Snackbar snackbar) {
           //  on show  
        }
 public void onDismissed(Snackbar snackbar, int event) {
          //  on dismiss  
        }
      });
Другие вопросы по тегам