Setzen Sie den Status von BottomSheetDialogFragment auf erweitert


92

Wie können Sie den Status eines Fragments mithilfe der Android Support Design Library (v23.2.1) BottomSheetDialogFragmentauf erweitert erweitern BottomSheetBehavior#setState(STATE_EXPANDED)?

https://code.google.com/p/android/issues/detail?id=202396 sagt:

Die unteren Blätter werden zuerst auf STATE_COLLAPSED gesetzt. Rufen Sie BottomSheetBehavior # setState (STATE_EXPANDED) auf, wenn Sie es erweitern möchten. Beachten Sie, dass Sie die Methode nicht vor dem Anzeigen von Layouts aufrufen können.

Die vorgeschlagene Vorgehensweise erfordert, dass zuerst eine Ansicht aufgeblasen wird, aber ich bin nicht sicher, wie ich das BottomSheetBehaviour auf ein Fragment ( BottomSheetDialogFragment) setzen werde.

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

Antworten:


221

"Beachten Sie, dass Sie die Methode nicht vor dem Anzeigen von Layouts aufrufen können."

Der obige Text ist der Hinweis.

Dialoge haben einen Listener, der ausgelöst wird, sobald der Dialog angezeigt wird . Der Dialog kann nicht angezeigt werden, wenn er nicht angelegt ist.

Rufen Sie also in onCreateDialog()Ihrem modalen unteren Blatt ( BottomSheetFragment) kurz vor dem Zurücksenden des Dialogfelds (oder irgendwo, sobald Sie einen Verweis auf das Dialogfeld haben) Folgendes auf:

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

In meinem Fall stellte BottomSheetsich heraus, dass mein Brauch :

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

Lassen Sie mich wissen, ob das hilft.

AKTUALISIEREN

Beachten Sie, dass Sie Folgendes überschreiben können BottomSheetDialogFragment:

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

Aber ich verstehe wirklich nicht, warum irgendjemand das tun möchte, da die Basis BottomSheetFragmentnichts anderes tut, als a zurückzugeben BottomSheetDialog.

UPDATE FÜR ANDROIDX

Bei Verwendung von AndroidX kann die zuvor gefundene Ressource android.support.design.R.id.design_bottom_sheetjetzt unter gefunden werden com.google.android.material.R.id.design_bottom_sheet.


Danke, ich habe diese Methode ausprobiert. Es lässt das BottomSheetDialogFragmentBild ruckelig erscheinen (scheint Frames in der Eröffnungsanimation zu überspringen), wenn es vom reduzierten zum erweiterten Verhalten übergeht. Bearbeiten: Getestet auf Android Marshmallow und KitKat Geräten
user2560886

1
Es funktioniert perfekt für mich. Kein Überspringen. Tun Sie etwas anderes als nur einen Dialog zurückzugeben? Ich würde mich freuen, wenn Sie Ihren Beitrag mit Ihrem Code aktualisieren, damit ich eine bessere Idee habe.
Efemoney

5
Kann ich android.support.design.Rnach dem Aktualisieren der Support-Bibliotheken nur kein Paket finden ?
Natario

2
Ich habe auch Probleme mit der Lösung android.support.design.R, genau wie @natario. Ich benutze implementation "com.google.android.material:material:1.0.0". Ich benutze auch AndroidX im Projekt.
Hsson

21
Bei Verwendung von AndroidX finden Sie die Ressource untercom.google.android.material.R.id.design_bottom_sheet
urgentx

45

Die Antwort von efeturi ist großartig. Wenn Sie jedoch onCreateView () zum Erstellen Ihres BottomSheet verwenden möchten, anstatt mit onCreateDialog () zu arbeiten , müssen Sie den folgenden Code unter Ihrer onCreateView () -Methode hinzufügen :

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

