BottomSheetDialogFragmentの状態を展開に設定します


97

Android Support Design Library(v23.2.1)BottomSheetDialogFragmentBottomSheetBehavior#setState(STATE_EXPANDED)使用して、拡張されたフラグメントの状態をどのように設定しますか?

https://code.google.com/p/android/issues/detail?id=202396のコメント:

下部のシートは、最初はSTATE_COLLAPSEDに設定されています。拡張する場合は、BottomSheetBehavior#setState(STATE_EXPANDED)を呼び出します。ビューレイアウトの前にメソッドを呼び出すことはできないことに注意してください。

提案の練習は(最初に膨張するビューが必要ですが、私は私がフラグメントにBottomSheetBehaviourを設定しますかどうかはわかりませんBottomSheetDialogFragment)。

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

回答:


233

「ビューレイアウトの前にメソッドを呼び出すことはできないことに注意してください。」

上記のテキストが手がかりです。

ダイアログには、ダイアログが表示されると起動されるリスナーがあります。レイアウトされていない場合、ダイアログは表示されません。

したがって、onCreateDialog()モーダルボトムシート(BottomSheetFragment)の、ダイアログを返す直前(または、ダイアログへの参照がある場合はどこでも)を呼び出します。

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

私の場合、私の習慣BottomSheetは次のようになりました。

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

これが役立つかどうか教えてください。

更新

次のようにオーバーライドすることもできることに注意してください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;
    }
}

しかし、ベースBottomSheetFragmentはを返す以外に何もしないので、なぜ誰もがそれをしたいと思うのか私は本当にわかりませんBottomSheetDialog

ANDROIDXのアップデート

AndroidXを使用する場合、以前はで見つかったリソースandroid.support.design.R.id.design_bottom_sheetがで見つかるようになりましたcom.google.android.material.R.id.design_bottom_sheet


おかげで、私はこの方法を試しました。BottomSheetDialogFragment折りたたまれた動作から展開された動作に移行するときに、見た目がぎくしゃくします(オープニングアニメーションのフレームをスキップするように見えます)。編集:Android MarshmallowおよびKitKatデバイスでこれをテスト
user2560886 2016年

1
それは私にとって完璧に機能します。スキップなし。ダイアログを返す以外に何かしていることはありますか?あなたがあなたのコードであなたの投稿を更新してくれれば幸いです。
efemoney 2016年

5
android.support.design.Rサポートライブラリを更新した後、パッケージが見つからないのは私だけですか?
natario 2017

2
android.support.design.R@natarioと同じように、解決にも問題があります。私はを使用していimplementation "com.google.android.material:material:1.0.0"ます。プロジェクトではAndroidXも使用しています。
hsson 2018年

23
AndroidXを使用している場合、リソースはcom.google.android.material.R.id.design_bottom_sheet
urgentx 2018年

46

efeturiの答えは素晴らしいですあなたが使用したい場合は、しかし、onCreateViewを()と一緒に行くとは反対に、あなたのBottomSheetを作成するためにonCreateDialog() 、ここでは、あなたの下に追加する必要がありますコードです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
または、getDialogを呼び出す必要はまったくありません。これを行う最もクリーンな方法は、onCreateViewとonCreateDialogの両方をオーバーライドすることです。(あなたが任意のフラグメントと同じように)onCreateViewであなたのビューを構築し、(インスタンスを取得するための呼び出しsuper.onCreateDialog)onCreateDialogダイアログ固有のコードを実行します
Stimsoni

2
これは私の日を救います。ありがとう。
AndroidRuntimeException 2017年

onCreateDialogが使用されている場合、@ StimsonionCreateViewは呼び出されません。 developer.android.com/reference/android/support/v4/app/...
Vincent_Paing

1
@Vincent_Paing、そうです。添付のリンクには、「onCreateViewを実装する必要はありません」と書かれています。呼ばれないとは言っていません。ここでソースコードを見てくださいgithub.com/material-components/material-components-android/blob/…。デフォルトの実装では、onCreateDialogを呼び出してボトムシートを作成しますが、上記のすべてのソリューションは引き続きonCreateViewを使用しているため、両方とも常に呼び出されます。オーバーライドする場合は、super.onCreateDialog()を呼び出すようにしてください。
Stimsoni

BottomSheetDialogFragmentでは、onCreateView()でクラッシュします。onViewCreated()に入れると、完璧です。ありがとう
avisper

21

シンプルでエレガントなソリューション:

BottomSheetDialogFragment これに対処するためにサブクラス化することができます:

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

したがってBottomSheetDialogFragment、独自のボトムシートを作成する代わりに、このクラスを拡張してください。

注意

プロジェクトで古いAndroidサポートライブラリを使用com.google.android.material.R.id.design_bottom_sheetするandroid.support.design.R.id.design_bottom_sheet場合に変更します。


1
com.google.android.material.R代わりになりましたandroid.support.design.R
EpicPandaForce

@EpicPandaForceもちろんです。GoogleのAndroidチームは最近、以前のサポートライブラリを再パッケージ化しました。
DYS

4

上記のものの方が良いと思います。悲しいことに、私は解決する前にそれらの解決策を見つけられませんでした。しかし、私の解決策を書いてください。すべてに非常に似ています。

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

