Alt Sayfada Kullanıcı sürüklemesini devre dışı bırakma


101

Kullanıcı sürüklemeyi devre dışı bırakmaya çalışıyorum BottomSheet. Devre dışı bırakmak istememin iki nedeni var. 1. ListViewAşağıya doğru kaydırmayı engelliyor , 2. Kullanıcıların sürükleyerek değil, üzerinde bir düğme ile kapatmasını istemiyorum BottomSheetView. Bu benim yaptığım şey

 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>


Lütfen cevabımı kontrol edin. Kabul edilen cevaptan daha alakalı olduğunu fark ettim
Vitalii Obideiko

Yanıtlar:


94

Artık alakalı olmayabilir, ancak burada bırakacağım:

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

Ve xml dosyanızda kullanın:

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

Tüm kullanıcı eylemlerini devre dışı bırakır, BottomSheet'i yalnızca programla kontrol etmek istediğinizde kullanılabilir.


2
BottomSheetBehaviour'u devre dışı bırakmak için en iyi cevap budur. Yukarıdaki bir adam da benzer bir çözüm yayınladı, ancak onTouchEvent () gibi diğer olayları geçersiz kılmak için yazmadı . Öte yandan, yanlış
murt

3
Bunu BottomSheetFragment ile nasıl kullanıyorsunuz?
user3144836

7
XML'nizde bu sınıfa özellikle başvurmanız gerekir. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Steve

3
Bazı durumlarda, bu hala işe yaramıyor, eğer alt sayfa parçasında bir listemiz varsa, yine de sürükleniyor
Deepak Joshi

1
@DeepakJoshi, RecyclerView'ı genişletebilir ve 'hasNestedScrollingParent' gibi birkaç yöntemi geçersiz kılabilir, ancak emin değilim
Vitalii Obideiko

77

onStateChangedyönteminde durumu kontrol edin , setBottomSheetCallbackeğer durum ise BottomSheetBehavior.STATE_DRAGGINGdaha sonra BottomSheetBehavior.STATE_EXPANDEDbu şekilde değiştirerek STATE_DRAGGINGkullanıcı tarafından durdurulabilirsiniz . aşağıdaki gibi

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

aşağıdaki gibi alt sayfayı kapatmak için düğmesini kullanın

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

kullanma setPeekHeightya daapp:behavior_peekHeight

yukarıdaki yolla hedefine ulaşabilirsin


1
İyi numara. Bunu fark etmedim. Teşekkürler. Ayrıca bu konuda yardımcı olabilir misiniz? İlk başta genişlemesini söylediğimde, şeffaftır ve arkadaki görünümü görebiliyorum, ancak onu görünür hale getirmeden önce SheetView'daki EditText'e dokunana kadar etkileşim kuramıyorum.
Tonespy

Ben-den BottomSheet View match_parentve benim de o kadar getirmek için deneyin ne zaman ActivityI o kadar slaytlar, ama ben dokunun kadar görünmez değil fark EditTextgetirir ki o Keyboardve yapmak BottomSheet Viewgörünür
Tonespy

1
Bunu denedim ama eyaletler sona erdi STATE_SETTLING. Alt sayfayı açıp kapamak için butonum var, GİZLİ ise genişletiyorum. GENİŞLETİLMİŞ ise, onu gizlerim. SETTLING'de sıkıştığı için alt sayfayı sürükledikten sonra düğmem çalışmıyor. Bununla ilgili bir fikrin var mı?
Gökhan Arık

3
Bu çözüm güvenilmezdir; Gökhan'ın dediği gibi alt sayfa kötü bir duruma geçiyor ... ve bu kötü durumda olduğunda, alt sayfaya yeni bir parça yüklemek gibi çağrılar tamamen boşa çıkacak.
Ray W

8
Alt sayfanın içinde iç içe geçmiş bir görüntüye sahipseniz işe yaramayacaktır
Rishabh Chandel

33

Pekala, kabul edilen cevap benim için işe yaramadı. Bununla birlikte, Виталий Обидейко'nın cevabı benim nihai çözümüme ilham verdi.

İlk olarak, aşağıdaki özel BottomSheetBehavior'ı yarattım. Dokunmayı içeren tüm yöntemleri geçersiz kılar ve kilitliyse yanlış döndürür (veya hiçbir şey yapmaz). Aksi takdirde, normal bir BottomSheetBehavior gibi davranır. Bu, kullanıcının aşağı sürükleme yeteneğini devre dışı bırakır ve koddaki durumu değiştirmeyi etkilemez.

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;

    }
}

İşte nasıl kullanılacağına dair bir örnek. Benim durumumda, buna ihtiyacım vardı, böylece Alt Sayfa genişletildiğinde kilitlendi.

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>

