BottomSheetDialogFragment durumunu genişletilmiş olarak ayarla


92

Android Destek Tasarım Kitaplığı'nı (v23.2.1) kullanarak BottomSheetDialogFragmentgenişletilmiş bir parçanın durumunu nasıl ayarlarsınız BottomSheetBehavior#setState(STATE_EXPANDED)?

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

Alt sayfalar ilk başta STATE_COLLAPSED olarak ayarlandı. Genişletmek istiyorsanız BottomSheetBehavior # setState (STATE_EXPANDED) öğesini çağırın. Düzenleri görüntülemeden önce yöntemi çağıramayacağınızı unutmayın.

Önerilen uygulama ilk şişirilmiş edilecek bir görünüm gerektirir, ama emin bir parçası üzerine BottomSheetBehaviour belirleriz nasıl değilim ( BottomSheetDialogFragment).

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

Yanıtlar:


221

"Düzenleri görüntülemeden önce yöntemi çağıramayacağınızı unutmayın."

Yukarıdaki metin ipucudur.

İletişim kutularında, iletişim kutusu gösterildiğinde tetiklenen bir dinleyici bulunur . İletişim kutusu düzenlenmemişse gösterilemez.

Bu nedenle, onCreateDialog()modal alt sayfanızda ( BottomSheetFragment), iletişim kutusunu döndürmeden hemen önce (veya herhangi bir yerde, iletişim kutusuna bir referansınız olduğunda), şunu arayın:

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

Benim durumumda, geleneğim BottomSheetşöyle oldu:

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

Bunun yardımcı olup olmadığını bana bildirin.

GÜNCELLEME

Şu şekilde de geçersiz kılabileceğinizi unutmayın 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;
    }
}

Ama üs BottomSheetFragmenta dönüşünden başka bir şey yapmadığından neden birinin bunu yapmak isteyeceğini gerçekten anlamıyorum BottomSheetDialog.

ANDROIDX İÇİN GÜNCELLEME

AndroidX kullanırken, daha önce adresinde bulunan kaynak android.support.design.R.id.design_bottom_sheetartık adresinde bulunabilir com.google.android.material.R.id.design_bottom_sheet.


Teşekkürler, bu yöntemi denedim. O yapar BottomSheetDialogFragmentgenişletilmiş davranışa çökmüş gider olarak (açılış animasyondaki kareleri atlamak için görünür) janky görünür. Düzenleme: Bunu Android Marshmallow ve KitKat cihazlarında test etti
user2560886

1
Benim için mükemmel çalışıyor. Atlamak yok. Bir diyaloğu geri vermekten başka bir şey yapıyor musunuz? Gönderinizi kodunuzla güncellerseniz sevinirim, böylece daha iyi bir fikir edinebilirim.
efemoney

5
android.support.design.RDestek kitaplıklarını güncelledikten sonra paketi bulamıyorum mu?
natario

2
Ayrıca android.support.design.R, @natario gibi çözme ile ilgili sorunlar yaşıyorum. Kullanıyorum implementation "com.google.android.material:material:1.0.0". Ayrıca projede AndroidX kullanıyorum.
hsson

21
AndroidX kullanırken kaynak şu adreste bulunabilir:com.google.android.material.R.id.design_bottom_sheet
acil x

45

Kullanmak istediğiniz takdirde efeturi cevabı, ancak, büyük onCreateView () ile devam aksine, sizin BottomSheet oluşturmak için onCreateDialog () , burada kendi altında eklemesi gerekir kodudur onCreateView () yöntemi:

