支持库中的 Snackbar 不包含 OnDismissListener()?
Snackbar in Support Library doesn't include OnDismissListener()?
我想实现最新设计支持库中包含的新 Snackbar,但它的提供方式似乎对我和我假设许多其他人的使用有悖常理。
当用户执行重要操作时,我想让他们通过 Snackbar 撤消它,但似乎无法检测何时取消执行操作。按照以下方式进行对我来说很有意义:
- 用户执行操作。
- 显示 Snackbar 并更新 UI 就像操作已经完成一样(即看起来数据已发送到数据库,但实际上还没有)。
- 如果用户按下 "undo," 还原 UI 更改。如果没有,当 Snackbar 被关闭时,它会发送数据。
但是因为我没有看到任何可访问的 OnDismissListener,因此我不得不:
- 用户执行操作。
- 立即将信息发送到数据库并更新UI。
- 如果用户按下 "undo," 向数据库发送另一个调用以删除刚添加的数据并还原 UI 更改。
我真的很想避免必须对数据库进行两次调用,而只在应用程序知道它是安全的时候发送一次(用户已避免按 "undo")。我注意到通过 EventListener 在第三方库中有一些实现,但我真的很想坚持使用 Google 库。
我有同样的问题,但我提供了一个 'undo' 用于删除数据。
我是这样处理的:
- 假装数据已从数据库中删除(从 UI 中隐藏,这是一个项目列表)
- 等小吃店'should'消失
- 向数据库发送删除调用
- 如果,用户使用了撤消操作,阻止挂起的数据库调用
这可能对您不起作用,因为您正在插入数据并且您可能(?)需要它在第一次操作后在数据库中可用,但如果 'faking' 数据(在UI) 是可行的。我的代码不是最优的,我会称之为 hack,但它是我在官方库中找到的最好的代码。
// 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 无法在 sub 类 内部访问的问题,但不是最终的,但这基本上就是我设法实现你所谈论的内容的方式。
希望有人能想出更好的解决方案。
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 时,将调用 CoordinatorLayout 的 onMeasure 和 onMeasureChild。所以我重写了这些方法。
请注意,您必须设置自定义协调器布局的子项的 ID。因为我是通过id找到SnackBar的view的。 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();
在您的 activity 或片段中实施 OnSnackBarListener。当显示 snackbar 时,它会调用 onShow。一个被解雇然后它会调用onDismiss。
找我的帮手 class 找小吃吧。
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();
}
}
改进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();
Francesco 的回答() is right, but unfortunately it only works on API > 12. I submitted a feature request to the Android Issue Tracker. You can check it here 如果您感兴趣,请加注星标。谢谢。
现在does
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();
这刚刚添加到 v23。
要在显示或关闭快餐栏时收到通知,您可以通过 setCallback(Callback)
提供 Snackbar.Callback。
我想实现最新设计支持库中包含的新 Snackbar,但它的提供方式似乎对我和我假设许多其他人的使用有悖常理。
当用户执行重要操作时,我想让他们通过 Snackbar 撤消它,但似乎无法检测何时取消执行操作。按照以下方式进行对我来说很有意义:
- 用户执行操作。
- 显示 Snackbar 并更新 UI 就像操作已经完成一样(即看起来数据已发送到数据库,但实际上还没有)。
- 如果用户按下 "undo," 还原 UI 更改。如果没有,当 Snackbar 被关闭时,它会发送数据。
但是因为我没有看到任何可访问的 OnDismissListener,因此我不得不:
- 用户执行操作。
- 立即将信息发送到数据库并更新UI。
- 如果用户按下 "undo," 向数据库发送另一个调用以删除刚添加的数据并还原 UI 更改。
我真的很想避免必须对数据库进行两次调用,而只在应用程序知道它是安全的时候发送一次(用户已避免按 "undo")。我注意到通过 EventListener 在第三方库中有一些实现,但我真的很想坚持使用 Google 库。
我有同样的问题,但我提供了一个 'undo' 用于删除数据。
我是这样处理的:
- 假装数据已从数据库中删除(从 UI 中隐藏,这是一个项目列表)
- 等小吃店'should'消失
- 向数据库发送删除调用
- 如果,用户使用了撤消操作,阻止挂起的数据库调用
这可能对您不起作用,因为您正在插入数据并且您可能(?)需要它在第一次操作后在数据库中可用,但如果 'faking' 数据(在UI) 是可行的。我的代码不是最优的,我会称之为 hack,但它是我在官方库中找到的最好的代码。
// 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 无法在 sub 类 内部访问的问题,但不是最终的,但这基本上就是我设法实现你所谈论的内容的方式。
希望有人能想出更好的解决方案。
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 时,将调用 CoordinatorLayout 的 onMeasure 和 onMeasureChild。所以我重写了这些方法。
请注意,您必须设置自定义协调器布局的子项的 ID。因为我是通过id找到SnackBar的view的。 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();
在您的 activity 或片段中实施 OnSnackBarListener。当显示 snackbar 时,它会调用 onShow。一个被解雇然后它会调用onDismiss。
找我的帮手 class 找小吃吧。
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();
}
}
改进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();
Francesco 的回答(
现在does
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();
这刚刚添加到 v23。
要在显示或关闭快餐栏时收到通知,您可以通过 setCallback(Callback)
提供 Snackbar.Callback。