3
Alternativ müssen Sie getDialog überhaupt nicht aufrufen. Ich finde, der sauberste Weg, dies zu tun, besteht darin, sowohl onCreateView als auch onCreateDialog zu überschreiben. Erstellen Sie Ihre Ansicht in onCreateView (wie bei jedem Fragment) und führen Sie den dialogspezifischen Code in onCreateDialog aus (rufen Sie super.onCreateDialog auf, um die Instanz
abzurufen

2
Das rette meinen Tag. Vielen Dank.
AndroidRuntimeException

@Stimsoni onCreateView wird nicht aufgerufen, wenn onCreateDialog verwendet wird. developer.android.com/reference/android/support/v4/app/…
Vincent_Paing

1
@ Vincent_Paing, ja das ist es. In Ihrem angehängten Link steht "onCreateView muss nicht implementiert werden". Es heißt nicht, dass es nicht aufgerufen wird. Schauen Sie sich den Quellcode hier an: github.com/material-components/material-components-android/blob/… . Die Standardimplementierung ruft onCreateDialog auf, um das untere Blatt zu erstellen, und jede der oben genannten Lösungen verwendet weiterhin onCreateView, was bedeutet, dass beide immer aufgerufen werden. Stellen Sie einfach sicher, dass Sie immer noch super.onCreateDialog () aufrufen, wenn Sie es überschreiben.
Stimsoni

in BottomSheetDialogFragment stürzt es mich in onCreateView () ab Ich habe es in onViewCreated () eingefügt und es ist perfekt! danke
avisper

22

Eine einfache und elegante Lösung:

BottomSheetDialogFragment könnte in Unterklassen unterteilt werden, um dies zu beheben:

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

Erweitern Sie also diese Klasse, anstatt BottomSheetDialogFragmentIhr eigenes unteres Blatt zu erstellen.

Hinweis

Wechseln Sie com.google.android.material.R.id.design_bottom_sheetzu, android.support.design.R.id.design_bottom_sheetwenn Ihr Projekt alte Android-Unterstützungsbibliotheken verwendet.


1
Scheint com.google.android.material.Rjetzt statt zu sein android.support.design.R.
EpicPandaForce

@EpicPandaForce Sicher. Das Android-Team von Google hat kürzlich die frühere Support-Bibliothek neu verpackt.
DYS

4

Ich denke, die oben genannten sind besser. Leider habe ich diese Lösung nicht gefunden, bevor ich sie gelöst hatte. Aber schreibe meine Lösung. ganz ähnlich zu allen.

================================================== ===============================

Ich stehe vor dem gleichen Problem. Das habe ich gelöst. Das Verhalten ist in BottomSheetDialog ausgeblendet, das zum Abrufen des Verhaltens verfügbar ist. Wenn Sie Ihr übergeordnetes Layout nicht in CooridateLayout ändern möchten, können Sie dies versuchen.

SCHRITT 1: Passen Sie das BottomSheetDialogFragment an

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

SCHRITT 2: Lassen Sie Ihr Fragment dieses angepasste Fragment erweitern

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

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

Ich habe NullPointException in getroffen, BottomSheetBehavior.from(bottomSheet)weil d.findViewById(android.support.design.R.id.design_bottom_sheet)null zurückgegeben wird.

Es ist komisch. Ich habe diese Codezeile im DEBUG-Modus zu Watches in Android Monitor hinzugefügt und festgestellt, dass sie Framelayout normal zurückgibt.

Hier ist der Code von wrapInBottomSheetin 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;
    }

Gelegentlich stellte ich fest, dass R.id.design_bottom_sheetdas nicht gleich ist android.support.design.R.id.design_bottom_sheet. Sie haben unterschiedliche Werte in unterschiedlichen R.java.

Also wechsle ich android.support.design.R.id.design_bottom_sheetzu 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);
            }
        });

Keine NullPointException mehr.


3

BottomsheetDialogFragmentStatus anwenden in onResumewird dieses Problem lösen

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

onShow(DialogInterface dialog)und postDelayedkann Animationsfehler verursachen


2

Alle Ergebnisse bei Verwendung von onShow () verursachen einen zufälligen Renderfehler, wenn eine weiche Tastatur angezeigt wird. Siehe Screenshot unten - Das Dialogfeld "BottomSheet" befindet sich nicht am unteren Bildschirmrand, sondern wird so platziert, als ob die Tastatur angezeigt wurde. Dieses Problem tritt nicht immer, sondern häufig auf.

Geben Sie hier die Bildbeschreibung ein

AKTUALISIEREN

Meine Lösung mit Reflexion eines privaten Mitglieds ist unnötig. Die Verwendung von postDelayed (mit ca. 100 ms) zum Erstellen und Anzeigen von Dialogen nach dem Ausblenden der Softtastatur ist die bessere Lösung. Dann sind die obigen Lösungen mit onShow () in Ordnung.

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

