Отключение перетаскивания пользователем на BottomSheet


100

Я пытаюсь отключить перетаскивание пользователя 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;
                    }
                }
            });
        }
    });

The 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>


Пожалуйста, проверьте мой ответ. Я заметил, что это более актуально, чем принятый ответ
Виталий Обидейко

Ответы:


92

Возможно, сейчас это уже не актуально, но я оставлю это здесь:

import android.content.Context
import android.util.AttributeSet
import androidx.coordinatorlayout.widget.CoordinatorLayout
import android.view.MotionEvent
import android.view.View
import com.google.android.material.bottomsheet.BottomSheetBehavior

@Suppress("unused")
class LockableBottomSheetBehavior<V : View> : BottomSheetBehavior<V> {
    constructor() : super()
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    var swipeEnabled = true

    override fun onInterceptTouchEvent(
        parent: CoordinatorLayout,
        child: V,
        event: MotionEvent
    ): Boolean {
        return if (swipeEnabled) {
            super.onInterceptTouchEvent(parent, child, event)
        } else {
            false
        }
    }

    override fun onTouchEvent(parent: CoordinatorLayout, child: V, event: MotionEvent): Boolean {
        return if (swipeEnabled) {
            super.onTouchEvent(parent, child, event)
        } else {
            false
        }
    }

    override fun onStartNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        directTargetChild: View,
        target: View,
        axes: Int,
        type: Int
    ): Boolean {
        return if (swipeEnabled) {
            super.onStartNestedScroll(
                coordinatorLayout,
                child,
                directTargetChild,
                target,
                axes,
                type
            )
        } else {
            false
        }
    }

    override fun onNestedPreScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        target: View,
        dx: Int,
        dy: Int,
        consumed: IntArray,
        type: Int
    ) {
        if (swipeEnabled) {
            super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
        }
    }

    override fun onStopNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        target: View,
        type: Int
    ) {
        if (swipeEnabled) {
            super.onStopNestedScroll(coordinatorLayout, child, target, type)
        }
    }

    override fun onNestedPreFling(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        target: View,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        return if (swipeEnabled) {
            super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY)
        } else {
            false
        }
    }
}

И используйте его в своем xml файле:

app:layout_behavior="com.your.package.LockableBottomSheetBehavior"

Он отключает все действия пользователей, его можно использовать, когда вы хотите управлять BottomSheet только программно.


2
Это лучший ответ для отключения BottomSheetBehaviour. Человек выше также опубликовал похожее решение, но он не писал, чтобы переопределить другие события, такие как onTouchEvent () . С другой стороны, вы можете улучшить свой ответ, если поставите флажок вместо false
murt

3
Как вы используете это с BottomSheetFragment?
user3144836

7
Вам нужно конкретно указать этот класс в вашем XML. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Стив,

3
В некоторых случаях это все еще не работает, если у нас есть список во фрагменте нижнего листа, он все равно тащит
Дипак Джоши

1
@DeepakJoshi, возможно, вы можете расширить RecyclerView и переопределить несколько методов, таких как hasNestedScrollingParent, но я не уверен
Виталий Обидейко

74

проверьте состояние в onStateChangedметоде setBottomSheetCallbackif state, 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

указанным выше способом вы можете достичь своей цели


1
Хороший трюк. Не заметил этого. Спасибо. А также можете ли вы помочь с этим. Когда я сначала говорю ему расширяться, он прозрачен, и я вижу вид сзади, но я не могу взаимодействовать, пока не коснусь EditText в SheetView, прежде чем сделать его видимым.
Tonespy 04

Я сделал свой, BottomSheet View match_parentи всякий раз, когда я пытался поднять его в своем, Activityя замечал, что он скользит вверх, но его не видно, пока я не EditTextKeyboardBottomSheet View
коснусь

1
Я пробовал это, но в штатах оказывается STATE_SETTLING. У меня есть кнопка для открытия и закрытия нижнего листа, если она СКРЫТА, разворачиваю. Если он РАСШИРЕН, я его прячу. Поскольку он застревает в SETTLING, моя кнопка не работает после перетаскивания нижнего листа. Есть идеи по этому поводу?
Gokhan Arik

3
Это решение ненадежно; нижний лист переходит в плохое состояние, как сказал Гохан ... и в этом плохом состоянии такие вызовы, как загрузка нового фрагмента в нижний лист, просто гаснут.
Ray W

7
Это не будет работать , если у вас есть nestedscrollview в нижний лист
Rishabh Chandel

