Setel status BottomSheetDialogFragment menjadi diperluas


92

Bagaimana Anda menyetel status fragmen yang diperluas BottomSheetDialogFragmentmenjadi diperluas menggunakan BottomSheetBehavior#setState(STATE_EXPANDED)Android Support Design Library (v23.2.1)?

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

Sheet bawah disetel ke STATE_COLLAPSED pada awalnya. Panggil BottomSheetBehavior # setState (STATE_EXPANDED) jika Anda ingin memperluasnya. Perhatikan bahwa Anda tidak dapat memanggil metode ini sebelum melihat tata letak.

The praktek menyarankan memerlukan pandangan akan meningkat pertama, tapi aku tidak yakin bagaimana saya akan mengatur BottomSheetBehaviour ke fragmen ( BottomSheetDialogFragment).

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

Jawaban:


222

"Perhatikan bahwa Anda tidak dapat memanggil metode sebelum melihat tata letak."

Teks di atas adalah petunjuknya.

Dialog memiliki pendengar yang diaktifkan setelah dialog ditampilkan . Dialog tidak dapat ditampilkan jika tidak ditata.

Jadi, di onCreateDialog()modal lembar bawah Anda ( BottomSheetFragment), tepat sebelum mengembalikan dialog (atau di mana saja, setelah Anda memiliki referensi ke dialog), panggil:

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

Dalam kasus saya, kebiasaan saya BottomSheetternyata:

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

Beri tahu saya jika ini membantu.

MEMPERBARUI

Perhatikan bahwa Anda juga dapat mengganti BottomSheetDialogFragmentsebagai:

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

Tapi saya benar-benar tidak melihat mengapa ada orang yang ingin melakukan itu karena pangkalan BottomSheetFragmenttidak melakukan apa pun selain mengembalikan a BottomSheetDialog.

UPDATE UNTUK ANDROIDX

Saat menggunakan AndroidX, sumber daya yang sebelumnya ditemukan di android.support.design.R.id.design_bottom_sheetsekarang dapat ditemukan di com.google.android.material.R.id.design_bottom_sheet.


Terima kasih, saya mencoba metode ini. Itu membuat tampilan BottomSheetDialogFragmenttersendat (tampak melewatkan bingkai dalam animasi pembuka) saat beralih dari perilaku diciutkan ke diperluas. Sunting: Diuji ini pada perangkat Android Marshmallow dan KitKat
pengguna2560886

1
Ini bekerja dengan sempurna untuk saya. Tidak boleh melewatkan. Apakah Anda melakukan hal lain selain hanya mengembalikan dialog? Akan sangat menghargai jika Anda memperbarui posting Anda dengan kode Anda sehingga saya dapat memiliki Ide yang lebih baik.
efemoney

5
Apakah hanya saya yang tidak dapat menemukan paket android.support.design.Rsetelah memperbarui pustaka dukungan?
natario

2
Saya juga mengalami masalah dalam mengatasinya android.support.design.R, seperti @natario. Saya menggunakan implementation "com.google.android.material:material:1.0.0". Saya juga menggunakan AndroidX dalam proyek ini.
hsson

21
Saat menggunakan AndroidX, sumber daya dapat ditemukan dicom.google.android.material.R.id.design_bottom_sheet
urgenx

45

jawaban efeturi bagus, namun, jika Anda ingin menggunakan onCreateView () untuk membuat BottomSheet Anda, sebagai lawan menggunakan onCreateDialog () , berikut adalah kode yang perlu Anda tambahkan di bawah metode 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);
}

3
Alternatifnya, Anda tidak perlu menelepon getDialog sama sekali. Saya menemukan cara terbersih untuk melakukannya adalah dengan mengganti onCreateView dan onCreateDialog. Buat tampilan Anda di onCreateView (seperti yang Anda lakukan dengan fragmen apa pun) dan lakukan kode khusus dialog di onCreateDialog (panggil super.onCreateDialog untuk mendapatkan instance)
Stimsoni

