Desativando o usuário arrastando em BottomSheet

99

Estou tentando desativar o recurso de arrastar do usuário BottomSheet. O motivo pelo qual desejo desativar são duas coisas. 1. Está impedindo que o ListViewrole para baixo, 2. Não quero que os usuários descartem arrastando, mas com um botão no BottomSheetView. Isso é o que eu fiz

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

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

Abubakar Oladeji
fonte
Por favor, verifique minha resposta. Percebi que é mais relevante do que a resposta aceita
Vitalii Obideiko

Respostas:

92

Agora pode não ser mais relevante, mas vou deixá-lo aqui:

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

E use-o em seu arquivo xml:

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

Ele desativa todas as ações do usuário e pode ser usado quando você deseja controlar BottomSheet apenas de forma programática.

Vitalii Obideiko
fonte
2
Esta é a melhor resposta para desativar BottomSheetBehaviour. Um homem acima também postou uma solução semelhante, mas ele não escreveu para substituir outros eventos como onTouchEvent () . Por outro lado, você pode melhorar sua resposta se colocar uma bandeira em vez de falso
murt
3
Como você usa isso com um BottomSheetFragment?
user3144836
7
Você precisa se referir especificamente a esta classe em seu XML. app: layout_behavior = "com.my.package.UserLockBottomSheetBehavior"
Steve
3
Em alguns casos, isso ainda não funciona, se tivermos uma lista no fragmento da folha inferior, ainda arrasta
Deepak Joshi
1
@DeepakJoshi talvez você possa estender o RecyclerView e substituir alguns métodos como 'hasNestedScrollingParent', mas não tenho certeza
Vitalii Obideiko
74

verifique o estado no onStateChangedmétodo de setBottomSheetCallbackse o estado é, BottomSheetBehavior.STATE_DRAGGINGentão altere-o para BottomSheetBehavior.STATE_EXPANDEDque você possa interromper STATE_DRAGGINGpelo usuário. como abaixo

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

use o botão para abrir e fechar a folha inferior como abaixo

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

não use setPeekHeightouapp:behavior_peekHeight

pelo caminho acima você pode alcançar seu objetivo

Dhaval Parmar
fonte
1
Belo truque. Não percebi isso. Obrigado. E também, você pode ajudar com isso. Quando digo para expandir a princípio, é transparente e posso ver a visão atrás, mas não posso interagir até tocar em EditText em SheetView antes de torná-lo visível.
Abubakar Oladeji
Eu fiz o meu BottomSheet View match_parente sempre que tento trazê-lo para o meu Activity, notei que ele desliza para cima, mas não é visível até que eu toque EditTextnele, o que traz o Keyboarde torna o BottomSheet Viewvisível
Abubakar Oladeji
1
Eu tentei isso, mas os estados acabam em STATE_SETTLING. Eu tenho um botão para abrir e fechar a folha inferior, se estiver OCULTO, eu o expande. Se estiver EXPANDIDO, eu oculto. Como ele fica preso em SETTLING, meu botão não funciona depois de arrastar a folha inferior. Alguma ideia disso?
Gokhan Arik
3
Esta solução não é confiável; a folha inferior fica em um estado ruim, como Gokhan disse ... e quando nesse estado ruim, reclamações como carregar um novo fragmento na folha inferior simplesmente ficam em branco.
Ray W
7
Não funcionará se você tiver aninhado uma visualização da tela dentro da folha inferior
Rishabh Chandel
32

Tudo bem, então a resposta aceita não funcionou para mim. No entanto, a resposta de Виталий Обидейко inspirou minha solução final.

Primeiro, criei o seguinte BottomSheetBehavior personalizado. Ele substitui todos os métodos que envolvem toque e retorna falso (ou não fez nada) se estiver bloqueado. Caso contrário, ele atua como um BottomSheetBehavior normal. Isso desativa a capacidade do usuário de arrastar para baixo e não afeta a alteração do estado no código.

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;

    }
}

Aqui está um exemplo de como usá-lo. No meu caso, eu precisava para que a Folha Inferior travasse quando expandida.

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

Espero que isso ajude a esclarecer grande parte da confusão!