32

Хорошо, принятый ответ мне не подошел. Однако ответ Виталий Обидейко вдохновил мое окончательное решение.

Сначала я создал следующий пользовательский BottomSheetBehavior. Он отменяет все методы, связанные с касанием, и возвращает 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) {}
    });
}

Надеюсь, это поможет устранить большую путаницу!


1
Хорошо, это лучший ответ, что мы можем избежать обходного пути этих состояний, которые приводят к пропуску событий. Спасибо.
Tn Nguyên

@James - Хороший ответ, но теперь я не могу установить PeekHeight (). Любая идея?
Адарш Ядав

Я пробовал это. меня устраивает. спасибо братан за спасение моей задницы
Sup.Ia

1
Это хороший обходной путь, хотя на сегодняшний день он не обновляется. OnNestedPreScroll и некоторые другие методы устарели. Необходимо обновить эти методы, и все работает нормально.
Ajay

4
Здравствуйте, это не работает с BottomSheetDialogFragment, я все еще могу перетащить нижний лист
florian-do

23

Я закончил тем, что написал обходной путь для решения этого варианта использования динамического отключения перетаскивания пользователя, в соответствии с которым 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, приводили к тому, что BottomSheet попадал в плохое состояние или вызывали значительные проблемы UX (в случае публикации вызова setState в Runnable).

Надеюсь, это кому-то поможет :)

Рэй


4
Это довольно здорово
Odys

3
@BeeingJk Вместо FrameLayout используйте NestedScrollView и установитеbottomSheetFragContainer.setNestedScrollingEnabled(false);
Afzal N

1
РЕШЕНО: установив callback-функцию CoordinatorLayout.Behavior behavior = layoutParams.getBehavior (); if (поведение! = null && поведение instanceof BottomSheetBehavior) {((BottomSheetBehavior) поведение) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG

4
Это не для меня! PS: У меня есть прокручиваемый текст в нижнем листе
Торвальд Олавсен

6
Как его кастовать при инициализации? Это дает мне предупреждение WABottomSheetBehavior <View> behavior = (WABottomSheetBehavior) BottomSheetBehavior.from (sheetView);
Leo Droidcoder,

8

Поздний ответ, но это то, что сработало для меня, что немного отличается от того, что предлагали другие.

Вы можете попробовать установить для 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 внутри фрагмента диалогового окна, в котором я немного застревал с другими решениями.


Красивое лаконичное решение. Для всех, кто читает это, вы (вероятно) захотите провести дополнительные проверки для event.isCanceled()и event.getAction() == MotionEvent.ACTION_UPперед закрытием диалогового окна - это предотвратит отклонение от ошибочных щелчков.
Эрик Бачхубер

Спасибо за это. Это простейшее решение для отключения перетаскивания.
AVJ 01

7

Принятый ответ не работает на первом используемом мной тестовом устройстве. И возврат в норму не бывает плавным. Кажется, лучше установить состояние 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) {
        }
    });

1
Позвольте мне рассказать вам о проблеме с добавлением его в runnable, если вы этого не хотите. Вы не можете закрыть его кнопкой, потому что его нужно перетащить, чтобы закрыть. И он всегда будет реагировать на перетаскивание, просто чтобы предотвратить перетаскивание пользователя, чтобы закрыть
Tonespy 05

7

Добавьте этот код в объект 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) {

      }
});

1
Это не отключает смахивание. Он полностью сворачивает нижний лист.
Адам Гурвиц

7

Ожидаемое поведение:

  • BottomSheet не закрывается при перетаскивании
  • BottomSheet закрывается, если касаться за пределами диалогового окна

Код:

class MyBottomSheet : BottomSheetDialogFragment () {

   override fun onActivityCreated(savedInstanceState: Bundle?) {
       super.onActivityCreated(savedInstanceState)
       disableBottomSheetDraggableBehavior()
   }

   private fun disableBottomSheetDraggableBehavior() {
      this.isCancelable = false
      this.dialog?.setCanceledOnTouchOutside(true)
   }

 }

По какой-то причине я не могу закрыть диалоговое окно, касаясь снаружи, но он работает, чтобы отключить перетаскивание
Гастон Сайлен,

5

Чтобы заблокировать BottomSheet и избежать того, чтобы пользователь смахнул его, вот что я сделал

public void showBottomSheet() {
    bsb.setHideable(false);
    bsb.setState(BottomSheetBehavior.STATE_EXPANDED);
}