Ana SayfaActivity.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) {}
    });
}

Umarım bu, kafa karışıklığını gidermeye yardımcı olur!


1
Güzel, en iyi yanıt, olayların kaçırılmasına neden olan bu durumları geçici olarak çözmekten kaçınabilmemizdir. Teşekkür ederim.
Tấn Nguyenên

@James - Güzel cevap ama şimdiPeekHeight () ayarlayamıyorum. Herhangi bir fikir?
Adarsh ​​Yadav

Bunu denedim. benim için çalışıyor. kıçımı kurtardığın için teşekkürler kardeşim
Sup.Ia

1
Bugün itibariyle güncellenmemiş olsa da bu iyi bir çözümdür. OnNestedPreScroll ve diğer bazı yöntemler kullanımdan kaldırılmıştır. Bu yöntemleri güncellemeniz gerekiyor ve gayet iyi çalışıyor.
Ajay

4
Merhaba, BottomSheetDialogFragment üzerinde çalışmıyor, yine de alt sayfayı sürükleyebilirim
florian-do

23

Kullanıcı sürüklemesini dinamik olarak devre dışı bırakmanın bu kullanım durumunu ele almak için bir geçici çözüm yazmayı bıraktım, bu sayede BottomSheetBehavior onInterceptTouchEvent'i geçersiz kılmak ve özel bir bayrak (bu durumda mAllowUserDragging) false olarak ayarlandığında onu yok saymak için alt sınıflara alınır:

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

Ve xml düzeninizde:

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

Şimdiye kadar, bu, talep üzerine kullanıcının Alt Sayfada sürüklemesini devre dışı bırakmak için en tutarlı şekilde davranan çözümdür.

OnStateChanged geri aramasında başka bir setState çağrısını ateşlemeye dayanan diğer tüm çözümler, BottomSheet'in kötü bir duruma geçmesine veya önemli UX sorunlarına neden oldu (setState çağrısının bir Runnable'a gönderilmesi durumunda).

Umarım bu birine yardımcı olur :)

Ray


4
Oldukça temiz
Odys

3
@BeeingJk FrameLayout yerine, NestedScrollView kullanın ve ayarlayınbottomSheetFragContainer.setNestedScrollingEnabled(false);
Afzal N

1
ÇÖZÜLDÜ: CoordinatorLayout.Behavior davranışı
LOG_TAG

4
Bu benim için uyanmıyor! Not:
Alt sayfada

6
Başlatma sırasında bunu nasıl yaparsınız? Bu bana uyarı veriyor WABottomSheetBehavior <View> behaviour = (WABottomSheetBehavior) BottomSheetBehavior.from (sheetView);
Leo Droidcoder

9

Geç cevap, ama benim için işe yarayan şey buydu, başkalarının önerdiklerinden biraz farklı.

cancelableÖzelliği false olarak ayarlamayı deneyebilirsiniz , yani

setCancelable(false);

ve ardından setupDialogyöntemde diyaloğu kapatmak istediğiniz olayları manuel olarak işleyin .

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

Bu, diğer çözümlere biraz takılıp kaldığım diyalog parçasının içindeki ListView ile çalışıyor.


Güzel ve özlü çözüm. Bunu duyan kimse için, (muhtemelen) için ek kontroller isteyeceksiniz event.isCanceled()ve event.getAction() == MotionEvent.ACTION_UPiletişim kutusu kapatıldıktan önce - bu işten ateş gelen yanlış tıklama önleyecektir.
Eric Bachhuber

Bunun için teşekkürler. Sürüklemeyi devre dışı bırakmak için en basit çözüm budur.
AVJ

7

Kabul edilen cevap, kullandığım ilk test cihazında çalışmıyor. Ve geri tepme pürüzsüz değil. Durumun STATE_EXPANDED olarak ayarlanması, yalnızca kullanıcı sürüklemeyi bıraktıktan sonra daha iyi görünüyor. Aşağıdaki benim versiyonum:

    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
İstediğin bu değilse, koşabilir bir yere fırlatma problemini sana anlatayım. Bunu bir düğmeyle kapatamazsınız çünkü kapatmak için sürüklemesi gerekir. Ve her zaman sürüklemeye tepki verir, sadece kullanıcının sürükleyerek sürüklemesini engeller
Tonespy

7

Bu kodu BottomSheetBehavior nesnesine ekleyin . Sürükleme devre dışı bırakılacaktır. Benim için iyi çalışıyor.

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
Bu, kaydırmayı devre dışı bırakmaz. Alt tabakayı tamamen çökertir.
Adam Hurwitz

7

Beklenen davranış:

  • BottomSheet aşağı sürüklendiğinde kapanmıyor
  • BottomSheet, diyalog penceresinin dışından dokunulduğunda kapanır

