19

有没有办法防止用户通过在小吃店上滑动来关闭它?

我有一个在网络登录期间显示小吃店的应用程序,我想避免它被解雇。

根据 Nikola Despotoski 的建议,我尝试了两种解决方案:

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

}

这是 DummyBehavior 类:

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

但是我的小吃栏在刷卡时仍然消失,这是一个典型的日志:

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
4

8 回答 8

18

这对我有用:

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

DisableSwipeBehavior 在哪里:

public class DisableSwipeBehavior extends SwipeDismissBehavior<Snackbar.SnackbarLayout> {
    @Override
    public boolean canSwipeDismissView(@NonNull View view) {
        return false;
    }
}
于 2016-05-06T12:23:56.860 回答
17

Snackbar 现在通过使用 setBehavior 方法对此提供了实际支持。这里最棒的一点是,以前你总是会丢失一些现在保留下来的行为。

请注意,包已移动,因此您必须在 snapbar 包中导入“新”Snackbar

Snackbar.make(view, stringId, Snackbar.LENGTH_LONG)
    .setBehavior(new NoSwipeBehavior())
    .show();

class NoSwipeBehavior extends BaseTransientBottomBar.Behavior {

    @Override
    public boolean canSwipeDismissView(View child) {
      return false;
    }
}
于 2018-05-16T18:22:49.723 回答
5

这对我有用:

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

祝你好运!:)

于 2017-01-11T01:25:08.650 回答
1

这里有更好的解决方案......不要CoordinatorLayout在小吃店中提供或其任何子项作为视图。

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

其中,ROOT_LAYOUT应该是除 coordinatorlayout 或其子级之外的任何布局。

于 2016-02-23T11:50:25.633 回答
1

您可以禁用流式触摸事件而不是单击Snackbar视图。

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

或者你可以Snackbar用一些空的替换行为CoordinatorLayout.Behavior

public CouchPotatoBehavior extends CoordinatorLayout.Behavior<View>{

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

}

这是空洞的行为,什么都不做。默认SwipeToDismissBehavior用于ViewDragHelper处理触发解除的触摸事件。

 ViewGroup.LayoutParams lp = mSnackbar.getView().getLayoutParams();
 if (lp instanceof CoordinatorLayout.LayoutParams) {
     ((CoordinatorLayout.LayoutParams)lp).setBehavior(new CouchPotatoBehavior());
       mSnackbar.getView().setLayoutParams(lp);              
}
于 2015-12-01T22:45:36.033 回答
0

只需覆盖CoordinatorLayout.LayoutParams.getBehaviour()返回 null以禁用滑动。

例如:

Snackbar snackBar = Snackbar.make(view, "Enjoy!", Snackbar.LENGTH_INDEFINITE);
CoordinatorLayout.LayoutParams snackBarLayoutParams = new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.MATCH_PARENT, CoordinatorLayout.LayoutParams.WRAP_CONTENT){
                @Override
                public CoordinatorLayout.Behavior getBehavior() {
                    return null;
                }
            };
snackBarLayoutParams.gravity = Gravity.BOTTOM;
snackBar.getView().setLayoutParams(snackBarLayoutParams);
snackBar.show();

享受!

于 2021-06-08T14:37:02.883 回答
0

我有一个带有透明元素的小吃店(作为带有自定义视图的 Toast 的替代品,现在已弃用)。有几个挑战需要克服:

  1. 小吃店可以刷掉。
  2. 触摸小吃店的任何部分(包括透明背景部分)都会刷新自动关闭计时器。
  3. 由于小吃店拦截触摸事件,小吃店后面的 UI 元素(包括透明背景部分)无法交互。

要完全禁用与小吃店的所有交互,我使用以下内容。

// Counteract SnackbarBaseLayout's consumeAllTouchListener which prevents
// touches from going through our snack bar, because (a) most of our snack 
// bar is invisible and (b) we don't have any interaction elements
snackbar.view.setOnTouchListener { _, _ -> false }
snackbar.view.isFocusable = false

// Disable swipe-to-dismiss on the snackbar. Also important as if a user 
// clicks on a snackbar (including the transparent parts of it) then the 
// auto-dismiss timer is deferred by default.
snackbar.behavior = object : BaseTransientBottomBar.Behavior() {
    override fun canSwipeDismissView(child: View) = false
    override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: View, event: MotionEvent) = false
}
于 2021-11-16T03:12:16.877 回答
0

这是一个不需要您弄乱的解决方案ViewTreeObserver。请注意,以下解决方案是基于 SDK 26 用 Kotlin 编写的。

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

    ...
}

如果您查看 inBaseTransientBottomBar方法的源代码,showView如果 layoutParams 是,它会添加行为CoordinatorLayout.LayoutParams。我们可以通过将行为设置回 来撤消此操作null

因为它在视图显示之前添加了行为,所以我们应该在视图显示后撤消它。

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 }
    }
})
于 2017-10-13T07:06:48.067 回答