James Davis
fonte
1
Boa, é a melhor resposta que podemos evitar soluções alternativas para esses estados que levam a eventos perdidos. Obrigado.
Tấn Nguyên
@James - Boa resposta, mas agora não consigo setPeekHeight (). Qualquer ideia?
Adarsh ​​Yadav
Eu tentei isso. funciona para mim. obrigado mano por salvar minha bunda
Sup.Ia
1
Esta é uma boa solução alternativa, embora não seja atualizada até hoje. O OnNestedPreScroll e alguns outros métodos foram descontinuados. Precisa atualizar esses métodos e funciona muito bem.
Ajay de
4
Olá, não funciona em BottomSheetDialogFragment, ainda consigo arrastar a folha de fundo
florian-do
23

Acabei escrevendo uma solução alternativa para abordar este caso de uso de desabilitar dinamicamente o arrastar do usuário, em que BottomSheetBehavior é uma subclasse para substituir onInterceptTouchEvent e para ignorá-lo quando um sinalizador personalizado (neste caso mAllowUserDragging) é definido como falso:

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

E em seu layout 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" />

Até agora, esta é a solução de comportamento mais consistente para desativar o usuário arrastar na folha inferior sob demanda.

Todas as outras soluções que dependiam do disparo de outra chamada setState no retorno de chamada onStateChanged resultaram em um estado ruim do BottomSheet ou causaram problemas significativos de UX (no caso de postar a chamada setState em um Runnable).

Espero que isso ajude alguém :)

Raio

Ray W
fonte
4
Isso é muito legal
Odys
3
@BeeingJk Em vez de FrameLayout, use NestedScrollView e definabottomSheetFragContainer.setNestedScrollingEnabled(false);
AfzalivE
1
RESOLVIDO: definindo o retorno de chamada CoordinatorLayout.Behavior behavior = layoutParams.getBehavior (); if (behavior! = null && behavior instanceof BottomSheetBehavior) {((BottomSheetBehavior) behavior) .setBottomSheetCallback (mBottomSheetBehaviorCallback); }
LOG_TAG
3
Isso não funciona para mim! PS: Eu tenho um texto rolável na folha inferior
Thorvald Olavsen
6
Como você o converte durante a inicialização? Isso me dá um aviso WABottomSheetBehavior <View> behavior = (WABottomSheetBehavior) BottomSheetBehavior.from (sheetView);
Leo Droidcoder
8

Resposta tardia, mas, isso é o que funcionou para mim, o que é um pouco diferente do que outros sugeriram.

Você pode tentar definir a cancelablepropriedade como falsa, ou seja,

setCancelable(false);

e, em seguida, manipular manualmente os eventos em que deseja descartar o diálogo no setupDialogmétodo.

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

Isso funciona com um ListView dentro do fragmento de diálogo, que era onde eu estava ficando um pouco preso com outras soluções.

Chrisw
fonte
Boa solução concisa. Para quem lê este, você (provavelmente) quer verificações adicionais para event.isCanceled()e event.getAction() == MotionEvent.ACTION_UPantes de descartar o diálogo - isso vai impedir mis-cliques de disparar a demissão.
Eric Bachhuber
Obrigado por isso. Esta é a solução mais simples para desativar o arrasto.
AVJ 01 de
7

A resposta aceita não funciona no primeiro dispositivo de teste que uso. E a recuperação não é suave. Parece melhor definir o estado para STATE_EXPANDED somente depois que o usuário soltar o recurso de arrastar. O seguinte é minha versão:

    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) {
        }
    });
Mingfai
fonte
1
Deixe-me explicar o problema de colocá-lo em um runnable, a menos que seja isso o que você deseja. Você não pode dispensá-lo com um botão, porque ele precisa arrastar para dispensar. E, ele sempre responderá ao arrastar, apenas que evitaria que o usuário arrastasse para dispensar
Abubakar Oladeji
7

Adicione este código ao objeto BottomSheetBehavior . Arrastar será desativado. Funciona bem para mim.

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

      }
});
Kaushik R
fonte
1
Isso não desativa o deslizamento. Ele recolhe a folha inferior completamente.
Adam Hurwitz
7

