在底部工作表中禁用用户拖动
我正试图禁用用户在BottomSheet
拖动。 我想禁用的原因是两件事。 1.防止ListView
向下滚动,2.我不希望用户使用拖动,而是使用BottomSheetView
上的button。 这就是我所做的
bottomSheetBehavior = BottomSheetBehavior.from(bottomAnc); bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { //Log.e("BottomSheet", "Expanded"); } else if (newState == BottomSheetBehavior.STATE_COLLAPSED) { //Log.e("BottomSheet", "Collapsed"); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { // React to dragging events bottomSheet.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); switch (action) { case MotionEvent.ACTION_DOWN: return false; default: return true; } } }); } });
bottomSheetLayout
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" app:behavior_hideable="true" app:behavior_peekHeight="0dp" app:layout_behavior="@string/bottom_sheet_behavior" android:id="@+id/bottomSheet"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="match_parent" app:elevation="10dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical"> <TextView android:id="@+id/text1" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="Order Items" android:layout_margin="16dp" android:textAppearance="@android:style/TextAppearance.Large"/> <Button android:layout_width="50dp" android:layout_height="wrap_content" android:layout_marginRight="5dp" android:background="@drawable/bg_accept"/> <Button android:layout_width="50dp" android:layout_height="wrap_content" android:layout_marginRight="8dp" android:background="@drawable/bg_cancel"/> </LinearLayout> <ListView android:id="@+id/item_edit" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" android:divider="@color/md_divider_black" android:dividerHeight="1dp"/> </LinearLayout> </android.support.v7.widget.CardView>
现在可能不再相关了,但我会把它留在这里:
import android.content.Context; import android.support.design.widget.BottomSheetBehavior; import android.support.design.widget.CoordinatorLayout; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; /** * Created by vitaliiobideiko on 10/5/16. */ public class UserLockBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> { public UserLockBottomSheetBehavior() { super(); } public UserLockBottomSheetBehavior(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { return false; } @Override public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { return false; } @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { return false; } @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {} @Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {} @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) { return false; } }
它会禁用所有用户的操作,只有以编程方式控制BottomSheet时才可以使用。 您可以编辑它并创build用于调用super
方法的布尔标志,而不是return false
。
在setBottomSheetCallback
onStateChanged
方法中检查状态,如果状态是BottomSheetBehavior.STATE_DRAGGING
则将其更改为BottomSheetBehavior.STATE_EXPANDED
这样可以停止用户的STATE_DRAGGING
。 如下所示
final BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet); behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_DRAGGING) { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } });
使用button打开如下所示的封闭底部表单
fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (behavior.getState() == BottomSheetBehavior.STATE_HIDDEN) { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } else { behavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } } });
请勿使用setPeekHeight
或app:behavior_peekHeight
通过上面的方式,你可以达到你的目标
我最终编写了一个解决方法来解决这个dynamic禁用用户拖动的用例,即BottomSheetBehavior被子类化,以覆盖onInterceptTouchEvent,并在自定义标志(在这种情况下,mAllowUserDragging)设置为false时忽略它:
import android.content.Context; import android.support.design.widget.BottomSheetBehavior; import android.support.design.widget.CoordinatorLayout; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class WABottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> { private boolean mAllowUserDragging = true; /** * Default constructor for instantiating BottomSheetBehaviors. */ public WABottomSheetBehavior() { super(); } /** * Default constructor for inflating BottomSheetBehaviors from layout. * * @param context The {@link Context}. * @param attrs The {@link AttributeSet}. */ public WABottomSheetBehavior(Context context, AttributeSet attrs) { super(context, attrs); } public void setAllowUserDragging(boolean allowUserDragging) { mAllowUserDragging = allowUserDragging; } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { if (!mAllowUserDragging) { return false; } return super.onInterceptTouchEvent(parent, child, event); } }
而在你的布局xml中:
<FrameLayout android:id="@+id/bottom_sheet_frag_container" android:layout_width="match_parent" android:layout_height="match_parent" app:behavior_hideable="true" app:behavior_peekHeight="@dimen/bottom_sheet_peek_height" app:elevation="@dimen/bottom_sheet_elevation" app:layout_behavior="com.example.ray.WABottomSheetBehavior" />
到目前为止,这是用于在需求底层禁用用户拖动的最一贯行为的解决scheme。
所有依靠在onStateChangedcallback中触发另一个setState调用的其他解决scheme导致BottomSheet进入错误状态,或导致重大的UX问题(将setState调用发布到Runnable中)。
希望这可以帮助别人:)
射线
好的,所以接受的答案对我来说不起作用。 然而, ВиталийОбидейко的答案启发了我的最终解决scheme。
首先,我创build了下面的自定义BottomSheetBehavior。 它覆盖所有涉及触摸的方法,如果locking,则返回false(或者什么也不做)。 否则,它就像一个正常的BottomSheetBehavior一样。 这将禁止用户拖下来的能力,而不会影响更改代码中的状态。
LockableBottomSheetBehavior.java
public class LockableBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> { private boolean mLocked = false; public LockableBottomSheetBehavior() {} public LockableBottomSheetBehavior(Context context, AttributeSet attrs) { super(context, attrs); } public void setLocked(boolean locked) { mLocked = locked; } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { boolean handled = false; if (!mLocked) { handled = super.onInterceptTouchEvent(parent, child, event); } return handled; } @Override public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { boolean handled = false; if (!mLocked) { handled = super.onTouchEvent(parent, child, event); } return handled; } @Override public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { boolean handled = false; if (!mLocked) { handled = super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes); } return handled; } @Override public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) { if (!mLocked) { super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed); } } @Override public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { if (!mLocked) { super.onStopNestedScroll(coordinatorLayout, child, target); } } @Override public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) { boolean handled = false; if (!mLocked) { handled = super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); } return handled; } }
这里有一个如何使用它的例子。 在我的情况下,我需要它,所以展开底部表locking。
activity_home.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|snap" app:titleEnabled="false"/> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"/> </android.support.design.widget.AppBarLayout> <!-- Use layout_behavior to set your Behavior--> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutManager="android.support.v7.widget.LinearLayoutManager" app:layout_behavior="com.myapppackage.LockableBottomSheetBehavior"/> </android.support.design.widget.CoordinatorLayout>
HomeActivity.java
public class HomeActivity extends AppCompatActivity { BottomSheetBehavior mBottomSheetBehavior; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview); recyclerView.setAdapter(new SomeAdapter()); mBottomSheetBehavior = BottomSheetBehavior.from(recyclerView); mBottomSheetBehavior.setBottomSheetCallback(new MyBottomSheetCallback()); } class MyBottomSheetCallback extends BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { if (mBottomSheetBehavior instanceof LockableBottomSheetBehavior) { ((LockableBottomSheetBehavior) mBottomSheetBehavior).setLocked(true); } } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) {} }); }
希望这有助于澄清很多的困惑!
接受的答案不适用于我使用的第一个testing设备。 反弹并不顺利。 只有在用户释放拖动之后,才能将状态设置为STATE_EXPANDED。 以下是我的版本:
final BottomSheetBehavior behavior = BottomSheetBehavior.from(findViewById(R.id.bottomSheet)); behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState > BottomSheetBehavior.STATE_DRAGGING) bottomSheet.post(new Runnable() { @Override public void run() { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } }); } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } });
迟到的答案,但是,这对我来说是有效的,这与其他人所build议的有些不同。
您可以尝试将cancelable
属性设置为false,即
setCancelable(false);
然后手动处理您想要在setupDialog
方法中closures对话框的setupDialog
。
@Override public void setupDialog(final Dialog dialog, final int style) { // handle back button dialog.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(final DialogInterface dialog, final int keyCode, final KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { dialog.dismiss(); } return true; } }); // handle touching outside of the dialog final View touchOutsideView = getDialog().getWindow().getDecorView().findViewById(android.support.design.R.id.touch_outside); touchOutsideView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View view) { dialog.dismiss(); } }); }
这与对话框片段中的ListView一起工作,这是我在其他解决scheme中遇到的一些问题。
要lockingBottomSheet并避免用户将其滑出,这就是我所做的
public void showBottomSheet() { bsb.setHideable(false); bsb.setState(BottomSheetBehavior.STATE_EXPANDED); } public void hideBottomSheet() { bsb.setHideable(true); bsb.setState(BottomSheetBehavior.STATE_COLLAPSED); }
它对我来说工作得很好。
我find了惊人的解决scheme。 最初的问题是一旦bottomSheet将HIDDEN状态,然后它不显示在bottomSheetDialog.show()。 但是我想让show()方法看见对话框,并且想让用户将它滑下来,这样就像底部表单一样。 以下是我做的
BottomSheetDialog itemTypeDialog = new BottomSheetDialog(this); View bottomSheetView = getLayoutInflater().inflate(R.layout.dialog_bottomsheet, null); itemTypeDialog.setContentView(bottomSheetView); BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from((View) bottomSheetView.getParent()); bottomSheetBehavior.setBottomSheetCallback(bottomSheetCallback); // You can also attach the listener here itself. BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { Log.d(TAG, "BottomSheetCallback: " + newState); if (newState == BottomSheetBehavior.STATE_HIDDEN) { bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); itemTypeDialog.dismiss(); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } };
当底部工作表被禁用时,您不需要阻止所有事件。 您只能阻止ACTION_MOVE事件。 这就是为什么像这样使用自定义的底部表单行为
public class BottomSheetBehaviorWithDisabledState<V extends View> extends BottomSheetBehavior<V> { private boolean enable = true; /** * Default constructor for instantiating BottomSheetBehaviors. */ public BottomSheetBehaviorWithDisabledState() { super(); } /** * Default constructor for inflating BottomSheetBehaviors from layout. * * @param context The {@link Context}. * @param attrs The {@link AttributeSet}. */ public BottomSheetBehaviorWithDisabledState(Context context, AttributeSet attrs) { super(context, attrs); } public void setEnable(boolean enable){ this.enable = enable; } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { if (!enable && event.getAction() == MotionEvent.ACTION_MOVE){ return false; } return super.onInterceptTouchEvent(parent, child, event); } }
简单的方法来locking拖动是setPeekHeight相同的视图高度。 例如:
private LinearLayout bottomSheet; private BottomSheetBehavior bottomBehavior; @Override public void onResume() { super.onResume(); bottomBehavior = BottomSheetBehavior.from((bottomSheet); bottomBehavior.setPeekHeight(bottomSheet.getHeight()); bottomBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); }
将此代码添加到BottomSheetBehavior对象。 拖动将被禁用。 为我工作得很好。
最后BottomSheetBehavior行为= BottomSheetBehavior.from((View)view.getParent()); behavior.setHideable(假); behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback(){
@Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_DRAGGING) { behavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { } });
这对我来说是完美的。
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) { bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); bottomSheetBehavior.setHideable(false); } else if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED{ bottomSheetBehavior.setHideable(true); bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); }