2
Ini menyelamatkan hariku. Terima kasih.
AndroidRuntimeException

@Stimsoni onCreateView tidak dipanggil jika onCreateDialog digunakan. developer.android.com/reference/android/support/v4/app/…
Vincent_Paing

1
@Vincent_Paing, Ya. Di tautan terlampir Anda dikatakan 'onCreateView tidak perlu diimplementasikan'. Ia tidak mengatakan itu tidak akan dipanggil. Lihat kode sumbernya di sini github.com/material-components/material-components-android/blob/… . Implementasi default memanggil onCreateDialog untuk membuat sheet bawah dan setiap solusi di atas masih menggunakan onCreateView yang berarti keduanya selalu dipanggil. Pastikan Anda masih memanggil super.onCreateDialog () jika Anda menimpanya.
Stimsoni

di BottomSheetDialogFragment itu membuat saya crash di onCreateView () Saya meletakkannya di onViewCreated () dan itu sempurna! terima kasih
avisper

22

Solusi sederhana dan elegan:

BottomSheetDialogFragment bisa menjadi subclass untuk mengatasi ini:

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

Jadi perluas kelas ini daripada BottomSheetDialogFragmentmembuat lembar bawah Anda sendiri.

Catatan

Ubah com.google.android.material.R.id.design_bottom_sheetke android.support.design.R.id.design_bottom_sheetjika proyek Anda menggunakan pustaka dukungan Android lama.


1
Sepertinya com.google.android.material.Rsekarang, bukan android.support.design.R.
EpicPandaForce

@EpicPandaForce Tentu. Tim Android di Google baru-baru ini mengemas ulang pustaka dukungan sebelumnya.
DYS

4

Saya pikir yang di atas lebih baik. Sayangnya saya tidak menemukan solusi itu sebelum saya menyelesaikannya. Tapi tulis solusi saya. sangat mirip dengan semua.

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

Saya menghadapi masalah yang sama. Inilah yang saya pecahkan. Perilaku disembunyikan di BottomSheetDialog, yang tersedia untuk mendapatkan perilaku Jika Anda tidak ingin mengubah tata letak induk menjadi CooridateLayout, Anda dapat mencoba ini.

LANGKAH 1: sesuaikan 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
   }
}

LANGKAH 2: buat fragmen Anda memperluas fragmen yang disesuaikan ini

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

Saya bertemu NullPointException BottomSheetBehavior.from(bottomSheet)karena d.findViewById(android.support.design.R.id.design_bottom_sheet)mengembalikan null.

Ini aneh. Saya menambahkan baris kode ini ke Jam di Android Monitor dalam mode DEBUG dan menemukan itu mengembalikan Framelayout secara normal.

Berikut kode wrapInBottomSheetdi 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;
    }

Kadang-kadang, saya menemukan bahwa R.id.design_bottom_sheetitu tidak sama dengan android.support.design.R.id.design_bottom_sheet. Mereka memiliki nilai yang berbeda pada R.java yang berbeda.

Jadi saya berubah android.support.design.R.id.design_bottom_sheetmenjadi 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);
            }
        });

Tidak ada lagi NullPointException sekarang.


3

Terapkan BottomsheetDialogFragmentnegara bagian onResumeakan menyelesaikan masalah ini

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

onShow(DialogInterface dialog)dan postDelayeddapat menyebabkan kesalahan animasi


2

Semua hasil dengan menggunakan onShow () menyebabkan bug render acak saat keyboard lunak ditampilkan. Lihat tangkapan layar di bawah - Dialog BottomSheet tidak berada di bagian bawah layar tetapi ditempatkan seperti keyboard ditampilkan. Masalah ini tidak selalu terjadi tetapi cukup sering.

masukkan deskripsi gambar di sini

MEMPERBARUI