Comportamento esperado:

  • BottomSheet não fecha ao arrastar para baixo
  • BottomSheet fecha se tocado fora da janela de diálogo

Código:

class MyBottomSheet : BottomSheetDialogFragment () {

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

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

 }
Alif
fonte
Por algum motivo, não consigo fechar o diálogo tocando fora, mas funciona para desativar o arrasto
Gastón Saillén
5

Para bloquear a BottomSheet e evitar que o usuário a deslize, foi o que fiz

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

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

Funciona muito bem para mim.

Diego Ramírez Vásquez
fonte
Esta solução era atraente, mas estranhamente faz com que a folha inferior apareça na parte superior da tela em vez de na parte inferior! No entanto, ele desaparece da maneira normal. É muito Star Trek.
Tunga
Eu precisava fazer uma modificação na visão e, em vez disso, usar BottomSheetBehavior.STATE_HIDDEN. Nesse caso, você também não deve ligar setPeekHeight(). Isso é muito menos complicado do que outras soluções aqui.
HolySamosa
4

A maneira fácil de travar o arrasto é setPeekHeight igual à altura da vista. Por exemplo:

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);
}
Sergei K
fonte
4

Uma amostra com BottomSheetDialogFragment. Funciona perfeitamente.

Editar 09/04/2020: Substituído depreciado setBottomSheetCallback()poraddBottomSheetCallback()

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
    }

    ...
}
Sattar Hummatli
fonte
3

Você não precisa bloquear todos os eventos quando a folha inferior está desativada. Você pode bloquear apenas eventos ACTION_MOVE. É por isso que usar um comportamento personalizado de folha inferior como este

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);
    }
}
Ilya
fonte
Como você usa esta classe? Estou recebendo uma IllegalArgumentException: a visualização não está associada a BottomSheetBehavior
user3144836
3

Esta é uma versão funcional da principal solução em 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
    }
}

Então, sempre que você quiser usar:

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

Esta bottom_sheet_mainé a visualização real usando as extensões Kotlin do Android .

Rodrigo Queiroz
fonte
3

defina bottomSheet onClickListener como null.

bottomSheet.setOnClickListener(null);

esta linha desabilita todas as ações apenas sobre bottomSheet e não tem efeito na visualização interna.

Hashem Jahangiri
fonte
1
Isso causa uma animação inesperada quando a folha inferior está tentando fechar.
Adam Hurwitz
3
implementation 'com.google.android.material:material:1.2.0-alpha05'

você pode desativar o arrastamento da BottomSheet desta forma.

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), espero que esta resposta possa resolver seu problema.

lgb
fonte
1
Essa versão alfa se comporta loucamente. Eu não recomendo :(
Adam Styrc
2

Eu encontrei uma solução incrível. O problema inicial era quando bottomSheet ia para o estado HIDDEN e não aparecia em bottomSheetDialog.show (). Mas eu queria tornar a caixa de diálogo visível no método show () e também permitir que o usuário deslizasse para baixo de forma que parecesse a folha de baixo. Abaixo está o que eu fiz ..

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

    }
};
Deepesh
fonte
esta é uma resposta perfeita
Vivek Kumar Srivastava
2
  1. Copie BottomSheetDialogpara o seu projeto e renomeie paraMyBottomSheetDialog
  2. adicionar getBottomSheetBehavioraMyBottomSheetDialog
  3. use ao MyBottomSheetDialoginvésBottomSheetDialog
  4. bottomSheetBehavior.setBottomSheetCallback

código como este

public class MyBottomSheetDialog extends AppCompatDialog {

    // some code

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

    // more code

no seu código

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

        }
aotian16
fonte
2

Esta é basicamente a versão kotlin da resposta certa no topo:

    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
}
Jamal
fonte
Como você usa esta classe? Estou recebendo uma IllegalArgumentException: a visualização não está associada a BottomSheetBehavior
user3144836
1
app: layout_behavior = "UserLockBottomSheetBehavior"> em xml e, em seguida, no código, faça o seguinte. // obter a visualização da folha inferior LinearLayout llBottomSheet = (LinearLayout) findViewById (R.id.bottom_sheet); // inicia o comportamento da folha inferior BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from (llBottomSheet);
Jamal
1

Experimente isso.