@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
Alternatif olarak, getDialog'u aramanıza hiç gerek yoktur. Bunu yapmanın en temiz yolunun hem onCreateView hem de onCreateDialog'u geçersiz kılmak olduğunu buluyorum. Görünümünüzü onCreateView'da oluşturun (herhangi bir parçada yapacağınız gibi) ve onCreateDialog'da iletişim kutusuna özel kodu yapın (örneği almak için super.onCreateDialog'u arayın)
Stimsoni

2
Bu günümü kurtarıyor. Teşekkürler.
AndroidRuntimeException

@Stimsoni onCreateView, onCreateDialog kullanılırsa çağrılmaz. developer.android.com/reference/android/support/v4/app/…
Vincent_Paing

1
@Vincent_Paing, Evet öyle. Ekli bağlantınızda 'onCreateView'ün uygulanmasına gerek yok' yazıyor. Çağrılmayacağını söylemiyor. Kaynak koduna buradan github.com/material-components/material-components-android/blob/… bir göz atın . Varsayılan uygulama, alt sayfayı oluşturmak için onCreateDialog'u çağırır ve yukarıdaki her çözüm, her ikisinin de her zaman çağrıldığı anlamına gelen onCreateView'ü kullanmaya devam eder. Geçersiz kılarsanız, yine de super.onCreateDialog () 'u çağırdığınızdan emin olun.
Stimsoni

BottomSheetDialogFragment'de beni onCreateView () 'da çökertiyorum Onu onViewCreated () içine koydum ve mükemmel! teşekkür ederim
avisper

22

Basit ve zarif bir çözüm:

BottomSheetDialogFragment bunu ele almak için alt sınıflandırılabilir:

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

Bu nedenle BottomSheetDialogFragment, kendi alt sayfanızı oluşturmak yerine bu sınıfı genişletin .

Not

Değişim com.google.android.material.R.id.design_bottom_sheetiçin android.support.design.R.id.design_bottom_sheetproje Eski Android desteği kütüphaneleri kullanıyorsa.


1
com.google.android.material.RŞimdi yerine görünüyor android.support.design.R.
EpicPandaForce

@EpicPandaForce Elbette. Google'daki Android ekibi kısa süre önce eski destek kitaplığını yeniden paketledi.
DYS

4

Yukarıdakilerin daha iyi olduğunu düşünüyorum. Ne yazık ki çözmeden önce bu çözümü bulamadım. Ama çözümümü yaz. hepsine oldukça benzer.

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

Ben de aynı sorunla karşılaşıyorum. Çözdüğüm şey bu. Davranış, davranışı elde etmek için kullanılabilen BottomSheetDialog'da gizlidir. Ana düzeninizi CooridateLayout olarak değiştirmek istemiyorsanız, bunu deneyebilirsiniz.

ADIM 1: BottomSheetDialogFragment'ı özelleştirin

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

ADIM 2: Parçanızın bu özelleştirilmiş parçayı genişletmesini sağlayın

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

NullPointException ile tanıştım BottomSheetBehavior.from(bottomSheet)çünkü d.findViewById(android.support.design.R.id.design_bottom_sheet)null döndürüyor.

Bu garip. Bu kod satırını DEBUG modunda Android Monitor'daki Saatler'e ekledim ve Framelayout'u normal şekilde döndürdüğünü gördüm.

wrapInBottomSheetBottomSheetDialog'daki kodu :

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

Bazen R.id.design_bottom_sheetbunun eşit olmadığını buldum android.support.design.R.id.design_bottom_sheet. Farklı R.java'da farklı değerleri vardır.

Değişmek Yani android.support.design.R.id.design_bottom_sheetiçin 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);
            }
        });

Artık NullPointException yok.


3

Uygula BottomsheetDialogFragmentdevlet içinde onResumebu sorunu çözecektir

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

onShow(DialogInterface dialog)ve postDelayedanimasyon arızasına neden olabilir


2

OnShow () kullanılan tüm sonuçlar, yazılım klavyesi görüntülendiğinde rastgele işleme hatasına neden olur. Aşağıdaki ekran görüntüsüne bakın - BottomSheet iletişim kutusu ekranın altında değil, klavyenin görüntülendiği gibi yerleştirilmiştir. Bu sorun her zaman değil, oldukça sık görülür.

görüntü açıklamasını buraya girin

GÜNCELLEME

