android - example - Establecer el estado de BottomSheetDialogFragment en expandido
bottom sheet android (9)
¿Cómo se configura el estado de un fragmento que extiende
BottomSheetDialogFragment
para expandirse usando
BottomSheetBehavior#setState(STATE_EXPANDED)
usando la Biblioteca de diseño de soporte de Android (v23.2.1)?
https://code.google.com/p/android/issues/detail?id=202396 dice:
Las hojas inferiores se establecen en STATE_COLLAPSED al principio. Llame a BottomSheetBehavior # setState (STATE_EXPANDED) si desea expandirlo. Tenga en cuenta que no puede llamar al método antes de ver los diseños.
La
práctica sugerida
requiere que primero se infle una vista, pero no estoy seguro de cómo estableceré BottomSheetBehaviour en un fragmento (
BottomSheetDialogFragment
).
View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
"Tenga en cuenta que no puede llamar al método antes de ver los diseños".
El texto anterior es la pista.
Los cuadros de diálogo tienen un oyente que se activa una vez que se muestra el cuadro de diálogo. El cuadro de diálogo no se puede mostrar si no se presenta.
Entonces, en el
onCreateDialog()
de su hoja inferior modal (
BottomSheetFragment
), justo antes de devolver el cuadro de diálogo (o en cualquier lugar, una vez que tenga una referencia al cuadro de diálogo), llame al:
// This listener''s onShow is fired when the dialog is shown
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
// In a previous life I used this method to get handles to the positive and negative buttons
// of a dialog in order to change their Typeface. Good ol'' days.
BottomSheetDialog d = (BottomSheetDialog) dialog;
// This is gotten directly from the source of BottomSheetDialog
// in the wrapInBottomSheet() method
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
// Right here!
BottomSheetBehavior.from(bottomSheet)
.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
En mi caso, mi
BottomSheet
personalizado 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);
}
});
// Do something with your dialog like setContentView() or whatever
return dialog;
}
}
Pero realmente no veo por qué alguien querría hacer eso, ya que el
BottomSheetFragment
base no hace nada más que devolver un
BottomSheetDialog
.
ACTUALIZACIÓN 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
.
Aplicar el estado BottomsheetDialogFragment en OnResume resolverá este problema
@Override
public void onResume() {
super.onResume();
if(mBehavior!=null)
mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
onShow (diálogo DialogInterface) y postDelayed pueden causar fallas en la animación
Creo que los de arriba son mejores. Lamentablemente no encontré esa solución antes de haber resuelto. 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 desea no cambiar su diseño principal para que sea CooridateLayout, puede intentarlo.
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: haga que su 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()
}
}
Escribí una subclase de
BottomSheetDialogFragment
para manejar esto:
public 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(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setSkipCollapsed(true);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
return bottomSheetDialog;
}
}
Por lo tanto, amplíe esta clase en lugar de
BottomSheetDialogFragment
para crear su propia hoja inferior.
Cambie
com.google.android.material.R.id.design_bottom_sheet
a
android.support.design.R.id.design_bottom_sheet
si su proyecto utiliza antiguas bibliotecas de soporte de Android.
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 EXPANDIDO .
Sin esto, mi hoja inferior siempre estaba atascada en el estado COLAPSED 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
}
}
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 bajo 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);
}
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.
STATE_COLLAPSED: la hoja inferior es visible pero solo muestra su altura de vista.
STATE_EXPANDED: la hoja inferior es visible y su altura máxima.
STATE_HALF_EXPANDED: la hoja inferior es visible pero solo muestra su altura media.
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 del diseño de materiales en gradle.
implementation "com.google.android.material:material:$version"
Consulte también las hojas de referencia de diseño de materiales.
Todos los resultados con el uso de onShow () provocan un error de renderizado aleatorio cuando se muestra el teclado virtual. Vea la captura de pantalla a continuación: el cuadro de diálogo BottomSheet no se encuentra en la parte inferior de la pantalla, sino que se coloca como se muestra el teclado. Este problema no se produce siempre, sino con bastante frecuencia.
ACTUALIZAR
Mi solución con reflejo de 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);
Así que 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 un hijo propio de AppCompatDialogFragment que crea mi clase extiende BottomSheetDialog y resuelve el acceso al miembro de comportamiento privado y lo establece 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());
}
....
}
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);
}
});
Conocí a NullPointException en
BottomSheetBehavior.from(bottomSheet)
porque
d.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;
}
Ocasionalmente, descubrí que
R.id.design_bottom_sheet
no es igual a
android.support.design.R.id.design_bottom_sheet
.
Tienen un valor diferente en diferentes R.java.
Así que cambio
android.support.design.R.id.design_bottom_sheet
a
R.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.