私は同じ問題に直面しています。これが私が解決したことです。ビヘイビアはBottomSheetDialogに隠されており、ビヘイビアを取得するために使用できます。親レイアウトをCooridateLayoutに変更したくない場合は、これを試すことができます。

ステップ1: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
   }
}

ステップ2:フラグメントにこのカスタマイズされたフラグメントを拡張させる

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

nullを返すBottomSheetBehavior.from(bottomSheet)ため、でNullPointExceptionに遭遇しましたd.findViewById(android.support.design.R.id.design_bottom_sheet)

おかしいです。このコード行をAndroidモニターの時計にデバッグモードで追加すると、Framelayoutが正常に返されることがわかりました。

wrapInBottomSheetBottomSheetDialogのコードは次のとおりです。

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

時折、私はそれR.id.design_bottom_sheetがに等しくないことに気づきましたandroid.support.design.R.id.design_bottom_sheet。それらは異なるR.javaで異なる値を持っています。

だから私はに変更android.support.design.R.id.design_bottom_sheetR.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);
            }
        });

NullPointExceptionはもうありません。


3

BottomsheetDialogFragment状態を適用onResumeすると、この問題が解決します

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

onShow(DialogInterface dialog)そして、postDelayedアニメーションの不具合を引き起こす可能性があり


2

onShow()を使用したすべての結果は、ソフトキーボードが表示されたときにランダムなレンダリングバグを引き起こします。以下のスクリーンショットを参照してください-BottomSheetダイアログは画面の下部にはありませんが、キーボードが表示されたように配置されます。この問題は常に発生するわけではありませんが、頻繁に発生します。

ここに画像の説明を入力してください

更新

個人会員を反映した私の解決策は不要です。ソフトキーボードを非表示にした後にダイアログを作成および表示するためにpostDelayed(約100ミリ秒)を使用することは、より良い解決策です。次に、onShow()を使用した上記のソリューションは問題ありません。

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

したがって、他のソリューションを実装しますが、BottomSheetDialogにはすべてのメンバーがプライベートとして含まれているため、リフレクションを使用する必要があります。しかし、それはレンダリングのバグを解決します。BottomSheetDialogFragmentクラスは、BottomSheetDialogを作成するonCreateDialogメソッドを持つAppCompatDialogFragmentのみです。AppCompatDialogFragmentの子を作成し、クラスを作成してBottomSheetDialogを拡張し、プライベート動作メンバーへのアクセスを解決して、onStartメソッドで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());
    }

    ....
}

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

2

これを将来の読者に投稿してください。今は別のソリューションを使用できると思います。

私はあなたがで説明したのと同じ問題を解決しようとしていましたBottomSheetDialog

内部AndroidIDを使用するのは好きではありませんが、内部にBottomSheetDialog getBehavior使用できるメソッドがあることがわかりました。

あなたはあなたの中でこれを使うことができますBottomSheetDialog

behavior.state = BottomSheetBehavior.STATE_EXPANDED

を使用しBottomSheetDialogFragmentて、そのDialogFragmentからにダイアログをキャストするのと同じことを行うことができますBottomSheetDialog


1

私が実装した最も簡単な方法は次のとおりです。ここでは、android.support.design.R.id.design_bottom_sheetを見つけ、ボトムシートの状態をEXPANDEDとして設定しています

これがないと、ビューの高さが画面の高さの0.5を超える場合、下部のシートは常にCOLLAPSED状態のままになり、完全な下部のシートを表示するには手動でスクロールする必要があります。

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の回答と同様に、kotlinでは、から拡張するフラグメントクラスを宣言できBottomSheetDialogFragment、ビューの作成時に、ダイアログが表示された後のダイアログリスナーのデフォルト状態を設定できます。

STATE_COLLAPSED:下部のシートは表示されますが、ピークの高さのみが表示されます。

STATE_EXPANDED:一番下のシートが表示され、その最大の高さ。

STATE_HALF_EXPANDED:下部のシートは表示されますが、半分の高さしか表示されません。

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

Gradleでマテリアルデザインの実装を使用することを忘れないでください。

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

マテリアルデザインリファレンスのボトムシートもご覧ください


dialog?変数はonCreateViewのどこから来ていますか?
エリックスミス

dialogはクラスのプロパティでありDialogFragment、実際にはゲッターです。この例では、そのゲッターを使用して、現在のDialogFragmentインスタンスを取得しますsetOnShowListener。あなたは既にバーアクションへのアクセスに、活動に例えば、プロジェクト内の命令のようなものを、使用していることがありactionBarます。たとえば、そのコンポーネントを変更することができますので、ゲッターが使用されているactionBar?.subtitle = "abcd"
FJCG

1

私の答えは、上記のほとんどの答えとほぼ同じですが、少し変更が加えられています。findViewByIdを使用して最初に下部のシートビューを見つける代わりに、フレームワークビューのリソースIDは将来変更される可能性があるため、ハードコーディングしないことをお勧めします。

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

1

BottomSheetDialogFragment

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

または表示する準備ができたら:

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

1

Kotlin BottomSheetDialogFragmentクラスで、以下のようにonCreateDialogをオーバーライドします

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
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.