"Observe que você não pode chamar o método antes de visualizar os layouts."
O texto acima é a pista.
Os diálogos têm um ouvinte que é disparado assim que o diálogo é mostrado . A caixa de diálogo não pode ser exibida se não estiver disposta.
Portanto, no onCreateDialog()
de sua folha de fundo modal ( BottomSheetFragment
), antes de retornar o diálogo (ou em qualquer lugar, uma vez que você tenha uma referência para o diálogo), chame:
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet)
.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
No meu caso, meu costume BottomSheet
acabou sendo:
@SuppressWarnings("ConstantConditions")
public class ShareBottomSheetFragment extends AppCompatDialogFragment {
@NonNull @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
BottomSheetDialog dialog =
new BottomSheetDialog(getActivity(), R.style.Haute_Dialog_ShareImage);
dialog.setContentView(R.layout.dialog_share_image);
dialog.findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
SwitchCompat switchview = (SwitchCompat) dialog.findViewById(R.id.switchview);
switchview.setTypeface(FontCache.get(dialog.getContext(), lookup(muli, NORMAL)));
return dialog;
}
}
Avise-me se isso ajudar.
ATUALIZAR
Observe que você também pode substituir BottomSheetDialogFragment
como:
public class SimpleInitiallyExpandedBottomSheetFragment extends BottomSheetDialogFragment {
@NonNull @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
return dialog;
}
}
Mas eu realmente não vejo por que alguém iria querer fazer isso já que a base BottomSheetFragment
não faz nada além de retornar um BottomSheetDialog
.
ATUALIZAÇÃO PARA ANDROIDX
Ao usar o AndroidX, o recurso anteriormente encontrado em android.support.design.R.id.design_bottom_sheet
agora pode ser encontrado em com.google.android.material.R.id.design_bottom_sheet
.
BottomSheetDialogFragment
pareça instável (parece pular quadros na animação de abertura) à medida que vai do comportamento recolhido ao expandido. Editar: Testado em dispositivos Android Marshmallow e KitKatandroid.support.design.R
após atualizar as bibliotecas de suporte?android.support.design.R
, assim como @natario. Estou usandoimplementation "com.google.android.material:material:1.0.0"
. Também estou usando o AndroidX no projeto.com.google.android.material.R.id.design_bottom_sheet
A resposta de efeturi é ótima, no entanto, se você quiser usar onCreateView () para criar sua BottomSheet, em vez de usar onCreateDialog ( ) , aqui está o código que você precisará adicionar em seu método onCreateView () :
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { getDialog().setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { BottomSheetDialog d = (BottomSheetDialog) dialog; View bottomSheetInternal = d.findViewById(android.support.design.R.id.design_bottom_sheet); BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED); } }); return inflater.inflate(R.layout.your_bottomsheet_content_layout, container, false); }
fonte
Uma solução simplista e elegante:
BottomSheetDialogFragment
poderia ser uma subclasse para resolver isso:class NonCollapsableBottomSheetDialogFragment extends BottomSheetDialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState); bottomSheetDialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { FrameLayout bottomSheet = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet); behavior.setSkipCollapsed(true); behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } }); return bottomSheetDialog; } }
Portanto, estenda esta classe em vez de
BottomSheetDialogFragment
criar sua própria planilha inferior.Nota
Altere
com.google.android.material.R.id.design_bottom_sheet
paraandroid.support.design.R.id.design_bottom_sheet
se o seu projeto usa bibliotecas de suporte Android antigas.fonte
com.google.android.material.R
agora, em vez deandroid.support.design.R
.Eu acho que aqueles acima são melhores. Infelizmente, não encontrei essas soluções antes de resolver. Mas escreva minha solução. bastante semelhante a todos.
======================================================== ==================================
Eu enfrento o mesmo problema. Isso é o que eu resolvi. O comportamento está oculto em BottomSheetDialog, que está disponível para obter o comportamento. Se você não quiser alterar o layout pai para CooridateLayout, pode tentar isso.
ETAPA 1: personalize o BottomSheetDialogFragment
open class CBottomSheetDialogFragment : BottomSheetDialogFragment() { //wanna get the bottomSheetDialog protected lateinit var dialog : BottomSheetDialog override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog return dialog } //set the behavior here fun setFullScreen(){ dialog.behavior.state = STATE_EXPANDED } }
PASSO 2: faça seu fragmento estender este fragmento personalizado
class YourBottomSheetFragment : CBottomSheetDialogFragment(){ //make sure invoke this method after view is built //such as after OnActivityCreated(savedInstanceState: Bundle?) override fun onStart() { super.onStart() setFullScreen()//initiated at onActivityCreated(), onStart() } }
fonte
dialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { BottomSheetDialog d = (BottomSheetDialog) dialog; FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet); BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED); } });
Conheci NullPointException em
BottomSheetBehavior.from(bottomSheet)
porqued.findViewById(android.support.design.R.id.design_bottom_sheet)
retorna null.É estranho. Eu adicionei esta linha de código a Relógios no Android Monitor no modo DEBUG e descobri que ela retorna o Framelayout normalmente.
Aqui está o código de
wrapInBottomSheet
BottomSheetDialog:private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) { final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(), R.layout.design_bottom_sheet_dialog, null); if (layoutResId != 0 && view == null) { view = getLayoutInflater().inflate(layoutResId, coordinator, false); } FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet); BottomSheetBehavior.from(bottomSheet).setBottomSheetCallback(mBottomSheetCallback); if (params == null) { bottomSheet.addView(view); } else { bottomSheet.addView(view, params); } // We treat the CoordinatorLayout as outside the dialog though it is technically inside if (shouldWindowCloseOnTouchOutside()) { coordinator.findViewById(R.id.touch_outside).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { if (isShowing()) { cancel(); } } }); } return coordinator; }
Ocasionalmente, descobri que
R.id.design_bottom_sheet
não é igual aandroid.support.design.R.id.design_bottom_sheet
. Eles têm valores diferentes em diferentes R.java.Então eu mudo
android.support.design.R.id.design_bottom_sheet
paraR.id.design_bottom_sheet
.dialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialog) { BottomSheetDialog d = (BottomSheetDialog) dialog; FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.design_bottom_sheet); // use R.java of current project BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED); } });
Não há mais NullPointException agora.
fonte
Aplicar
BottomsheetDialogFragment
estado emonResume
resolverá este problema@Override public void onResume() { super.onResume(); if(mBehavior!=null) mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); }
onShow(DialogInterface dialog)
epostDelayed
pode causar falha de animaçãofonte
Todos os resultados com o uso de onShow () causam bug de renderização aleatória quando o teclado virtual é exibido. Veja a captura de tela abaixo - a caixa de diálogo BottomSheet não está na parte inferior da tela, mas está posicionada como o teclado foi exibido. Este problema não ocorre sempre, mas com bastante frequência.
ATUALIZAR
Minha solução com reflexão de membro privado é desnecessária. Usar postDelayed (com cerca de 100 ms) para criar e mostrar o diálogo após ocultar o teclado virtual é a melhor solução. Então, as soluções acima com onShow () estão ok.
Utils.hideSoftKeyboard(this); mView.postDelayed(new Runnable() { @Override public void run() { MyBottomSheetDialog dialog = new MyBottomSheetDialog(); dialog.setListener(MyActivity.this); dialog.show(getSupportFragmentManager(), TAG_BOTTOM_SHEET_DLG); } }, 100);
Portanto, implemento outra solução, mas exige o uso de reflexão, porque BottomSheetDialog tem todos os membros como privados. Mas resolve bug de renderização. A classe BottomSheetDialogFragment é apenas AppCompatDialogFragment com o método onCreateDialog que cria BottomSheetDialog. Eu crio o próprio filho de AppCompatDialogFragment que cria minha classe extends BottomSheetDialog e que resolve o acesso ao membro de comportamento privado e o define no método onStart para o estado STATE_EXPANDED.
public class ExpandedBottomSheetDialog extends BottomSheetDialog { protected BottomSheetBehavior<FrameLayout> mBehavior; public ExpandedBottomSheetDialog(@NonNull Context context, @StyleRes int theme) { super(context, theme); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { Field privateField = BottomSheetDialog.class.getDeclaredField("mBehavior"); privateField.setAccessible(true); mBehavior = (BottomSheetBehavior<FrameLayout>) privateField.get(this); } catch (NoSuchFieldException e) { // do nothing } catch (IllegalAccessException e) { // do nothing } } @Override protected void onStart() { super.onStart(); if (mBehavior != null) { mBehavior.setSkipCollapsed(true); mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); } } } public class AddAttachmentBottomSheetDialog extends AppCompatDialogFragment { .... @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { return new ExpandedBottomSheetDialog(getContext(), getTheme()); } .... }
fonte
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return super.onCreateDialog(savedInstanceState).apply { setOnShowListener { (this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState( BottomSheetBehavior.STATE_EXPANDED ) } } }
fonte
Postando isso aqui para futuros leitores, pois acho que agora podemos usar outra solução.
Eu estava tentando resolver o mesmo problema que você descreveu com a
BottomSheetDialog
.Não gosto de usar IDs internos do Android e acabei de descobrir que há um método interno
BottomSheetDialog
getBehavior
que você pode usar:Você pode usar isso dentro de
BottomSheetDialog
:behavior.state = BottomSheetBehavior.STATE_EXPANDED
Usando,
BottomSheetDialogFragment
você pode fazer o mesmo convertendo a caixa de diálogo desse DialogFragment paraBottomSheetDialog
.fonte
A maneira mais fácil que implementei é a abaixo, aqui estamos encontrando android.support.design.R.id.design_bottom_sheet e definindo o estado da folha inferior como EXPANDIDO .
Sem isso, minha folha inferior sempre ficava presa no estado RECOLHIDO se a altura da visualização for maior que 0,5 da altura da tela e eu tiver que rolar manualmente para visualizar a folha inferior inteira.
class BottomSheetDialogExpanded(context: Context) : BottomSheetDialog(context) { private lateinit var mBehavior: BottomSheetBehavior<FrameLayout> override fun setContentView(view: View) { super.setContentView(view) val bottomSheet = window.decorView.findViewById<View>(android.support.design.R.id.design_bottom_sheet) as FrameLayout mBehavior = BottomSheetBehavior.from(bottomSheet) mBehavior.state = BottomSheetBehavior.STATE_EXPANDED } override fun onStart() { super.onStart() mBehavior.state = BottomSheetBehavior.STATE_EXPANDED } }
fonte
Semelhante à resposta uregentx , no kotlin , você pode declarar sua classe de fragmento que se estende de
BottomSheetDialogFragment
, e quando a visualização é criada, você pode definir o estado padrão do ouvinte de diálogo após a caixa de diálogo ser exibida.class FragmentCreateGroup : BottomSheetDialogFragment() { ... override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? { // Set dialog initial state when shown dialog?.setOnShowListener { val bottomSheetDialog = it as BottomSheetDialog val sheetInternal: View = bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheet)!! BottomSheetBehavior.from(sheetInternal).state = BottomSheetBehavior.STATE_COLLAPSED } val view = inflater.inflate(R.layout.fragment_create_group, container, false) ... return view } }
Lembre-se de usar a implementação de design de material no gradle.
Também dê uma olhada nas folhas inferiores de referência do material design
fonte
dialog?
variável em onCreateView?dialog
é uma propriedade da classeDialogFragment
, na verdade é um Getter. Neste exemplo, usei esse getter para obter a instância DialogFragment atual esetOnShowListener
para ela. Pode ser que você já tenha usado esse tipo de instruções em seu projeto, por exemplo em uma atividade, para acessar oactionBar
getter da barra de ação , então você pode modificar esse componente, por exemploactionBar?.subtitle = "abcd"
Minha resposta é mais ou menos igual à maioria das respostas acima, com uma ligeira modificação. Em vez de usar findViewById para encontrar primeiro a visualização da folha inferior, preferi não codificar permanentemente nenhum ID de recurso de visualização de estrutura, pois eles podem mudar no futuro.
setOnShowListener(dialog -> { BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior(); bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); });
fonte
BottomSheetDialogFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED }
ou quando estiver pronto para mostrar:
private fun onContentLoaded(items: List<Any>) { adapter.submitList(items) (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED }
fonte
Em sua classe Kotlin BottomSheetDialogFragment, substitua onCreateDialog como abaixo
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog bottomSheetDialog.setOnShowListener { val bottomSheet = bottomSheetDialog.findViewById<FrameLayout>( com.google.android.material.R.id.design_bottom_sheet ) val behavior = BottomSheetBehavior.from(bottomSheet!!) behavior.skipCollapsed = true behavior.state = BottomSheetBehavior.STATE_EXPANDED } return bottomSheetDialog }
fonte