"Tenga en cuenta que no puede llamar al método antes de ver diseños".
El texto anterior es la clave.
Los diálogos tienen un oyente que se activa una vez que se muestra el diálogo . El cuadro de diálogo no se puede mostrar si no está diseñado.
Entonces, en la onCreateDialog()
parte inferior de su hoja modal ( BottomSheetFragment
), justo antes de devolver el diálogo (o en cualquier lugar, una vez que tenga una referencia al diálogo), llame a:
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);
}
});
En mi caso, mi costumbre BottomSheet
resultó ser:
@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;
}
}
Déjeme saber si esto ayuda.
ACTUALIZAR
Tenga en cuenta que también puede anular 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;
}
}
Pero realmente no veo por qué alguien querría hacer eso ya que la base BottomSheetFragment
no hace nada más que devolver un BottomSheetDialog
.
ACTUALIZAR PARA ANDROIDX
Al usar AndroidX, el recurso que se encontraba anteriormente en android.support.design.R.id.design_bottom_sheet
ahora se puede encontrar en com.google.android.material.R.id.design_bottom_sheet
.
BottomSheetDialogFragment
parezca inestable (parece saltar fotogramas en la animación de apertura) a medida que pasa del comportamiento contraído al expandido. Editar: probado esto en dispositivos Android Marshmallow y KitKatandroid.support.design.R
después de actualizar las bibliotecas de soporte?android.support.design.R
, como @natario. Estoy usandoimplementation "com.google.android.material:material:1.0.0"
. También estoy usando AndroidX en el proyecto.com.google.android.material.R.id.design_bottom_sheet
La respuesta de efeturi es excelente, sin embargo, si desea usar onCreateView () para crear su BottomSheet, en lugar de ir con onCreateDialog () , aquí está el código que deberá agregar en su 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); }
fuente
Una solución simple y elegante:
BottomSheetDialogFragment
podría subclasificarse para abordar esto: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; } }
Así que amplíe esta clase en lugar de
BottomSheetDialogFragment
crear su propia hoja inferior.Nota
Cambie
com.google.android.material.R.id.design_bottom_sheet
aandroid.support.design.R.id.design_bottom_sheet
si su proyecto usa bibliotecas de soporte de Android antiguas.fuente
com.google.android.material.R
ahora en lugar deandroid.support.design.R
.Creo que los de arriba son mejores. Lamentablemente, no encontré esas soluciones antes de resolverlas. Pero escribe mi solución. bastante similar a todos.
================================================ ================================
Me enfrento al mismo problema. Esto es lo que resolví. El comportamiento está oculto en BottomSheetDialog, que está disponible para obtener el comportamiento. Si no desea cambiar su diseño principal para que sea CooridateLayout, puede intentar esto.
PASO 1: personalice el 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 } }
PASO 2: haz que tu fragmento extienda 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() } }
fuente
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); } });
Me encontré con NullPointException
BottomSheetBehavior.from(bottomSheet)
porqued.findViewById(android.support.design.R.id.design_bottom_sheet)
devuelve nulo.Es extraño. Agregué esta línea de código a Watches en Android Monitor en modo DEBUG y encontré que devuelve Framelayout normalmente.
Aquí está el código de
wrapInBottomSheet
en 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; }
De vez en cuando, descubrí que
R.id.design_bottom_sheet
no es igual aandroid.support.design.R.id.design_bottom_sheet
. Tienen un valor diferente en diferentes R.java.Entonces me cambio
android.support.design.R.id.design_bottom_sheet
aR.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); } });
No más NullPointException ahora.
fuente
Aplicar
BottomsheetDialogFragment
estado enonResume
resolverá este problema@Override public void onResume() { super.onResume(); if(mBehavior!=null) mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); }
onShow(DialogInterface dialog)
ypostDelayed
puede causar problemas de animaciónfuente
Todos los resultados con el uso de onShow () causan un error de renderizado aleatorio cuando se muestra el teclado virtual. Vea la captura de pantalla a continuación: el cuadro de diálogo Hoja inferior no está en la parte inferior de la pantalla, pero se coloca como se mostró el teclado. Este problema no ocurre siempre, pero sí con bastante frecuencia.
ACTUALIZAR
Mi solución con la reflexión de un miembro privado es innecesaria. Usar postDelayed (con aproximadamente 100 ms) para crear y mostrar el diálogo después de ocultar el teclado virtual es una mejor solución. Entonces las soluciones anteriores con onShow () están bien.
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);
Entonces implemento otra solución, pero requiere el uso de la reflexión, porque BottomSheetDialog tiene todos los miembros como privados. Pero resuelve el error de renderizado. La clase BottomSheetDialogFragment es solo AppCompatDialogFragment con el método onCreateDialog que crea BottomSheetDialog. Creo el propio hijo de AppCompatDialogFragment que crea mi clase extiende BottomSheetDialog y que resuelve el acceso al miembro de comportamiento privado y lo configuro en el método onStart al 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()); } .... }
fuente
La forma más fácil que implementé es la siguiente: Aquí encontramos android.support.design.R.id.design_bottom_sheet y configuramos el estado de la hoja inferior como EXPANDED .
Sin esto, mi hoja inferior siempre estaba atascada en el estado COLLAPSED si la altura de la vista es más de 0.5 de la altura de la pantalla y tengo que desplazarme manualmente para ver la hoja inferior completa.
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 } }
fuente
Similar a la respuesta de uregentx , en kotlin , puede declarar su clase de fragmento que se extiende desde
BottomSheetDialogFragment
, y cuando se crea la vista, puede establecer el estado predeterminado del oyente de diálogo después de que se muestre el diálogo.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 } }
Recuerde usar la implementación de material design en gradle.
También eche un vistazo a las hojas inferiores de referencia de material design
fuente
dialog?
viene la variable en onCreateView?dialog
es una propiedad de la claseDialogFragment
, en realidad es un Getter. En este ejemplo utilicé ese captador para obtener la instancia actual de DialogFragment ysetOnShowListener
a ella. Puede ser que ya haya usado ese tipo de instrucciones en su proyecto, por ejemplo, en una actividad, para acceder a la barra de acciónactionBar
se usa el getter, por lo que puede modificar ese componente, por ejemploactionBar?.subtitle = "abcd"
Mi respuesta es más o menos la misma que la mayoría de las respuestas anteriores con una ligera modificación. En lugar de usar findViewById para buscar primero la vista de la hoja inferior, he preferido no codificar ningún ID de recurso de vista de marco, ya que podrían cambiar en el futuro.
setOnShowListener(dialog -> { BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior(); bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); });
fuente
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return super.onCreateDialog(savedInstanceState).apply { setOnShowListener { (this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState( BottomSheetBehavior.STATE_EXPANDED ) } } }
fuente
Publicando esto aquí para futuros lectores, ya que creo que podemos usar otra solución.
Estaba tratando de resolver el mismo problema que describiste con un
BottomSheetDialog
.No me gusta usar ID internos de Android y acabo de descubrir que hay un método dentro
BottomSheetDialog
getBehavior
que puedes usar:Puedes usar esto dentro de tu
BottomSheetDialog
:behavior.state = BottomSheetBehavior.STATE_EXPANDED
Utilizando
BottomSheetDialogFragment
puede hacer lo mismo para convertir el diálogo desde ese DialogFragment aBottomSheetDialog
.fuente
BottomSheetDialogFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED }
o cuando esté listo para mostrar:
private fun onContentLoaded(items: List<Any>) { adapter.submitList(items) (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED }
fuente
En su clase Kotlin BottomSheetDialogFragment, anule onCreateDialog como se muestra a continuación
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 }
fuente