Закусочная в библиотеке поддержки не включает OnDismissListener()?
Я хотел бы реализовать новую Snackbar, включенную в последнюю библиотеку поддержки дизайна, но способ, которым она предлагается, кажется противоречивым для моего, и я предполагаю, что многие другие используют.
Когда пользователь делает важное действие, я хочу разрешить им отменить его через Snackbar, но, похоже, нет способа обнаружить, когда он отклоняется, чтобы выполнить действие. Для меня имеет смысл сделать это следующим образом:
- Пользователь выполняет действие.
- Показать Snackbar и обновить интерфейс, как если бы действие было завершено (т.е. кажется, что данные отправляются в базу данных, но на самом деле еще нет).
- Если пользователь нажал "отменить" , верните изменения пользовательского интерфейса. Если нет, когда Snackbar будет уволен, он отправит данные.
Но поскольку я не вижу доступного OnDismissListener, мне пришлось бы:
- Пользователь выполняет действие.
- Немедленно отправьте информацию в базу данных и обновите интерфейс.
- Если пользователь нажимает "отменить" , отправьте другой вызов в базу данных, чтобы удалить только что добавленные данные и отменить изменения пользовательского интерфейса.
Мне бы очень хотелось, чтобы вам не пришлось делать два вызова в базе данных и просто отправлять их, когда приложение знает, что оно безопасно (пользователь избегал нажатия "отменить" ). Я замечаю, что некоторые из них реализованы в сторонней библиотеке через EventListener, но я действительно хотел бы придерживаться библиотеки Google.
Ответы
Ответ 1
Теперь он делает
Snackbar.make(getView(), "Hi there!", Snackbar.LENGTH_LONG).setCallback( new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
switch(event) {
case Snackbar.Callback.DISMISS_EVENT_ACTION:
Toast.makeText(getActivity(), "Clicked the action", Toast.LENGTH_LONG).show();
break;
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
Toast.makeText(getActivity(), "Time out", Toast.LENGTH_LONG).show();
break;
}
}
@Override
public void onShown(Snackbar snackbar) {
Toast.makeText(getActivity(), "This is my annoying step-brother", Toast.LENGTH_LONG).show();
}
}).setAction("Go away!", new View.OnClickListener() {
@Override
public void onClick(View v) {
}
}).show();
Ответ 2
Ознакомьтесь со своим вспомогательным классом для закусочных.
public class SnackbarUtils {
private static final String LOG_TAG = SnackbarUtils.class.getSimpleName();
private SnackbarUtils() {
}
public interface SnackbarDismissListener {
void onSnackbarDismissed();
}
public static void showSnackbar(View rootView, String message, String actionMessage, View.OnClickListener callbacks, final SnackbarDismissListener dismissListener){
Snackbar snackbar = Snackbar.make(rootView,message,Snackbar.LENGTH_LONG);
snackbar.setAction(actionMessage,callbacks);
if (dismissListener != null){
snackbar.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
}
@Override
public void onViewDetachedFromWindow(View v) {
dismissListener.onSnackbarDismissed();
}
});
}
snackbar.show();
}
}
Ответ 3
У меня такая же проблема, но я предоставляю "отмену" для удаления данных.
Вот как я с этим справляюсь:
- Притвориться, что данные удалены из db (скрыть от пользовательского интерфейса, который является списком элементов).
- Подождите, пока снек-бар "должен" исчезнуть.
- Отправить запрос на удаление в базу данных
- ЕСЛИ, пользователь использовал действие отмены, блокирует ожидающий вызов БД
Это может не сработать для вас, поскольку вы вставляете данные, и вы можете (?) нуждаться в том, чтобы он был доступен в базе данных после первого действия, но он будет работать, если "подделка" данных (в пользовательском интерфейсе) возможно. Мой код не оптимален, и я бы назвал его взломом, но его лучшее, что я смог найти, находясь в официальных библиотеках.
// Control objects
boolean canRemoveData = true;
Object removedData = getData(id);
UI.remove(id);
// Snackbar code
Snackbar snackbar = Snackbar.make(view, "Data removed", Snackbar.LENGTH_LONG);
snackbar.setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View v){
canRemoveData = false;
DB.remove(id);
}
});
// Handler to time the dismissal of the snackbar
new Handler(getActivity().getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
if(canRemoveData){
DB.remove(id);
}
}
}, (int)(snackbar.getDuration() * 1.05f));
// Here I am using a slightly longer delay before sending the db delete call,
// just because I don't trust the accuracy of the Handler timing and I want
// to be on the safe side. Either way this is dirty code, but the best I could do.
Мой фактический код более сложный (проблема с проблемой canRemoveData недоступна в подклассах, не будучи окончательной, но это в основном то, как мне удалось добиться того, о чем вы говорите.
Надеюсь, кто-то может найти лучшее решение.
Ответ 4
public class CustomCoordinatorLayout extends CoordinatorLayout {
private boolean mIsSnackBar = false;
private View mSnakBarView = null;
private OnSnackBarListener mOnSnackBarListener = null;
public CustomCoordinatorLayout(Context context) {
super(context);
}
public CustomCoordinatorLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(mIsSnackBar){
// Check whether the snackbar is existed.
// If it is not existed then index of the snackbar is -1.
if(indexOfChild(mSnakBarView) == -1){
mSnakBarView = null;
mIsSnackBar = false;
if(mOnSnackBarListener != null)
mOnSnackBarListener.onDismiss();
Log.d("NEOSARCHIZO","SnackBar is dismissed!");
}
}
}
@Override
public void onMeasureChild(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
super.onMeasureChild(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
// onMeaureChild is called before onMeasure.
// The view of SnackBar doesn't have an id.
if(child.getId() == -1){
mIsSnackBar = true;
// Store the view of SnackBar.
mSnakBarView = child;
if(mOnSnackBarListener != null)
mOnSnackBarListener.onShow();
Log.d("NEOSARCHIZO","SnackBar is showed!");
}
}
public void setOnSnackBarListener(OnSnackBarListener onSnackBarListener){
mOnSnackBarListener = onSnackBarListener;
}
public interface OnSnackBarListener{
public void onShow();
public void onDismiss();
}
}
Я использую custom Coordinatorlayout. Когда отображается Snackbar, вызывается onMeasure и onMeasureChild координатораLayout. Поэтому я переопределил эти методы.
Обратите внимание, что вы должны установить идентификаторы дочерних элементов компоновки пользовательского координатора. Потому что я нахожу представление SnackBar по id. Идентификатор SnackBar равен -1.
CustomCoordinatorLayout layout = (CustomCoordinatorLayout)findViewById(R.id.main_content);
layout.setOnSnackBarListener(this);
Snackbar.make(layout, "Hello!", Snackbar.LENGTH_LONG).setAction("UNDO", new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO something
}
}).show();
Внедрите OnSnackBarListener в свою активность или фрагмент. Когда появится закусочная, он будет вызывать onShow. И тот, кто уволен, тогда он будет называтьDismiss.
Ответ 5
Чтобы улучшить Hitch.united answer
boolean mAllowedToRemove = true;
Snackbar snack = Snackbar.make(mView, mSnackTitle, Snackbar.LENGTH_LONG);
snack.setAction(getString(R.string.snackbar_undo), new OnClickListener() {
@Override
public void onClick(View v) {
mAllowedToRemove = false;
// undo
...
}
});
snack.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
}
@Override
public void onViewDetachedFromWindow(View v) {
if(!mAllowedToRemove){
// handle actions like http requests
...
}
}
});
snack.show();
Ответ 6
Это было просто добавлено в v23.
Чтобы получать уведомления о том, что snackbar был показан или отклонен, вы можете предоставить Snackbar.Callback через setCallback(Callback)
.
Ответ 7
Ответ Франческо (здесь) прав, но, к сожалению, он работает только с API > 12. Я отправил запрос функции на Android Issue Tracker. Вы можете проверить это здесь и пометить его, если вы заинтересованы. Спасибо.