29

Jak ustawić stan fragmentu rozszerzającego BottomSheetDialogFragment na rozwinięty przy użyciu BottomSheetBehavior#setState(STATE_EXPANDED) przy użyciu biblioteki projektowania wsparcia dla Androida (v23.2.1)?Ustaw stan BottomSheetDialogFragment na rozwinięty

https://code.google.com/p/android/issues/detail?id=202396 mówi: Arkusze

denne są ustawione na STATE_COLLAPSED na początku. Call BottomSheetBehavior # setState (STATE_EXPANDED), jeśli chcesz go rozwinąć. Zauważ, że nie możesz wywołać metody przed układem widoku.

The suggested practice wymaga aby być pierwszy zawyżone, ale nie jestem pewien, jak będę ustawić BottomSheetBehaviour na fragmencie (BottomSheetDialogFragment).

View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet); 
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet); 

Odpowiedz

70

"Należy pamiętać, że nie można wywołać metody przed układem widoku." < --- To jest wskazówka.

W dialogach znajduje się detektor, który jest uruchamiany po wyświetleniu okna dialogowego . Nie można wyświetlić okna dialogowego, jeśli nie jest ono ułożone.

Więc w onCreateDialog() swojego modalnej prześcieradło (BottomSheetFragment), tuż przed powrotem do okna dialogowego (lub gdziekolwiek, skoro masz odniesienie do okna), zadzwoń:

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

W moim Przypadek mój niestandardowy BottomSheet okazał się być:

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

Daj mi znać, jeśli to pomoże.

UPDATE

pamiętać, że można również zastąpić BottomSheetDialogFragment jak:

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

ale ja naprawdę nie rozumiem, dlaczego ktoś chciałby to zrobić jako doesnt baza BottomSheetFragment zrobić coś innego niż zwróci BottomSheetDialog

+0

Dzięki, próbowałem tej metody.To sprawia, że ​​'BottomSheetDialogFragment' wygląda jak jankowy (wydaje się pomijać klatki w animacji otwierającej), gdy przechodzi od zwiniętego do rozwiniętego zachowania. Edycja: Przetestowano to na urządzeniach Android Marshmallow i KitKat – user2560886

+1

Działa to idealnie dla mnie. Bez przeskakiwania. Czy robisz cokolwiek innego poza właśnie zwróceniem okna dialogowego? Będziemy wdzięczni, jeśli zaktualizujesz swój post za pomocą kodu, dzięki czemu będę mógł mieć lepszy pomysł. – efemoney

+0

Nie zauważam żadnego szarpnięcia; działa dobrze. – rpattabi

8

Odpowiedź efeturi jest świetna, jednak jeśli chcesz użyć onCreateView() stworzyć swój BottomSheet, w przeciwieństwie do przechodząc z onCreateDialog(), oto kod trzeba będzie dodać pod onCreateView() metoda:

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

Alternatywnie nie musisz wywoływać getDialog w ogóle. Uważam, że najczystszym sposobem na to jest zastąpienie zarówno onCreateView, jak i onCreateDialog. Zbuduj swój widok w onCreateView (tak jak w przypadku dowolnego fragmentu) i wykonaj specyficzny dialog w kodzie onCreateDialog (zadzwoń super.onCreateDialog, aby pobrać instancję). – Stimsoni

+0

To uratuje mój dzień. Dzięki. – AndroidRuntimeException

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

poznałem NullPointException w BottomSheetBehavior.from(bottomSheet) ponieważ d.findViewById(android.support.design.R.id.design_bottom_sheet) zwraca wartość null.

To dziwne. Dodaję ten wiersz kodu do zegarków w Android Monitor w trybie DEBUG i stwierdzam, że zwykle zwraca Framelayout.

Oto kod wrapInBottomSheet w 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; 
    } 

sporadycznie, stwierdziliśmy, że R.id.design_bottom_sheet nie jest równa android.support.design.R.id.design_bottom_sheet. Mają różną wartość w różnych R.java.

Zmieniam android.support.design.R.id.design_bottom_sheet na 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); 
      } 
     }); 

Nie ma więcej wyjątku NullPointException.

0

Wszystkie wyniki z użyciem funkcji onShow() powodują losowy błąd renderowania po wyświetleniu klawiatury programowej. Zobacz zrzut ekranu poniżej - Okno dialogowe BottomSheet nie znajduje się u dołu ekranu, ale jest umieszczone tak, jak wyświetlana jest klawiatura. Ten problem nie występuje zawsze, ale często.

enter image description here

UPDATE

Moje rozwiązanie z odbiciem prywatnej członka jest niepotrzebna. Używanie postDelayed (około 100 ms) do tworzenia i pokazywania dialogów po ukrytej klawiaturze programowej jest lepszym rozwiązaniem. Wtedy powyższe rozwiązania z funkcją onShow() są w porządku.

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

więc zaimplementować inne rozwiązanie, ale wymaga przy użyciu odbicia, ponieważ BottomSheetDialog ma wszystkich członków jako prywatne. Ale rozwiązuje problem z renderowaniem. Klasa BottomSheetDialogFragment to tylko AppCompatDialogFragment z metodą onCreateDialog, która tworzy BottomSheetDialog. Tworzę własne dziecko AppCompatDialogFragment, które tworzy moją klasę rozszerzającą BottomSheetDialog i która rozwiązuje dostęp do prywatnego elementu zachowania i ustawia go w metodzie onStart na stan 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()); 
    } 

    .... 
} 
0

Zastosuj stan BottomsheetDialogFragment w OnResume będzie rozwiązać ten problem

@Override 
    public void onResume() { 
     super.onResume(); 
     if(mBehavior!=null) 
      mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); 
    } 

onShow (okno DialogInterface) i postDelayed może spowodować animację Glitch

0

pisałem podklasę BottomSheetDialogFragment obsłużyć to:

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

}

Rozszerz tę klasę zamiast BottomSheetDialogFragment, aby utworzyć własny arkusz dolny.

0

Najłatwiej I realizowany jest poniżej Tutaj znajdujemy android.support.design.R.id.design_bottom_sheet i ustawiając stan dolny arkusz jako EXPANDED.

Bez tego, mój dolny arkusz był zawsze zablokowany w stanie ZWOLNIONYM, jeśli wysokość widoku jest większa niż 0,5 wysokości ekranu i muszę ręcznie przewinąć, aby wyświetlić pełny dolny arkusz.

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