2015-12-01 12 views
9

Gibt es eine Möglichkeit, den Benutzer daran zu hindern, eine Snackbar durch Wischen zu verwerfen?So deaktivieren Sie das Swipe-to-Fake-Verhalten der Snackbar

Ich habe eine App, die eine Snack-Bar während Netzwerk-Login zeigt, möchte ich vermeiden, dass es entlassen wird.

Nach Nikola Despotoski Vorschlag, den ich habe beide Lösungen experimentiert:

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

} 

dies DummyBehavior Klasse:

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

Aber mein snackbar verschwindet nach wie vor, wenn geklaut und dies ist ein typisches Protokoll:

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 [email protected]c0e9 
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 

Antwort

1

Sie können Streaming-Touch-Ereignisse statt Klicks deaktivieren zur Snackbar Ansicht.

mSnackBar.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: { 
          mInitialTime = System.currentTimeMillis(); 
          break; 
         } 
         case MotionEvent.ACTION_UP: { 
          long clickDuration = System.currentTimeMillis() - mInitialTime; 
          if (clickDuration <= ViewConfiguration.getTapTimeout()) { 
           return false;// click event, proceed normally 
          } 
         } 
        } 
        return true; 
       } 
      }); 

Oder Sie könnten das Snackbar Verhalten mit einigen leeren CoordinatorLayout.Behavior einfach ersetzen:

public CouchPotatoBehavior extends CoordinatorLayout.Behavior<View>{ 

    //override all methods and don't call super methods. 

} 

Dies ist das leere Verhalten, die nichts tut. Der Standard SwipeToDismissBehavior verwendet ViewDragHelper, um Berührungsereignisse zu verarbeiten, auf denen die Entlassung ausgelöst wird.

ViewGroup.LayoutParams lp = mSnackbar.getView().getLayoutParams(); 
if (lp instanceof CoordinatorLayout.LayoutParams) { 
    ((CoordinatorLayout.LayoutParams)lp).setBehavior(new CouchPotatoBehavior()); 
     mSnackbar.getView().setLayoutParams(lp);    
} 
+0

_Oder Sie könnten das Snackbar-Verhalten einfach durch einige leere CoordinatorLayout.Behavior_ ersetzen Ich mag diese Idee ... können Sie erweitern? liebe die Verwendung von 'ViewConfiguration.getTapTimeout()' – petey

+0

@petey Da gehts los. Stellen Sie nur sicher, dass getView() nicht null ist. :) –

+0

Danke für Vorschläge, aber ich kann sie nicht arbeiten. Ich habe meine Frage modifiziert und Code hinzugefügt, der gemäß Ihrer vorgeschlagenen Lösung entwickelt wurde. – Paolone

1

Bessere Lösung hier .... Geben Sie keine CoordinatorLayout oder eines seiner Kinder als Blick in snackbar.

Snackbar.make(ROOT_LAYOUT , "No internet connection", Snackbar.LENGTH_INDEFINITE).show(); 

Wo sollte die ROOT_LAYOUT jedes Layout außer coordinatorlayout oder sein Kind sein.

+1

Gute Lösung, aber benutzen Sie es auf eigenes Risiko. Von [Android-Dokumenten] (https://developer.android.com/reference/android/support/design/widget/Snackbar.html): _ "Wenn Sie ein CoordinatorLayout in Ihrer Ansichtshierarchie verwenden, kann Snackbar bestimmte Funktionen wie z. B. Swipe aktivieren -abweisen ** und automatisches Verschieben von Widgets wie FloatingActionButton **. "_ –

14

Das ist für mich gearbeitet:

Snackbar.SnackbarLayout layout = (Snackbar.SnackbarLayout) snackbar.getView(); 
    snackbar.setDuration(Snackbar.LENGTH_INDEFINITE); 
    snackbar.show(); 
    layout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      ViewGroup.LayoutParams lp = layout.getLayoutParams(); 
      if (lp instanceof CoordinatorLayout.LayoutParams) { 
       ((CoordinatorLayout.LayoutParams) lp).setBehavior(new DisableSwipeBehavior()); 
       layout.setLayoutParams(lp); 
      } 
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 
       layout.getViewTreeObserver().removeOnGlobalLayoutListener(this); 
      } else { 
       //noinspection deprecation 
       layout.getViewTreeObserver().removeGlobalOnLayoutListener(this); 
      } 
     } 
    }); 