Kod:

class MyBottomSheet : BottomSheetDialogFragment () {

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

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

 }

Bazı nedenlerden dolayı, dışarıya dokunan diyaloğu
kapatamıyorum

5

BottomSheet'i kilitlemek ve kullanıcının dışarı kaydırmasını önlemek için bunu yaptım

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

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

Benim için oldukça iyi çalışıyor.


Bu çözüm cazipti, ancak tuhaf bir şekilde alt sayfanın alt taraf yerine ekranın üstünden görünmesine neden oluyor! Ancak normal şekilde kaybolur. Bu çok Star Trek.
Tunga

Bir görüş değişikliği yapmam ve bunun yerine kullanmam gerekiyordu BottomSheetBehavior.STATE_HIDDEN. Böyle bir durumda da aramamalısınız setPeekHeight(). Bu, buradaki diğer çözümlerden çok daha az karmaşıktır.
HolySamosa

5

Sürüklemeyi kilitlemenin kolay yolu, PeekHeight ayarıyla görünüm yüksekliğiyle aynıdır. Örneğin:

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

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

BottomSheet'i bu şekilde sürüklemeyi devre dışı bırakabilirsiniz.

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), umarım bu yanıt sorununuzu çözebilir.

Düzenlendi) Kitaplık güncellendi! yeni kütüphane sürümünü kullanabilirsiniz

implementation 'com.google.android.material:material:1.2.1'

Örnekler aynı, iyi şanslar ve iyi kod


1
Bu alfa versiyonu çılgınca davranıyor. Tavsiye etmiyorum :(
Adam Styrc

4

BottomSheetDialogFragment ile bir örnek. Mükemmel çalışıyor.

Düzenleme 2020/09/04: amorti Değiştirilen setBottomSheetCallback()ileaddBottomSheetCallback()

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

Alt sayfa devre dışı bırakıldığında tüm olayları engellemenize gerek yoktur. Yalnızca ACTION_MOVE olayını engelleyebilirsiniz. Bu nedenle, bunun gibi özel alt sayfa davranışını kullanın

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

Bu dersi nasıl kullanıyorsunuz? IllegalArgumentException alıyorum: Görünüm BottomSheetBehavior ile ilişkili değil
user3144836

3

İşte Kotlin'deki en iyi çözümün çalışan bir versiyonu:

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

Sonra ne zaman kullanmak istersen:

val bottomSheetBehavior by lazy {
    CustomBottomSheetBehavior.from(bottom_sheet_main)
}

bottom_sheet_mainKullanarak gerçek görünümüdür Kotlin Android Uzantıları .


3

bottomSheet onClickListener null olarak ayarlayın.

bottomSheet.setOnClickListener(null);

bu satır, yalnızca bottomSheet ile ilgili tüm eylemleri devre dışı bırakır ve iç görünümü etkilemez.


1
Bu, Alt Sayfa kapanmaya çalışırken beklenmedik bir animasyona neden olur.
Adam Hurwitz

2

Harika bir çözüm buldum. İlk sorun, bottomSheet'in GİZLİ durumuna geçtikten sonra bottomSheetDialog.show () 'da görünmemesiydi. Ancak, iletişim kutusunu show () yönteminde görünür hale getirmek ve ayrıca kullanıcının alt sayfa gibi hissetmesi için aşağı kaydırmasını sağlamak istedim. Aşağıda yaptığım şey ...

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

    }
};

bu mükemmel bir cevap
Vivek Kumar Srivastava

2
  1. BottomSheetDialogProjenize kopyalayın ve olarak yeniden adlandırınMyBottomSheetDialog
  2. eklemek getBottomSheetBehavioriçinMyBottomSheetDialog
  3. kullanmak MyBottomSheetDialogyerineBottomSheetDialog
  4. bottomSheetBehavior.setBottomSheetCallback

bunun gibi kod

public class MyBottomSheetDialog extends AppCompatDialog {

    // some code

    public BottomSheetBehavior<FrameLayout> getBottomSheetBehavior() {
        return mBehavior;
    }

    // more code

kodunuzda

    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

Bu temelde en üstteki doğru cevabın kotlin versiyonu:

    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
}

Bu dersi nasıl kullanıyorsunuz? Bir IllegalArgumentException alıyorum: Görünüm BottomSheetBehavior ile ilişkili değil
user3144836

1
app: layout_behavior = "UserLockBottomSheetBehavior"> xml'de ve sonra kodda aşağıdakileri yaparsınız. // alt sayfa görünümünü al LinearLayout llBottomSheet = (LinearLayout) findViewById (R.id.bottom_sheet); // alt sayfa davranışını başlat BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from (llBottomSheet);
Jamal

2

Bunu dene.