Özel üye yansıması ile çözümüm gereksizdir. Yumuşak klavyeyi gizledikten sonra iletişim kutusu oluşturmak ve göstermek için postDelayed (yaklaşık 100 ms ile) kullanmak daha iyi bir çözümdür. O zaman onShow () ile yukarıdaki çözümler tamamdır.

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

Bu yüzden başka bir çözüm uyguluyorum, ancak yansımayı kullanmayı gerektiriyor çünkü BottomSheetDialog tüm üyelere özel olarak sahip. Ancak render hatasını çözer. BottomSheetDialogFragment sınıfı, BottomSheetDialog'u oluşturan onCreateDialog yöntemine sahip yalnızca AppCompatDialogFragment'dir. Kendi sınıfımı oluşturan, BottomSheetDialog'u genişleten ve özel davranış üyesine erişimi çözen ve onStart yönteminde STATE_EXPANDED durumuna ayarlayan kendi AppCompatDialogFragment alt öğesini oluşturuyorum.

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

Uyguladığım en kolay yol aşağıdaki gibidir, Burada android.support.design.R.id.design_bottom_sheet bulup alt sayfa durumunu EXPANDED olarak ayarlıyoruz .

Bu olmadan, görünüm yüksekliği ekran yüksekliğinin 0,5'inden fazlaysa ve alt sayfayı tam olarak görüntülemek için el ile kaydırmam gerekiyorsa , alt sayfam her zaman ÇARPILDI durumunda kaldı .

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

Uregentx cevabına benzer şekilde , kotlin'de , içinden uzanan parça sınıfınızı bildirebilirsiniz BottomSheetDialogFragmentve görünüm oluşturulduğunda, iletişim kutusu görüntülendikten sonra iletişim dinleyicisi varsayılan durumunu ayarlayabilirsiniz.

STATE_COLLAPSED: Alt sayfa görünür durumda ancak yalnızca gözetleme yüksekliğini gösteriyor.

STATE_EXPANDED: Alt sayfa görünür ve maksimum yüksekliği.

STATE_HALF_EXPANDED: Alt sayfa görülebilir ancak yalnızca yarı yüksekliğini gösterir.

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

Materyal tasarım uygulamasını kademeli olarak kullanmayı unutmayın.

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

Ayrıca malzeme tasarım referansı Alt Sayfalara da göz atın


Nerede gelmez dialog?değişken onCreateView içinde geliyor?
Eric Smith

dialogsınıfın bir özelliğidir DialogFragment, aslında bir Getter'dir. Bu örnekte, o alıcıyı mevcut DialogFragment örneğini ve setOnShowListenerona almak için kullandım . Projenizde bu tür talimatları zaten kullanmış olabilirsiniz, örneğin bir faaliyette, eylem çubuğu actionBaractionBar?.subtitle = "abcd"
alıcıya

1

Cevabım, küçük bir değişiklikle yukarıdaki cevapların çoğuyla aşağı yukarı aynı. İlk olarak alt sayfa görünümünü bulmak için findViewById kullanmak yerine, gelecekte değişebileceklerinden herhangi bir çerçeve görünümü kaynak kimliğini kodlamamayı tercih ettim.

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

Başka bir çözüm kullanabileceğimizi bildiğimizden, bunu gelecekteki okuyuculara burada yayınlamak.

A ile tarif ettiğiniz problemin aynısını çözmeye çalışıyordum BottomSheetDialog.

Dahili Android kimliklerini kullanmayı sevmiyorum ve içinde BottomSheetDialog getBehaviorkullanabileceğiniz bir yöntem buldum :

Bunu içinde kullanabilirsiniz BottomSheetDialog:

behavior.state = BottomSheetBehavior.STATE_EXPANDED

BottomSheetDialogFragmentBunu kullanarak , iletişim kutusunun o DialogFragment'a aktarımını yapabilirsiniz BottomSheetDialog.


1

BottomSheetDialogFragment :

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

veya göstermeye hazır olduğunda:

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

0

Kotlin BottomSheetDialogFragment sınıfınızda, onCreateDialog'u aşağıdaki gibi geçersiz kılı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
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.