Wo DisableSwipeBehavior ist:

public class DisableSwipeBehavior extends SwipeDismissBehavior<Snackbar.SnackbarLayout> { 
    @Override 
    public boolean canSwipeDismissView(@NonNull View view) { 
     return false; 
    } 
} 
+0

Das hat super funktioniert. –

+0

Dies kann eine akzeptierte Antwort sein. Selbst wenn wir die Snackbar nicht benutzen sollten, wenn wir die Wischgeste deaktivieren wollen. – Tobliug

2

Das ist für mich gearbeitet:

Snackbar snackbar = Snackbar.make(findViewById(container), R.string.offers_refreshed, Snackbar.LENGTH_LONG); 
    final View snackbarView = snackbar.getView(); 
    snackbar.show(); 

    snackbarView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { 
     @Override 
     public boolean onPreDraw() { 
      snackbarView.getViewTreeObserver().removeOnPreDrawListener(this); 
      ((CoordinatorLayout.LayoutParams) snackbarView.getLayoutParams()).setBehavior(null); 
      return true; 
     } 
    }); 

Viel Glück! :)

0

Hier ist eine Lösung, die Sie nicht mit ViewTreeObserver durcheinander bringen müssen. Beachten Sie, dass die folgende Lösung in Kotlin basierend auf SDK geschrieben 26.

BaseTransientBottomBar

final void showView() { 
    if (mView.getParent() == null) { 
     final ViewGroup.LayoutParams lp = mView.getLayoutParams(); 

     if (lp instanceof CoordinatorLayout.LayoutParams) { 
      // If our LayoutParams are from a CoordinatorLayout, we'll setup our Behavior 
      final CoordinatorLayout.LayoutParams clp = (CoordinatorLayout.LayoutParams) lp; 

      final Behavior behavior = new Behavior(); 
      behavior.setStartAlphaSwipeDistance(0.1f); 
      behavior.setEndAlphaSwipeDistance(0.6f); 
      behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END); 
      behavior.setListener(new SwipeDismissBehavior.OnDismissListener() { 
       @Override 
       public void onDismiss(View view) { 
        view.setVisibility(View.GONE); 
        dispatchDismiss(BaseCallback.DISMISS_EVENT_SWIPE); 
       } 

       @Override 
       public void onDragStateChanged(int state) { 
        switch (state) { 
         case SwipeDismissBehavior.STATE_DRAGGING: 
         case SwipeDismissBehavior.STATE_SETTLING: 
          // If the view is being dragged or settling, pause the timeout 
          SnackbarManager.getInstance().pauseTimeout(mManagerCallback); 
          break; 
         case SwipeDismissBehavior.STATE_IDLE: 
          // If the view has been released and is idle, restore the timeout 
          SnackbarManager.getInstance() 
            .restoreTimeoutIfPaused(mManagerCallback); 
          break; 
        } 
       } 
      }); 
      clp.setBehavior(behavior); 
      // Also set the inset edge so that views can dodge the bar correctly 
      clp.insetEdge = Gravity.BOTTOM; 
     } 

     mTargetParent.addView(mView); 
    } 

    ... 
} 

Wenn Sie schauen, um den Quellcode BaseTransientBottomBar in Verfahren showView es das Verhalten hinzufügen, wenn die layoutParams ist CoordinatorLayout.LayoutParams. Wir können dies rückgängig machen, indem wir das Verhalten auf null zurücksetzen.

Da es das Verhalten kurz vor dem Anzeigen der Ansicht hinzufügen, sollten wir es rückgängig machen, nachdem die Ansicht angezeigt wird.

val snackbar = Snackbar.make(coordinatorLayout, "Hello World!", Snackbar.LENGTH_INDEFINITE) 
snackbar.show() 
snackbar.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() { 
    override fun onShown(transientBottomBar: Snackbar) { 
     val layoutParams = transientBottomBar.view.layoutParams as? CoordinatorLayout.LayoutParams 
     layoutParams?.let { it.behavior = null } 
    } 
})