Отключение перетаскивания пользователя на нижний лист
Я пытаюсь отключить перетаскивание пользователя BottomSheet
. Почему я хочу отключить это две вещи. 1. Это мешает ListView
прокрутить вниз, 2. Я не хочу, чтобы пользователи увольняли с помощью перетаскивания, но с помощью кнопки на BottomSheetView
. Вот что я сделал
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>
16 ответов:
Это может быть уже не актуально, но я оставлю его здесь:
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 только программно. Вы можете отредактировать его и создать логический флаг для вызова
super
методы вместоreturn false
.
проверить состояние
onStateChanged
методsetBottomSheetCallback
если государство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) { } });
открыть закрыть нижний лист, как показано ниже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
выше способом вы можете достичь своей цели
Я закончил писать обходной путь для решения этого случая использования динамического отключения перетаскивания пользователя, в результате чего 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" />
до сих пор это наиболее последовательно действующее решение для отключения перетаскивания пользователя на нижнем листе по требованию.
все остальные решения, которые полагались на запуск другого вызова setState в обратном вызове onStateChanged, привели к тому, что нижний лист попал в плохое состояние или вызывает значительные проблемы с UX (в случае публикации вызова setState в Runnable).
надеюсь, это кому-то поможет :)
Рэй
хорошо, так что принятый ответ не сработал для меня. Однако,Виталий Обидейко это!--5--> вдохновило мое окончательное решение.
во-первых, я создал следующие пользовательские BottomSheetBehavior. Он переопределяет все методы, связанные с touch, и возвращает 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; } }
вот пример того, как его использовать. В моем случае мне это было нужно, чтобы нижний лист был заблокирован при расширении.
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) {} }); }
надеюсь, это поможет прояснить много путаницы!
принятый ответ не работает на первом тестовом устройстве, которое я использую. И отскок назад не является гладким. Кажется, лучше установить состояние 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) { } });
чтобы заблокировать нижний лист и избежать пользователя, чтобы провести его это то, что я сделал
public void showBottomSheet() { bsb.setHideable(false); bsb.setState(BottomSheetBehavior.STATE_EXPANDED); } public void hideBottomSheet() { bsb.setHideable(true); bsb.setState(BottomSheetBehavior.STATE_COLLAPSED); }
это работает довольно хорошо для меня.
вам не нужно блокировать все события, когда нижний лист отключен. Вы можете заблокировать только событие 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); } }
простой способ, чтобы заблокировать перетаскивание 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); }
Я нашел удивительное решение. Первоначальная проблема заключалась в том, что когда-то bottomSheet собирался в скрытое состояние, тогда он не появлялся в bottomSheetDialog.шоу.)( Но я хотел, чтобы диалоговое окно было видно на методе 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) { } };
поздний ответ, но это то, что сработало для меня, что немного отличается от того, что предложили другие.
вы можете попробовать установить
cancelable
свойство false, т. е.setCancelable(false);
а затем вручную обрабатывать события, где вы хотели бы закрыть диалоговое окно в
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 внутри фрагмента диалога, где я немного застрял с другими решениями.
добавить этот код BottomSheetBehavior "объект". Перетаскивание будет отключено. Отлично работает для меня.
final BottomSheetBehavior behavior = BottomSheetBehavior.from((View) view.getParent()); behavior.setHideable(false); 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) { } });
- скопировать
BottomSheetDialog
к вашему проекту и переименовать вMyBottomSheetDialog
- добавить
getBottomSheetBehavior
доMyBottomSheetDialog
- использовать
MyBottomSheetDialog
вместоBottomSheetDialog
- bottomSheetBehavior.setBottomSheetCallback
код
public class MyBottomSheetDialog extends AppCompatDialog { // some code public BottomSheetBehavior<FrameLayout> getBottomSheetBehavior() { return mBehavior; } // more code
код
final BottomSheetBehavior<FrameLayout> bottomSheetBehavior = myBottomSheetDialog.getBottomSheetBehavior(); bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_DRAGGING) { bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); } } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { }
это в основном версия Котлина правильного ответа вверху:
class UserLockBottomSheetBehavior<V : View> : BottomSheetBehavior<V> { companion object { fun <V : View> from(view: V): UserLockBottomSheetBehavior<V> { val params = view.layoutParams as? CoordinatorLayout.LayoutParams ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout") val behavior = params.behavior as? UserLockBottomSheetBehavior<*> ?: throw IllegalArgumentException( "The view is not associated with BottomSheetBehavior") return behavior as UserLockBottomSheetBehavior<V> } } constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, directTargetChild: View, target: View, axes: Int, type: Int): Boolean { return false } override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {} override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) {} override fun onNestedPreFling(coordinatorLayout: CoordinatorLayout, child: V, target: View, velocityX: Float, velocityY: Float): Boolean { return false } }
вот рабочая версия топового решения в Котлине:
import android.support.design.widget.BottomSheetBehavior import android.support.design.widget.CoordinatorLayout import android.view.MotionEvent import android.view.View class CustomBottomSheetBehavior<V : View> : BottomSheetBehavior<V>() { @Suppress("UNCHECKED_CAST") companion object { fun <V : View> from(view: V): CustomBottomSheetBehavior<V> { val params = view.layoutParams as? CoordinatorLayout.LayoutParams ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout") params.behavior as? BottomSheetBehavior<V> ?: throw IllegalArgumentException("The view is not associated with BottomSheetBehavior") params.behavior = CustomBottomSheetBehavior<V>() return params.behavior as CustomBottomSheetBehavior<V> } } override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean { return false } override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, directTargetChild: View, target: View, axes: Int, type: Int): Boolean { return false } override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {} override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) {} override fun onNestedPreFling(coordinatorLayout: CoordinatorLayout, child: V, target: View, velocityX: Float, velocityY: Float): Boolean { return false } }
тогда, когда вы хотите использовать:
val bottomSheetBehavior by lazy { CustomBottomSheetBehavior.from(bottom_sheet_main) }
The
bottom_sheet_main
это фактический вид с помощью Котлин Android Расширения.
установите для параметра bottomSheet onClickListener значение null.
bottomSheet.setOnClickListener(null);
эта строка отключает все действия только для нижнего листа и не влияет на внутренний вид.
настройка
peakHeight
стоимость работал для меня.Я устанавливаю peakheight как высоту нижнего листа, если он расширен.
private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() { override fun onSlide(bottomSheet: View, slideOffset: Float) { } override fun onStateChanged(bottomSheet: View, newState: Int) { if (newState == BottomSheetBehavior.STATE_EXPANDED) bottomSheetBehavior.peekHeight = bottomSheet.height } }