public void hideBottomSheet() {
    bsb.setHideable(true);
    bsb.setState(BottomSheetBehavior.STATE_COLLAPSED);
}

У меня это работает очень хорошо.


Это решение было привлекательным, но из-за чего нижний лист появлялся в верхней части экрана, а не в нижней части! Однако он исчезает обычным образом. Это очень похоже на Star Trek.
Tunga

Мне нужно было сделать модификацию прицела и вместо него использовать BottomSheetBehavior.STATE_HIDDEN. В таком случае звонить также нельзя setPeekHeight(). Здесь это намного проще, чем другие решения.
HolySamosa

5

Простой способ заблокировать перетаскивание - 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);
}

4

Пример с BottomSheetDialogFragment. Работает отлично.

Изменить 09/04/2020: заменено устаревшим setBottomSheetCallback()наaddBottomSheetCallback()

class FragMenuBDrawer : BottomSheetDialogFragment() {

    ...

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog

        dialog.setOnShowListener {
            val bottomSheet = (it as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout?
            val behavior = BottomSheetBehavior.from(bottomSheet!!)
            behavior.state = BottomSheetBehavior.STATE_EXPANDED

            behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
                override fun onStateChanged(bottomSheet: View, newState: Int) {
                    if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                        behavior.state = BottomSheetBehavior.STATE_EXPANDED
                    }
                }

                override fun onSlide(bottomSheet: View, slideOffset: Float) {}
            })
        }

        // Do something with your dialog like setContentView() or whatever
        return dialog
    }

    ...
}

3

Вам не нужно блокировать все события, когда нижний лист отключен. Вы можете заблокировать только событие 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);
    }
}

Как вы используете этот класс? Я получаю исключение IllegalArgumentException: представление не связано с BottomSheetBehavior
user3144836

3

Вот рабочая версия топового решения в Kotlin:

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

Это bottom_sheet_mainфактическое представление с использованием Kotlin Android Extensions .


3

установите для bottomSheet onClickListener значение null.

bottomSheet.setOnClickListener(null);

эта строка отключает все действия только с bottomSheet и не влияет на внутренний вид.


1
Это вызывает неожиданную анимацию при попытке закрытия нижнего листа.
Адам Гурвиц

3
implementation 'com.google.android.material:material:1.2.0-alpha05'

вы можете отключить перетаскивание BottomSheet вот так.

import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED

//another code

this.bottomSheetBehavior = BottomSheetBehavior.from(view)
this.bottomSheetBehavior.state = STATE_EXPANDED
this.bottomSheetBehavior.isDraggable = false // disable dragging

//another code
this.bottomSheetbehavior.isDraggable = true //draggable

(Котлин), надеюсь, этот ответ поможет решить вашу проблему.


1
Эта альфа-версия ведет себя безумно. Не рекомендую :(
Адам Стирк

2

Я нашел удивительное решение. Первоначальная проблема заключалась в том, что когда 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) {

    }
};

это идеальный ответ
Вивек Кумар Шривастава

2
  1. Скопируйте BottomSheetDialogв свой проект и переименуйте вMyBottomSheetDialog
  2. добавить getBottomSheetBehaviorкMyBottomSheetDialog
  3. использовать MyBottomSheetDialogвместоBottomSheetDialog
  4. 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) {

        }

2

Это, по сути, котлинская версия правильного ответа вверху:

    class LockedBottomSheetBehavior<V : View>(context: Context, attrs: AttributeSet) :
        BottomSheetBehavior<V>(context, attrs) {

    companion object {
        fun <V : View> from(view: V): LockedBottomSheetBehavior<*> {
            val params = view.layoutParams as? CoordinatorLayout.LayoutParams
                    ?: throw IllegalArgumentException("The view is not a child of CoordinatorLayout")
            return params.behavior as? LockedBottomSheetBehavior<*>
                    ?: throw IllegalArgumentException(
                            "The view is not associated with BottomSheetBehavior")
        }
    }

    override fun onInterceptTouchEvent(
            parent: CoordinatorLayout,
            child: V, event: MotionEvent
    ) = false

    override fun onTouchEvent(
            parent: CoordinatorLayout,
            child: V,
            event: MotionEvent
    ) = false

    override fun onStartNestedScroll(
            coordinatorLayout: CoordinatorLayout,
            child: V,
            directTargetChild: View,
            target: View,
            axes: Int,
            type: Int) = 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
    ) = false
}