Also implementiere ich eine andere Lösung, aber es erfordert die Verwendung von Reflection, da BottomSheetDialog alle Mitglieder als privat hat. Aber es löst Renderfehler. Die BottomSheetDialogFragment-Klasse ist nur AppCompatDialogFragment mit der onCreateDialog-Methode, die BottomSheetDialog erstellt. Ich erstelle ein eigenes Kind von AppCompatDialogFragment, das meine Klasse erstellt, erweitert BottomSheetDialog und löst den Zugriff auf private Verhaltensmitglieder und setzt es in der onStart-Methode auf den Status 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());
    }

    ....
}

1

Der einfachste Weg, den ich implementiert habe, ist wie folgt: Hier finden wir android.support.design.R.id.design_bottom_sheet und setzen den Status des unteren Blattes auf EXPANDED .

Ohne dies blieb mein unteres Blatt immer im Status COLLAPSED hängen, wenn die Ansichtshöhe mehr als 0,5 der Bildschirmhöhe beträgt und ich manuell scrollen muss, um das vollständige untere Blatt anzuzeigen.

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

1

Ähnlich wie bei uregentx answer können Sie in kotlin Ihre Fragmentklasse deklarieren, die sich von erstreckt BottomSheetDialogFragment, und beim Erstellen der Ansicht können Sie den Standardstatus des Dialoglisteners festlegen , nachdem der Dialog angezeigt wurde.

STATE_COLLAPSED: Das untere Blatt ist sichtbar, zeigt jedoch nur die Blickhöhe an.

STATE_EXPANDED: Das untere Blatt ist sichtbar und seine maximale Höhe.

STATE_HALF_EXPANDED: Das untere Blatt ist sichtbar, zeigt jedoch nur seine halbe Höhe.

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

Denken Sie daran, die Implementierung des Materialdesigns in Gradle zu verwenden.

implementation "com.google.android.material:material:$version"

Beachten Sie auch die Materialdesign-Referenz Bottom Sheets


Woher kommt die dialog?Variable in onCreateView?
Eric Smith

dialogist eine Eigenschaft der Klasse DialogFragment, ist eigentlich ein Getter. In diesem Beispiel habe ich diesen Getter verwendet, um die aktuelle DialogFragment-Instanz und setOnShowListenerdarauf abzurufen . Möglicherweise haben Sie diese Art von Anweisungen bereits in Ihrem Projekt verwendet, z. B. in einer Aktivität, um auf den Aktionsbalken- actionBarGetter zuzugreifen , sodass Sie diese Komponente beispielsweise ändern könnenactionBar?.subtitle = "abcd"
FJCG

1

Meine Antwort ist mehr oder weniger dieselbe wie die meisten der oben genannten Antworten mit einer geringfügigen Änderung. Anstatt findViewById zu verwenden, um zuerst die untere Blattansicht zu finden, habe ich es vorgezogen, keine Ressourcen-IDs der Framework-Ansicht fest zu codieren, da sie sich in Zukunft möglicherweise ändern.

setOnShowListener(dialog -> {
            BottomSheetBehavior bottomSheetBehavior = ((BottomSheetDialog)dialog).getBehavior();
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        });

1
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return super.onCreateDialog(savedInstanceState).apply {
        setOnShowListener {
            (this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState(
                BottomSheetBehavior.STATE_EXPANDED
            )
        }
    }
}

1

Wenn Sie dies hier an zukünftige Leser senden, wissen wir, dass wir eine andere Lösung verwenden können.

Ich habe versucht, das gleiche Problem zu lösen, das Sie mit a beschrieben haben BottomSheetDialog.

Ich mag es nicht, interne Android-IDs zu verwenden, und ich habe gerade festgestellt, dass es eine Methode gibt BottomSheetDialog getBehavior, die Sie verwenden können:

Sie können dies in Ihrem verwenden BottomSheetDialog:

behavior.state = BottomSheetBehavior.STATE_EXPANDED

Mit BottomSheetDialogFragmentkönnen Sie das gleiche Casting des Dialogs von diesem DialogFragment in ausführen BottomSheetDialog.


1

BottomSheetDialogFragment :

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED
}

oder wenn Sie bereit sind zu zeigen:

private fun onContentLoaded(items: List<Any>) {
    adapter.submitList(items)
    (dialog as? BottomSheetDialog)?.behavior?.state = STATE_EXPANDED
}

0

Überschreiben Sie in Ihrer Kotlin BottomSheetDialogFragment-Klasse onCreateDialog wie folgt

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
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.