Вимкнення перетягування користувачів на BottomSheet


99

Я намагаюся вимкнути перетягування користувачів 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>


Будь ласка, перевірте мою відповідь. Я помітив, що це більше доречно, ніж прийнята відповідь
Віталій Обідейко

Відповіді:


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 () . З іншого боку, ви можете покращити свою відповідь, якщо ви поставите прапор замість помилкового
murt

3
Як ви використовуєте це за допомогою BottomSheetFragment?
user3144836

7
Вам потрібно спеціально звернутися до цього класу у своєму XML. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Стів

3
У деяких випадках це все одно не працює, якщо у нас є список у нижньому фрагменті аркуша, він все одно тягнеться
Діпак Джоші

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

74

перевірити стан у 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

наведеним вище способом ви можете досягти своєї мети


1
Хороший фокус. Не помітив цього. Дякую. А також, чи можете ви допомогти в цьому. Коли я спочатку кажу, щоб він розширився, він прозорий, і я бачу вид ззаду, але я не можу взаємодіяти, поки не натисну на EditText у SheetView, перш ніж зробити його видимим.
Тонеспі,

Я робив свій, BottomSheet View match_parentі всякий раз, коли я намагався підняти його в своєму, Activityя помітив, що він ковзає вгору, але це не видно, доки я не EditTextKeyboardBottomSheet View
натисну

1
Я спробував це, але штати опиняються в STATE_SETTLING. У мене є кнопка для відкриття та закриття нижнього аркуша, якщо він прихований, я розширюю його. Якщо його РОЗШИРЕНО, я приховую. Оскільки вона застрягає в SETTLING, моя кнопка не працює після перетягування нижнього аркуша. Будь-яка ідея щодо цього?
Гохан Арік

3
Це рішення ненадійне; нижній аркуш потрапляє в поганий стан, як сказав Гохан ... і коли він у такому поганому стані, дзвінки, такі як завантаження нового фрагмента в нижній аркуш, просто зникають.
Ray W

7
Це не спрацює, якщо у вас є вкладений перегляд прокрутки всередині нижнього аркуша
Rishabh Chandel

32

Гаразд, тому прийнята відповідь для мене не спрацювала. Однак відповідь Віталія Обидейко надихнула мене на остаточне рішення.

Спочатку я створив наступний власний BottomSheetBehavior. Він замінює всі методи, що стосуються дотику, і повертає помилкові (або нічого не робив), якщо заблоковано. В іншому випадку він діє як звичайний 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
Приємно, це найкраща відповідь, що ми можемо уникнути обходу цих станів, які призводять до пропуску подій. Дякую.
Tấn Nguyên

@James - Приємна відповідь, але зараз я не можу встановити SetPeekHeight (). Будь-яка ідея?
Adarsh ​​Yadav

Я спробував це. це працює для мене. спасибі брате, що врятував мою дупу
Sup. Ia

1
Це хороший обхідний шлях, хоча на сьогодні він не оновлюється. OnNestedPreScroll та деякі інші методи застаріли. Потрібно оновити ці методи, і це прекрасно працює.
Аджай

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
Це досить акуратно
Одіс

3
@BeeingJk Замість FrameLayout використовуйте NestedScrollView та встановітьbottomSheetFragContainer.setNestedScrollingEnabled(false);
AfzalivE

1
ВИРІШЕНО: встановивши зворотний виклик CoordinatorLayout.Behavior поведінка = layoutParams.getBehavior (); якщо (поведінка! = нуль && екземпляр поведінки BottomSheetBehavior) {((BottomSheetBehavior) поведінка) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG

3
Це не прокидається для мене! PS: У мене є прокручуваний текст в нижньому аркуші
Торвальд Олавсен,

6
Як ви робите це під час ініціалізації? Це дає мені застереження WABottomSheetBehavior <View> behaviour = (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

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
Дозвольте мені розповісти вам про проблему з киданням її в бігу, якщо це не те, що ви хочете. Ви не можете відхилити його кнопкою, тому що для її відхилення її потрібно перетягнути. І воно завжди реагуватиме на перетягування, лише щоб це заважало користувачеві перетягувати, щоб звільнити
Tonespy

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

Для мене це працює досить добре.


Це рішення було привабливим, але химерно спричиняє появу нижнього аркуша зверху екрана замість нижнього! Однак це зникає звичайним шляхом. Це дуже Зоряний шлях.
Тунга

Мені потрібно було змінити приціл і замість цього використовувати BottomSheetBehavior.STATE_HIDDEN. У такому випадку ви також не повинні телефонувати setPeekHeight(). Це набагато менш складно, ніж інші рішення тут.
HolySamosa

4

Простий спосіб заблокувати перетягування 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фактичний вигляд за допомогою розширень Android від Kotlin .


3

встановіть bottomSheet onClickListener на нуль.

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

(kotlin), сподіваюся, ця відповідь може вирішити вашу проблему.


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

Це в основному версія kotlin правильної відповіді вгорі:

    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);
Джамал

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 просто заблоковано для перетягування . Але якщо натиснути на Список країн, нижній аркуш сховається. Це був опис, тепер давайте копатимемось у Кодексі.

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

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

  • UserLockBottomSheetBehavior.java : Кредит: Джеймс Девіс (Thanks Man)

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

}
  • нижній аркуш.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
    }

1

З 'com.google.android.material:material:1.2.0-alpha06'

Чудово працює з NestedScrollViewтаRecyclerView

Приклад коду:

    LinearLayout contentLayout = findViewById(R.id.contentLayout);
    sheetBehavior = BottomSheetBehavior.from(contentLayout);
    sheetBehavior.setDraggable(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_

Цікаво, що це може бути так. Я вирішив проблему із несподіваним закриттям нижнього аркуша, встановивши панель інструментів 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і застосовувати безліч рішень , використовуючи behaviorз , 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.