Is there a way to prevent the user from dismissing a snackbar by swiping on it?
I have an app that shows a snack bar during network login, I want to avoid it to be dismissed.
According to Nikola Despotoski suggestion I've experimented both solutions:
private void startSnack(){
loadingSnack = Snackbar.make(findViewById(R.id.email_login_form), getString(R.string.logging_in), Snackbar.LENGTH_INDEFINITE)
.setAction("CANCEL", new OnClickListener() {
@Override
public void onClick(View view) {
getOps().cancelLogin();
enableControls();
}
});
loadingSnack.getView().setOnTouchListener(new View.OnTouchListener() {
public long mInitialTime;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (v instanceof Button) return false; //Action view was touched, proceed normally.
else {
switch (MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_DOWN: {
Log.i(TAG, "ACTION_DOWN");
mInitialTime = System.currentTimeMillis();
break;
}
case MotionEvent.ACTION_UP: {
Log.i(TAG, "ACTION_UP");
long clickDuration = System.currentTimeMillis() - mInitialTime;
if (clickDuration <= ViewConfiguration.getTapTimeout()) {
return false;// click event, proceed normally
}
}
case MotionEvent.ACTION_MOVE: {
Log.i(TAG, "ACTION_MOVE");
return true;
}
}
return true;
}
}
});
ViewGroup.LayoutParams lp = loadingSnack.getView().getLayoutParams();
if (lp != null && lp instanceof CoordinatorLayout.LayoutParams) {
((CoordinatorLayout.LayoutParams)lp).setBehavior(new DummyBehavior());
loadingSnack.getView().setLayoutParams(lp);
Log.i(TAG, "Dummy behavior assigned to " + lp.toString());
}
loadingSnack.show();
}
this is DummyBehavior class:
public class DummyBehavior extends CoordinatorLayout.Behavior<View>{
/**
* Debugging tag used by the Android logger.
*/
protected final static String TAG =
DummyBehavior.class.getSimpleName();
public DummyBehavior() {
Log.i(TAG, "Dummy behavior created");
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
Log.i(TAG, "Method " + stackTrace[2].getMethodName() );
}
public DummyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
Log.i(TAG, "Dummy behavior created");
}
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean blocksInteractionBelow(CoordinatorLayout parent, View child) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public void onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
}
@Override
public boolean isDirty(CoordinatorLayout parent, View child) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return false;
}
@Override
public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout, View child, WindowInsetsCompat insets) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return null;
}
@Override
public void onRestoreInstanceState(CoordinatorLayout parent, View child, Parcelable state) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
}
@Override
public Parcelable onSaveInstanceState(CoordinatorLayout parent, View child) {
Log.i(TAG, "Method " + Thread.currentThread().getStackTrace()[2].getMethodName() );
return null;
}
}
But my snackbar still disappears when swiped and this is a typical log:
12-02 22:26:43.864 19598-19598/ I/DummyBehavior: Dummy behavior created
12-02 22:26:43.866 19598-19598/ I/DummyBehavior: Method <init>
12-02 22:26:43.866 19598-19598/ I/LifecycleLoggingActivity: Dummy behavior assigned to android.support.design.widget.CoordinatorLayout$LayoutParams@808c0e9
12-02 22:26:44.755 19598-19598/ I/LifecycleLoggingActivity: ACTION_DOWN
12-02 22:26:44.798 19598-19598/ I/LifecycleLoggingActivity: ACTION_MOVE
12-02 22:26:44.815 19598-19598/ I/LifecycleLoggingActivity: ACTION_MOVE
12-02 22:26:44.832 19598-19598/ I/LifecycleLoggingActivity: ACTION_MOVE
12-02 22:26:44.849 19598-19598/ I/LifecycleLoggingActivity: ACTION_MOVE
12-02 22:26:44.866 19598-19598/ I/LifecycleLoggingActivity: ACTION_MOVE
12-02 22:26:44.883 19598-19598/ I/LifecycleLoggingActivity: ACTION_MOVE
Here is a solution that does not require you to mess with
ViewTreeObserver
. Note that the following solution is written in Kotlin based on SDK 26.BaseTransientBottomBar
If you look in to the source code of
BaseTransientBottomBar
in methodshowView
, it add the behavior if the layoutParams isCoordinatorLayout.LayoutParams
. We can undo this by setting the behavior back tonull
.As it add the behavior just before the view is shown, we should undo it after the view is shown.
This worked for me :
Good luck! :)
You can disable streaming touch events rather than clicks to the
Snackbar
view.Or you could just replace the
Snackbar
behavior with some emptyCoordinatorLayout.Behavior
:This is the empty behavior, that does nothing. Default
SwipeToDismissBehavior
usesViewDragHelper
to process touch events, upon which triggers the dismissal.Snackbar now has actual support for this by using the setBehavior method. The great thing here is that before you would always lose some behaviors which are now preserved.
Note that the package moved so you have to import the "new" Snackbar in the snackbar package.
Better solution here.... Don't provide
CoordinatorLayout
or any of its child as view in snackbar.Where, the
ROOT_LAYOUT
should be any layout except coordinatorlayout or its child.This worked for me:
Where DisableSwipeBehavior is: