사용자가 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;
                    }
                }
            });
        }
    });

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>


내 대답을 확인하십시오. 나는 그것이 허용 대답보다 더 많은 관련이 있음을 통지해야
Vitalii Obideiko

답변:


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"
Steve

3
경우에 따라이 여전히 우리가 바닥 시트 조각의 목록이있는 경우, 그것은 여전히 드래그 작동하지 않습니다
디팍 조시

1
@DeepakJoshi 아마도 u는 RecyclerView를 확장하고 'hasNestedScrollingParent'와 같은 몇 가지 메서드를 재정의 할 수 있지만 확실하지 않습니다
Vitalii Obideiko

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
좋은 속임수. 그것을 알아 차리지 못했습니다. 감사. 또한 이것으로 도와 줄 수 있습니까? 처음에 확장하라고하면 투명하고 뒤에있는보기를 볼 수 있지만 표시하기 전에 SheetView에서 EditText를 탭할 때까지 상호 작용할 수 없습니다.
Tonespy

내가 만들어 BottomSheet View match_parent내가 내에서 그것을 불러올 때마다 Activity나는 그것이 위로 슬라이드하지만 난을 누릅니다 때까지하지 보이지 않는 눈치 EditText납니다있는 그 안에 Keyboard는 AND하게 BottomSheet View
Tonespy

1
나는 이것을 시도했지만 상태는 STATE_SETTLING. 하단 시트를 열고 닫는 버튼이 있습니다. HIDDEN이면 확장합니다. EXPANDED이면 숨 깁니다. SETTLING에서 멈춰서 하단 시트를 드래그하면 버튼이 작동하지 않습니다. 그것에 대한 아이디어가 있습니까?
고칸 아리

3
이 솔루션은 신뢰할 수 없습니다. Gokhan이 말했듯이 하단 시트는 불량 상태가됩니다. 그리고 불량 상태에있을 때 하단 시트에 새 조각을로드하는 것과 같은 호출은 단순히 비워집니다.
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
좋습니다. 이벤트 누락으로 이어지는 이러한 상태의 해결 방법을 피할 수있는 것이 최선의 답변입니다. 감사합니다.
Tấn Nguyên

@James-좋은 대답이지만 지금은 setPeekHeight ()를 사용할 수 없습니다. 어떤 생각?
Adarsh ​​Yadav

나는 이것을 시도했다. 그것은 나를 위해 작동합니다. 내 엉덩이 절약에 대한 감사의 형제
Sup.Ia

1
오늘 현재 업데이트되지는 않았지만 이것은 좋은 해결 방법입니다. OnNestedPreScroll 및 기타 특정 메서드는 더 이상 사용되지 않습니다. 이러한 방법을 업데이트해야하며 제대로 작동합니다.
Ajay

4
안녕하세요, BottomSheetDialogFragment에서는 작동하지 않습니다. 여전히 bottomsheet를 드래그 할 수 있습니다
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" />

지금까지 이것은 요청시 사용자가 하단 시트에서 드래그하는 것을 비활성화하는 가장 일관되게 작동하는 솔루션입니다.

onStateChanged 콜백에서 다른 setState 호출을 실행하는 데 의존 한 다른 모든 솔루션은 BottomSheet가 잘못된 상태가되거나 심각한 UX 문제를 유발합니다 (Runnable에 setState 호출을 게시하는 경우).

이것이 누군가에게 도움이되기를 바랍니다 :)

레이


4
그것은 꽤 깔끔합니다
Odys

3
대신 FrameLayout이 사용 NestedScrollView의 @BeeingJk 및 세트bottomSheetFragContainer.setNestedScrollingEnabled(false);
AfzalivE

1
해결 : 콜백 설정 CoordinatorLayout.Behavior behavior = layoutParams.getBehavior (); if (동작! = null && BottomSheetBehavior의 동작 인스턴스) {((BottomSheetBehavior) 동작) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG

4
이것은 나를 위해 일하지 않는다! PS : 나는 bottomsheet에서 스크롤 텍스트가
Thorvald의 Olavsen

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대화 상자를 기각하기 전에를 -이 해고를 발사에서 잘못 클릭을 방지 할 수 있습니다.
Eric Bachhuber

감사합니다. 이것은 드래그를 비활성화하는 가장 간단한 솔루션입니다.
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
이것은 스 와이프를 비활성화하지 않습니다. 하단 시트를 완전히 접습니다.
Adam Hurwitz

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

 }

어떤 이유로 외부를 터치하는 대화 상자를 닫을 수 없지만 드래그를 비활성화하도록 작동합니다
Gastón Saillén

5

BottomSheet를 잠그고 사용자가 스 와이프하는 것을 방지하기 위해 이것이 제가 한 작업입니다.

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

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

그것은 나를 위해 아주 잘 작동합니다.


이 솔루션은 매력적 이었지만 기이하게도 하단 시트가 화면 하단이 아닌 상단에 나타납니다! 그러나 정상적인 방식으로 사라집니다. 바로 스타 트렉입니다.
Tunga

시력을 수정하고 대신 BottomSheetBehavior.STATE_HIDDEN. 이 경우에도을 (를) 호출해서는 안됩니다 setPeekHeight(). 이것은 다른 솔루션보다 훨씬 덜 복잡합니다.
HolySamosa 2017

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가 포함 된 샘플입니다. 완벽하게 작동합니다.

편집 2020년 9월 4일 : 감가 상각 대체 됨 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사용하여 실제이다 코 틀린 안드로이드 확장 .


3

bottomSheet onClickListener를 null로 설정하십시오.

bottomSheet.setOnClickListener(null);

이 행은 bottomSheet에 대한 모든 조치를 비활성화하고 내부보기에는 영향을주지 않습니다.


1
이로 인해 하단 시트를 닫으려고 할 때 예기치 않은 애니메이션이 발생합니다.
Adam Hurwitz 2018

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
이 알파 버전은 미친 듯이 작동합니다. 나는 추천하지 않는다 :(
Adam Styrc

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. 추가 getBottomSheetBehaviorMyBottomSheetDialog
  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);
Jamal

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 : : James Davis (감사합니다.)

public class UserLockBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {

    public UserLockBottomSheetBehavior() {
        super();
    }

    public UserLockBottomSheetBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
        return false;
    }

    @Override
    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
        return false;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) {
        return false;
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) {
    }

    @Override
    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
    }

    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) {
        return false;
    }

}
  • bottomsheet.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'

잘 작동합니다. NestedScrollViewRecyclerView

예제 코드 :

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

0

peakHeight값을 조정하는 것이 저에게 효과적이었습니다.

확장 된 경우 하단 시트의 높이로 peakheight를 설정합니다.

    private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
    override fun onSlide(bottomSheet: View, slideOffset: Float) {

    }

    override fun onStateChanged(bottomSheet: View, newState: Int) {
        if (newState == BottomSheetBehavior.STATE_EXPANDED)
            bottomSheetBehavior.peekHeight = bottomSheet.height
    }
}

1
예상치 못한 애니메이션이 발생할 수 있으므로 이상적이지 않습니다.
Adam Hurwitz 2018

나의 경우에는. 애니메이션 문제가 전혀 발생하지 않았습니다. 카드가 확장 된 후에는 움직이지 않습니다. 이상적이지는 않지만 예상대로 작동했습니다!
pz64_

흥미롭게도 그럴 수 있습니다. 하단 시트가 열려있을 때 CollapsingToolbarLayout의 도구 모음을 보이지 않거나 사라짐으로 설정하여 하단 시트가 예기치 않게 닫히는 문제를 해결했습니다. 툴바가 아래에 있었지만 관련 터치 상호 작용으로 인해 하단 시트가 예기치 않게 닫혔습니다. 이제 문제가 해결되었습니다.
Adam Hurwitz 2018

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닫을 수 있습니다 .dialogdismiss();


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


Android가 아닌 iOS 용입니다
Back Packer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.