1) Alt sayfayı oluşturun ve java sınıfınızdaki değişkeni şu şekilde bildirin:

private BottomSheetBehavior sheetBehavior;

2) sheetBehavior = BottomSheetBehavior.from(bottomSheet);

3) Alt sayfadaki geri arama işlevine aşağıdaki satırları ekleyin.

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

            }
        });

2

İle 'com.google.android.material:material:1.2.0-alpha06'

İle harika çalışıyor NestedScrollViewVeRecyclerView

Örnek kod:

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

2

Sadece kullan

bottomSheetBehavior.isDraggable = false

veya

bottomSheetBehavior.setDraggable(false)

1

İlk başta, cevap vermeye çalışan hepinize teşekkür etmek istiyorum. Ben sadece bu sorunu istediğim gibi çözerek bu cevabı yazıyorum. Buradan yardım alarak bunu adım adım nasıl yaptığımı anlatacağım.

Görselleştirme: butonuna bastıktan sonra Show BottomSheetgöreceğiniz ikinci ekranı . Şimdi BottomSheet'in sürüklemek için kilitlendiğini göreceksiniz . Ama tıklarsanız Ülke Listesine BottomSheet gizler. Bu açıklama buydu şimdi Kodu inceleyelim.

  • İlk olarak, tasarım destek kitaplığını build.gradle dosyanıza ekleyin :

    uygulama 'com.android.support:design:28.0.0'

  • UserLockBottomSheetBehavior.java : Kredi: James Davis (Teşekkürler Adam)

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

İlk Ekran İkinci ekran


1

Kabul edilen cevabın çözümü çoğunlukla benim için işe yaradı, ancak bir sorunla: alt sayfa görünümünün arkasındaki görünümler, alt sayfanın alanında alt sayfanın alanında dokunma olayı meydana geliyorsa dokunma olaylarına tepki vermeye başladı. Diğer bir deyişle, aşağıdaki resimde de görebileceğiniz gibi, kullanıcı parmağını alt sayfanın içine kaydırdığında, harita üzerine tepki vermeye başlıyor.

alt sayfa dokunma alanı

Sorunu düzeltmek için , alt sayfa görünümünü onInterceptTouchEventayarlayarak yöntemi değiştirdim touchListener(kodun geri kalanı, kabul edilen çözümde olduğu gibi kalır).

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

1

Ben de aynı sorunu var BottomSheetDialogFragmentve kullanan birçok çözümler uygulamak behaviorait dialogama bunların hiçbiri sorunumu çözmek ve daha sonra bunu ancak ayarı çözüldü setCancelable(false);başlatılması sırasında dialog.

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

Bu, hareketi devre dışı bırakır BottomSheetDialogFragmentve işlevi dialogkullanarak programlı olarak kapatabilirsiniz dismiss();.


1

Sadece işe yarayacağı aşağıdaki satırı ekleyin:

 behavior.setDraggable(false);

0

peakHeightDeğeri ayarlamak benim için çalıştı.

Tepe yüksekliğini eğer genişletilmişse alt tabakanın yüksekliği olarak ayarlıyorum.

    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
Beklenmedik animasyonlara neden olabileceğinden bu ideal değildir.
Adam Hurwitz

Benim durumumda. Herhangi bir animasyon sorununa neden olmadı. Kart genişletildikten sonra hareket etmez. İdeal değil ama beklendiği gibi çalıştı!
pz64_

İlginç, durum bu olabilir. Alt Sayfamın beklenmedik şekilde kapanmasıyla ilgili sorunu, Alt Sayfa açıkken CollapsingToolbarLayout Araç Çubuğunu Görünmez veya Gitti olarak ayarlayarak çözdüm. Altında olmasına rağmen Araç Çubuğu ile ilgili bir dokunma etkileşimi Alt Sayfanın beklenmedik şekilde kapanmasına neden oluyordu. Sorun şimdi çözüldü.
Adam Hurwitz

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

bu google'daki ilk sonuç, bu yüzden basit çözümü buraya koymanın tek adil olduğuna inanıyorum:

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

ve sadece onu aramak daha onCreateView()içinde BottomSheetDialogFragmentuygulanması


-1

Aynı sorunu yaşadım, bunu kodla çözdüm. Kullanıcının alt sayfayı sürüklemesine izin vermez. durumu programlı bir şekilde işlemeniz gerekir.

 mBottomSheetBehavior.isDraggable = false

-2

Basitçe şunu kullanın: bottomSheet.dismissOnDraggingDownSheet = false

Material.io web sitesinden kopyalandı:

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

// **** Bu satır, alt sayfa **** sürükleyerek kapatmayı önler

bottomSheet.dismissOnDraggingDownSheet = false

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


burada iOS için Android için değil
Back Packer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.