Как вы используете этот класс? Я получаю исключение IllegalArgumentException: представление не связано с BottomSheetBehavior
user3144836

1
app: layout_behavior = "UserLockBottomSheetBehavior"> в xml, а затем в коде, сделайте следующее. // получить представление нижнего листа LinearLayout llBottomSheet = (LinearLayout) findViewById (R.id.bottom_sheet); // инициируем поведение нижнего листа BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from (llBottomSheet);
Джамал

2

С участием 'com.google.android.material:material:1.2.0-alpha06'

Отлично работает с NestedScrollViewиRecyclerView

Пример кода:

    LinearLayout contentLayout = findViewById(R.id.contentLayout);
    sheetBehavior = BottomSheetBehavior.from(contentLayout);
    sheetBehavior.setDraggable(false);

1

Попробуй это.

1) Создайте нижний лист и объявите переменную в своем классе Java, например

private BottomSheetBehavior sheetBehavior;

2) sheetBehavior = BottomSheetBehavior.from(bottomSheet);

3) В функцию обратного вызова нижнего листа добавьте следующие строки.

sheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                switch (newState) {
                    case BottomSheetBehavior.STATE_HIDDEN:
                        Log.d(TAG, "--------------  STATE_HIDDEN");
                        break;
                    case BottomSheetBehavior.STATE_EXPANDED: {
                        Log.d(TAG, "--------------  STATE_EXPANDED");
                    }
                    break;
                    case BottomSheetBehavior.STATE_COLLAPSED: {
                        Log.d(TAG, "--------------  STATE_COLLAPSED");
                    }
                    break;
                    case BottomSheetBehavior.STATE_DRAGGING:
                        sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                        break;
                    case BottomSheetBehavior.STATE_SETTLING:
                        Log.d(TAG, "--------------  STATE_SETTLING");
                        break;
                }
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {

            }
        });

1

Сначала я просто хочу поблагодарить всех, кто пытался дать ответ. Я просто пишу этот ответ, решая эту проблему, как хочу. Я собираюсь описать, как я это делаю, шаг за шагом, воспользовавшись помощью здесь.

Визуализация: после нажатия на кнопку Show BottomSheetвы увидите второй экран . Теперь вы увидите, что BottomSheet просто заблокирован для перетаскивания . Но если вы нажмете на Список стран, BottomSheet скроется. Это было описание, теперь давайте углубимся в Кодекс.

  • Сначала добавьте библиотеку поддержки дизайна в файл build.gradle :

    реализация 'com.android.support:design:28.0.0'

  • UserLockBottomSheetBehavior.java : Предоставлено : Джеймс Дэвис (спасибо, человек)

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

}
  • botomsheet.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/bottomSheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center_vertical"
    android:orientation="vertical"
    app:behavior_hideable="true"
    app:layout_behavior="com.samsolution.custombottomsheet.UserLockBottomSheetBehavior">

 <RelativeLayout
     android:id="@+id/minimizeLayout"
     android:background="@color/colorPrimary"
     android:layout_width="match_parent"
     android:layout_height="?android:attr/actionBarSize">

     <TextView
         android:layout_centerHorizontal="true"
         android:padding="16dp"
         android:layout_width="wrap_content"
         android:layout_height="?android:attr/actionBarSize"
         android:gravity="center_horizontal|center"
         android:text="Country List"
         android:textColor="#FFFFFF"
         android:textStyle="bold" />
 </RelativeLayout>

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ListView
            android:id="@+id/homeCountryList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </android.support.v7.widget.CardView>

</LinearLayout>
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFFFFF"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="center"
        android:onClick="showCountryListFromBottomSheet">

        <Button
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_red_light"
            android:onClick="showCountryListFromBottomSheet"
            android:padding="16dp"
            android:text="Show BottomSheet"
            android:textAllCaps="false"
            android:textColor="#ffffff" />

    </LinearLayout>

    <include layout="@layout/bootomsheet" />

</android.support.design.widget.CoordinatorLayout>
  • MainActivity.java
public class MainActivity extends AppCompatActivity {

    private BottomSheetBehavior<LinearLayout> bottomSheetBehavior;                                  // BottomSheet Instance
    LinearLayout bottomsheetlayout;
    String[] list = {"A", "B", "C", "D", "A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bottomsheetlayout = findViewById(R.id.bottomSheet);
        bottomSheetBehavior = BottomSheetBehavior.from(bottomsheetlayout);

        ListView listView = findViewById(R.id.homeCountryList);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,list);
        listView.setAdapter(adapter);

