支持库中的小吃包不包含OnDismissListener()?

我想实现最新的devise支持库中包含的新Snackbar,但是它的提供方式似乎与我的反直觉,我假设许多人使用。

当用户做了一个重要的动作,我想让他们通过Snackbar撤消它,但似乎没有办法检测什么时候被解雇做这个动作。 这是有道理的,我这样做的方式如下:

  1. 用户执行操作。
  2. 显示小吃店和更新用户界面就好像行动已经完成(即它似乎是数据发送到数据库,但实际上还没有)。
  3. 如果用户按下“撤消”,则还原UI更改。 如果没有,当Snackbar被解雇时,它将发送数据。

但是因为我没有看到任何可访问的OnDismissListener,所以我必须:

  1. 用户执行操作。
  2. 立即发送信息到数据库并更新UI。
  3. 如果用户按“撤消”,则发送另一个调用数据库以删除刚添加的数据并恢复UI更改。

我真的希望避免必须对数据库进行两次调用,并在应用程序知道它是安全的时候发送一个(用户避免按“撤消”)。 我注意到通过EventListener在第三方库中有一些实现,但是我真的想要坚持到Google库。

Solutions Collecting From Web of "支持库中的小吃包不包含OnDismissListener()?"

现在呢

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(); 

看看我的帮手类小吃店。

 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(); } } 

我有同样的问题,但我提供了一个“撤消”删除数据。
这是我如何处理它:

  • 假装数据从数据库中被删除(从UI隐藏,这是一个项目列表)
  • 等到小吃店“应该”消失
  • 将删除呼叫发送到数据库
  • 如果用户使用了撤销操作,则阻止待处理的数据库调用

这可能对您不适用,因为您正在插入数据,并且您可能(?)需要在第一个操作后才能在数据库中使用该数据,但是如果在数据库中“伪造”数据是可行的,则它将起作用。 我的代码不是最优的,我会把它称为黑客,但它是我在官方图书馆中find的最好的。

  // 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问题不能在子类中访问,而不是最终的,但这基本上是我如何设法实现你在说什么。
希望有人能找出更好的解决scheme。

 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(); } } 

我使用自定义协调器布局。 当显示Snackbar时,则调用onMeasure和onMeasureChild的CoordinatorLayout。 所以我忽略了这些方法。

请注意,您必须设置自定义协调员布局的子项的ID。 因为我通过idfind了SnackBar的视图。 SnackBar的ID是-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。 当小吃店显示时,它会打电话给显示。 而那个被驳回的话就会要求驳回。

提高Hitch.united答案

  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(); 

这只是添加到v23

要在小吃店被显示或解散时得到通知,您可以通过setCallback(Callback)提供Snackbar.Callback。

弗朗切斯科的答案( 在这里 )是正确的,但不幸的是,它只适用于API> 12。我向Android Issue Tracker提交了一个function请求。 你可以在这里查看 ,如果你有兴趣,可以看看。 谢谢。