1) Crie a folha de baixo e declare a variável em sua classe java como

private BottomSheetBehavior sheetBehavior;

2) sheetBehavior = BottomSheetBehavior.from(bottomSheet);

3) Na função de retorno de chamada da folha inferior, adicione as seguintes linhas.

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

            }
        });
Surendar D
fonte
1

A princípio, só quero agradecer a todos que tentaram dar uma resposta. Estou apenas escrevendo esta resposta, resolvendo este problema como desejo. Vou descrever como faço isso passo a passo, obtendo ajuda daqui.

Visualização: Após clicar no Botão Show BottomSheetvocê verá a segunda tela . Agora você verá que BottomSheet está bloqueado apenas para arrastar . Mas se você clicar na Lista de Países, a Folha Inferior será ocultada. Essa foi a descrição agora vamos nos aprofundar no Código.

  • Em primeiro lugar, adicione a biblioteca de suporte de design ao seu arquivo build.gradle :

    implementação 'com.android.support:design:28.0.0'

  • UserLockBottomSheetBehavior.java : Crédito: James Davis (obrigado, cara)

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

Primeira Tela Segunda tela

Shahadat Hossain
fonte
1

A solução da resposta aceita funcionou principalmente para mim, mas com um problema: as visualizações, que estão atrás da visualização da folha inferior, começaram a reagir aos eventos de toque, se o evento de toque estiver acontecendo na área da folha inferior, que está livre de visualizações secundárias. Em outras palavras, como você pode ver na imagem abaixo, quando o usuário desliza o dedo dentro da folha inferior, o mapa começa a reagir sobre ela.

área de toque da folha inferior

Para corrigir o problema, modifiquei o onInterceptTouchEventmétodo configurando touchListenera visualização da folha inferior (o resto do código permanece o mesmo da solução aceita).

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

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

Funciona muito bem com NestedScrollVieweRecyclerView

Código de exemplo:

    LinearLayout contentLayout = findViewById(R.id.contentLayout);
    sheetBehavior = BottomSheetBehavior.from(contentLayout);
    sheetBehavior.setDraggable(false);
F.Mysir
fonte
0

Ajustar o peakHeightvalor funcionou para mim.

Eu defino a altura de pico como a altura da folha inferior se ela for expandida.

    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
    }
}
pz64_
fonte
1
Isso não é ideal, pois pode causar animações inesperadas.
Adam Hurwitz
No meu caso. Isso não causou nenhum problema de animações. Ele simplesmente não se move depois que o cartão é expandido. Não é o ideal, mas funcionou como esperado!
pz64_
Interessante, pode ser o caso. Resolvi o problema com minha folha inferior fechando inesperadamente definindo a barra de ferramentas CollapsingToolbarLayout como invisível ou desaparecido quando a folha inferior estava aberta. Uma interação de toque relacionada à barra de ferramentas, embora estivesse embaixo, fazia com que a folha inferior fechasse inesperadamente. O problema está resolvido agora.
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();
SRBhagwat
fonte
0

este é o primeiro resultado no google, então acredito que é justo colocar a solução simples aqui:

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

e do que apenas chamá-lo onCreateView()na BottomSheetDialogFragmentimplementação

Yosef
fonte
0

Eu tenho o mesmo problema BottomSheetDialogFragmente aplico muitas soluções usando o behaviorde dialogmas nenhuma resolveu meu problema e então resolvi mas configurando setCancelable(false);no momento da inicialização de dialog.

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

Isso desativará o gesto BottomSheetDialogFragmente você pode dispensar dialogprogramaticamente usando a dismiss();função.

rana_sadam
fonte
-1

Eu tive o mesmo problema, resolvi isso por código. Não permite que o usuário arraste a folha inferior. você precisa lidar com o estado programaticamente.

 mBottomSheetBehavior.isDraggable = false
Pritesh Vishwakarma
fonte
-2

Basta usar: bottomSheet.dismissOnDraggingDownSheet = false

Copiado do site Material.io:

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

// **** Esta linha evita dispensar arrastando bottomSheet ****

bottomSheet.dismissOnDraggingDownSheet = false

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

Henry Tokin
fonte
é para iOS aqui, não Android
Back Packer