        bottomSheetHide();                                                                          //BottomSheet get hide first time

        RelativeLayout minimizeLayoutIV;                                                            // It will hide the bottomSheet Layout
        minimizeLayoutIV = findViewById(R.id.minimizeLayout);
        minimizeLayoutIV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               bottomSheetHide();
            }
        });
    }

    public void showCountryListFromBottomSheet(View view) {
        bottomSheetBehavior.setHideable(false);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    }

    public void bottomSheetHide(){
        bottomSheetBehavior.setHideable(true);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
    }
}

Первый экран Второй экран


1

Решение из принятого ответа в основном сработало для меня, но с одной проблемой: представления, которые находятся за представлением нижнего листа, начали реагировать на события касания, если событие касания происходит в области нижнего листа, которая свободна от дочерних представлений. Другими словами, как вы можете видеть на изображении ниже, когда пользователь перемещает палец внутрь нижнего листа, карта начинает реагировать на это.

сенсорная область нижнего листа

Чтобы решить эту проблему, я изменил onInterceptTouchEventметод, установив вид touchListenerснизу листа (остальной код остается таким же, как в принятом решении).

override fun onInterceptTouchEvent(
        parent: CoordinatorLayout,
        child: V, event: MotionEvent
    ): Boolean {
        child.setOnTouchListener { v, event ->
            true
        }
        return false
    }

0

Изменение 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
    }
}

1
Это не идеально, так как может вызвать неожиданную анимацию.
Адам Гурвиц

В моем случае. Это не вызвало никаких проблем с анимацией. Он просто не двигается после раскрытия карты. Это не идеально, но работает, как ожидалось!
pz64_ 05

Интересно, что это могло быть так. Я решил проблему с неожиданным закрытием нижнего листа, установив для панели инструментов CollapsingToolbarLayout значение «Невидимая» или «Скрытая», когда нижний лист открыт. Сенсорное взаимодействие, связанное с панелью инструментов, несмотря на то, что она находилась под ней, вызывало неожиданное закрытие нижнего листа. Проблема решена.
Адам Гурвиц

0
    LayoutInflater inflater = LayoutInflater.from(context);
            View view = inflater.inflate(R.layout.bottomsheet_view_profile_image, null);
            BottomSheetDialog dialog = new BottomSheetDialog(context);
            dialog.setContentView(view);
            dialog.setCancelable(false);


            BottomSheetBehavior behavior = BottomSheetBehavior
                    .from(((View) view.getParent()));
            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) {
                }
            });
            behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            behavior.setSkipCollapsed(true);
            dialog.show();

0

это первый результат в Google, поэтому я считаю, что будет справедливо поместить здесь простое решение:

   private fun disableDragToDismiss() {
    if (dialog is BottomSheetDialog) {
        val bsDialog = dialog as BottomSheetDialog
        bsDialog.behavior.isHideable = false
    } else {
        Log.d(TAG, " BottomSheetDialog with dialog that is not BottomSheetDialog")
    }
}

а не просто вызвать его из onCreateView()в BottomSheetDialogFragmentреализации


0

У меня такая же проблема, BottomSheetDialogFragmentи я применяю многие решения с использованием behaviorof, dialogно ни одно из них не решает мою проблему, и затем я решил ее, но установил setCancelable(false);во время инициализации dialog.

DialogEndRide dialogCompleteRide = new DialogEndRide();
dialogCompleteRide.setCancelable(false);
dialogCompleteRide.show(getChildFragmentManager(), "");

Это отключит жест, BottomSheetDialogFragmentи вы можете dialogпрограммно закрыть его с помощью dismiss();функции.


-1

У меня была такая же проблема, я решил ее с помощью кода. Это не позволит пользователю перетаскивать нижний лист. вам нужно обрабатывать состояние программно.

 mBottomSheetBehavior.isDraggable = false

-2

Просто используйте: bottomSheet.dismissOnDraggingDownSheet = false

Скопировано с сайта Material.io:

let viewController: ViewController = ViewController() let bottomSheet: MDCBottomSheetController = MDCBottomSheetController(contentViewController: viewController)

// **** Эта строка предотвращает удаление путем перетаскивания bottomSheet ****

bottomSheet.dismissOnDraggingDownSheet = false

present(bottomSheet, animated: true, completion: nil)


здесь для iOS, а не для Android
Back Packer
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.