【问题标题】:Set state of BottomSheetDialogFragment to expanded将 BottomSheetDialogFragment 的状态设置为展开
【发布时间】:2016-06-26 12:50:18
【问题描述】:

如何使用 Android 支持设计库 (v23.2.1) 将扩展 BottomSheetDialogFragment 的片段的状态设置为使用 BottomSheetBehavior#setState(STATE_EXPANDED) 扩展?

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

首先将底页设置为 STATE_COLLAPSED。如果要扩展它,请调用 BottomSheetBehavior#setState(STATE_EXPANDED)。请注意,您不能在视图布局之前调用该方法。

suggested practice 需要先膨胀视图,但我不确定如何将 BottomSheetBehaviour 设置到片段 (BottomSheetDialogFragment) 上。

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

【问题讨论】:

    标签: android android-fragments android-support-library android-support-design


    【解决方案1】:

    “请注意,您不能在视图布局之前调用该方法。”

    以上文字为线索。

    对话框有一个监听器,一旦对话框显示就会触发。如果没有布局,对话框将无法显示。

    因此,在模态底部工作表 (BottomSheetFragment) 的 onCreateDialog() 中,就在返回对话框之前(或任何地方,一旦你引用了对话框),调用:

    // 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 从折叠行为变为展开行为时,它会使 BottomSheetDialogFragment 出现卡顿(似乎在开始动画中跳过帧)。编辑:在 Android Marshmallow 和 KitKat 设备上对此进行了测试
    • 它非常适合我。没有跳过。除了返回一个对话框之外,你还在做其他事情吗?如果你用你的代码更新你的帖子,我会很感激,这样我就有更好的想法。
    • 只是我在更新支持库后找不到包android.support.design.R
    • 我在解析android.support.design.R 时也遇到了问题,就像@natario 一样。我正在使用implementation "com.google.android.material:material:1.0.0"。我也在项目中使用AndroidX。
    • 使用 AndroidX 时可以在com.google.android.material.R.id.design_bottom_sheet找到资源
    【解决方案2】:

    efeturi 的答案很好,但是,如果您想使用 onCreateView() 来创建您的 BottomSheet,而不是使用 onCreateDialog() em>,这是您需要在 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);
    }
    

    【讨论】:

    • 另外,您根本不需要调用 getDialog。我发现最干净的方法是同时覆盖 onCreateView 和 onCreateDialog。在 onCreateView 中构建视图(就像使用任何片段一样)并在 onCreateDialog 中执行特定于对话框的代码(调用 super.onCreateDialog 以获取实例)
    • 这可以节省我的时间。谢谢。
    • @Stimsoni onCreateView 如果使用 onCreateDialog 则不会被调用。 developer.android.com/reference/android/support/v4/app/…
    • @Vincent_Paing,是的。在您附加的链接中,它说“不需要实施 onCreateView”。它没有说它不会被调用。看看这里的源代码github.com/material-components/material-components-android/blob/…。默认实现调用 onCreateDialog 来创建底部工作表,上面的每个解决方案仍在使用 onCreateView ,这意味着它们总是被调用。只要确保你仍然调用 super.onCreateDialog() 如果你覆盖它。
    • 在 BottomSheetDialogFragment 它让我在 onCreateView() 中崩溃我把它放在 onViewCreated() 中,它是完美的!谢谢
    【解决方案3】:

    一个简单而优雅的解决方案:

    BottomSheetDialogFragment 可以被子类化来解决这个问题:

    class NonCollapsableBottomSheetDialogFragment extends BottomSheetDialogFragment {
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
    
            dialog.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 dialog;
        }
    }
    

    所以扩展这个类而不是 BottomSheetDialogFragment 来创建你自己的底页。

    注意

    如果您的项目使用旧的 Android 支持库,请将 com.google.android.material.R.id.design_bottom_sheet 更改为 android.support.design.R.id.design_bottom_sheet

    【讨论】:

    • 现在似乎是 com.google.android.material.R 而不是 android.support.design.R
    • @EpicPandaForce 当然。 Google 的 Android 团队最近重新打包了以前的支​​持库。
    【解决方案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 = BottomSheetBehavior.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()
       }
    }
    

    【讨论】:

      【解决方案5】:

      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
      }
      

      【讨论】:

        【解决方案6】:

        我的答案与上述大多数答案或多或少相同,但略有修改。我不想使用 findViewById 来首先找到底部的工作表视图,而是更喜欢不要对任何框架视图资源 ID 进行硬编码,因为它们将来可能会发生变化。

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

        【讨论】:

          【解决方案7】:
          override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
              return super.onCreateDialog(savedInstanceState).apply {
                  setOnShowListener {
                      (this@TipsBottomDialogFragment.dialog as BottomSheetDialog).behavior.setState(
                          BottomSheetBehavior.STATE_EXPANDED
                      )
                  }
              }
          }
          

          【讨论】:

            【解决方案8】:

            在这里发布给未来的读者,因为我认为现在我们可以使用另一种解决方案。

            我试图解决您使用BottomSheetDialog 描述的相同问题。

            我不喜欢使用内部 Android id,我刚刚发现 BottomSheetDialog getBehavior 中有一个可以使用的方法:

            您可以在 BottomSheetDialog 中使用它:

            behavior.state = BottomSheetBehavior.STATE_EXPANDED

            使用BottomSheetDialogFragment,您可以将对话框从 DialogFragment 转换为 BottomSheetDialog

            【讨论】:

              【解决方案9】:
              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);
                          }
                      });
              

              我在BottomSheetBehavior.from(bottomSheet) 中遇到了 NullPointException,因为d.findViewById(android.support.design.R.id.design_bottom_sheet) 返回 null。

              这很奇怪。我在DEBUG模式下将这行代码添加到Android Monitor的Watches中,发现它正常返回Framelayout。

              这是BottomSheetDialog中wrapInBottomSheet的代码:

              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_sheet 更改为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);
                          }
                      });
              

              现在不再有 NullPointException。

              【讨论】:

                【解决方案10】:

                在 Kotlin 中,在 onStart()BottomSheetDialogFragment 中添加以下行

                (dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
                

                【讨论】:

                  【解决方案11】:

                  onResume 中应用BottomsheetDialogFragment 状态将解决此问题

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

                  onShow(DialogInterface dialog)postDelayed 可能会导致动画故障

                  【讨论】:

                    【解决方案12】:

                    在您的 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
                        }
                    

                    【讨论】:

                      【解决方案13】:

                      使用 onShow() 的所有结果在显示软键盘时都会导致随机渲染错误。请参阅下面的屏幕截图 - BottomSheet 对话框不在屏幕底部,而是像显示键盘一样放置。这个问题并不总是发生,而是经常发生。

                      更新

                      我的私有成员反射解决方案是不必要的。在隐藏软键盘后使用 postDelayed(大约 100 毫秒)创建和显示对话框是一个更好的解决方案。那么上面有onShow()的解决方案就ok了。

                      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 类只是 AppCompatDialogFragment 具有创建 BottomSheetDialog 的 onCreateDialog 方法。我创建了自己的 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());
                          }
                      
                          ....
                      }
                      

                      【讨论】:

                        【解决方案14】:

                        您可以执行以下操作(Kotlin 版本):

                        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
                            super.onViewCreated(view, savedInstanceState)
                            dialog?.let {
                                val sheet = it as BottomSheetDialog
                                sheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
                            }
                        
                            // rest of your stuff
                        }
                        

                        【讨论】:

                          【解决方案15】:

                          类似于 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"

                          还可以查看材料设计参考Bottom Sheets

                          【讨论】:

                          • onCreateView 中dialog? 变量从何而来?
                          • dialogDialogFragment类的属性,实际上是一个Getter。在此示例中,我使用该 getter 获取当前的 DialogFragment 实例和 setOnShowListener 。可能你已经在你的项目中使用了这种指令,例如在一个活动中,访问操作栏actionBar使用了getter,所以你可以修改那个组件,例如actionBar?.subtitle = "abcd"
                          【解决方案16】:

                          我实现的最简单的方法如下,在这里我们找到 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
                              }
                          }
                          

                          【讨论】:

                            【解决方案17】:

                            这是一个非常简洁的 Kotlin 解决方案,效果很好。

                            override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
                                return (super.onCreateDialog(savedInstanceState) as BottomSheetDialog).apply {
                                    setOnShowListener {
                                       behavior.state = BottomSheetBehavior.STATE_EXPANDED
                                    }
                                }
                            }
                            

                            【讨论】:

                              【解决方案18】:

                              根据下面链接的回复,这对我有用。

                              behavior = BottomSheetBehavior.from(bottomSheet1);
                              if(action.equals("post") ) {
                                  behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                                  behavior.setDraggable(false); // disable dragging
                              }
                              

                              enter link description here

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 2011-06-23
                                • 1970-01-01
                                • 2020-11-02
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2017-08-17
                                相关资源
                                最近更新 更多