Solusi saya dengan refleksi anggota pribadi tidak perlu. Menggunakan postDelayed (dengan sekitar 100 ms) untuk membuat dan menampilkan dialog setelah sembunyikan keyboard lunak adalah solusi yang lebih baik. Maka solusi di atas dengan onShow () tidak masalah.

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

Jadi saya menerapkan solusi lain, tetapi memerlukan penggunaan refleksi, karena BottomSheetDialog memiliki semua anggota sebagai pribadi. Tapi itu memecahkan bug render. Kelas BottomSheetDialogFragment hanya AppCompatDialogFragment dengan metode onCreateDialog yang membuat BottomSheetDialog. Saya membuat anak AppCompatDialogFragment sendiri yang membuat kelas saya memperluas BottomSheetDialog dan yang memecahkan akses ke anggota perilaku pribadi dan menetapkannya dalam metode onStart ke 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

Cara termudah yang saya terapkan adalah seperti di bawah ini, Di sini kita menemukan android.support.design.R.id.design_bottom_sheet dan mengatur status lembar bawah sebagai DIPERLUAS .

Tanpa ini, lembaran bawah saya selalu macet dalam keadaan TERTUTUP jika tinggi tampilan lebih dari 0,5 tinggi layar dan saya harus menggulir secara manual untuk melihat lembaran bawah penuh.

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

Mirip dengan jawaban uregentx , di kotlin , Anda dapat mendeklarasikan kelas fragmen yang merupakan turunan dari BottomSheetDialogFragment, dan saat tampilan dibuat, Anda dapat menyetel status default pendengar dialog setelah dialog ditampilkan.

STATE_COLLAPSED: Sheet bawah terlihat tetapi hanya menunjukkan ketinggian intipnya.

STATE_EXPANDED: Lembaran bawah terlihat dan tinggi maksimumnya.

STATE_HALF_EXPANDED: Sheet bagian bawah terlihat tetapi hanya menunjukkan tinggi setengahnya.

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

Ingat menggunakan implementasi desain material di gradle.

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

Lihat juga referensi desain material Lembaran Bawah


Dari mana dialog?asalnya variabel di onCreateView?
Eric Smith

dialogadalah properti kelas DialogFragment, sebenarnya adalah seorang Getter. Dalam contoh ini saya menggunakan getter itu untuk mendapatkan instance DialogFragment saat ini dan setOnShowListeneruntuk itu. Mungkin Anda telah menggunakan instruksi semacam itu dalam proyek Anda, misalnya dalam suatu aktivitas, untuk mengakses actionBarpengambil bilah tindakan digunakan, sehingga Anda dapat memodifikasi komponen itu, misalnyaactionBar?.subtitle = "abcd"
FJCG

1

Jawaban saya kurang lebih sama dengan kebanyakan jawaban diatas dengan sedikit modifikasi. Alih-alih menggunakan findViewById untuk pertama kali menemukan tampilan lembar bawah, saya lebih suka untuk tidak membuat kode keras id sumber daya tampilan kerangka kerja karena mereka mungkin berubah di masa depan.

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

Posting ini di sini untuk pembaca yang akan datang, karena saya pikir kita bisa menggunakan solusi lain.

Saya mencoba memecahkan masalah yang sama yang Anda gambarkan dengan file BottomSheetDialog.

Saya tidak suka menggunakan id Android internal dan saya baru saja menemukan ada metode di dalamnya BottomSheetDialog getBehavioryang dapat Anda gunakan:

Anda dapat menggunakan ini di dalam BottomSheetDialog:

behavior.state = BottomSheetBehavior.STATE_EXPANDED

Menggunakan BottomSheetDialogFragmentAnda dapat melakukan casting yang sama dialog dari DialogFragment itu ke BottomSheetDialog.


1

BottomSheetDialogFragment :

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

atau saat siap untuk ditampilkan:

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

0

Di kelas Kotlin BottomSheetDialogFragment Anda, ganti onCreateDialog seperti di bawah ini

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
    }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.