【问题标题】:Dynamically change height of BottomSheetBehavior动态改变 BottomSheetBehavior 的高度
【发布时间】:2016-02-28 17:17:54
【问题描述】:

我正在使用来自 Google 最近发布的 AppCompat v23.2 的 BottomSheetBehavior。我的底部表格的高度取决于底部表格内显示的内容(类似于 Google 在他们的地图应用中所做的)。

它适用于最初加载的数据,但我的应用程序会更改运行时显示的内容,当这种情况发生时,底部工作表会保留在其旧高度,这会导致底部未使用空间或视图被切掉。

有没有办法通知底部工作表布局重新计算用于展开状态的高度(当ViewGroup 的高度设置为MATCH_HEIGHT 时)或任何手动设置所需高度的方法?


编辑:我还尝试在ViewGroup 及其父级上手动调用invalidate(),但没有任何成功。

【问题讨论】:

  • 您可以将视图高度设置为 wrap_parent,然后在加载内容后使视图无效
  • 视图高度设置为 wrap_parent,但我的问题是,一旦视图无效,它就不会重新计算它的高度,底部工作表会保持原样
  • 查看BottomSheetBehavior的代码,它在CoordinatorLayout调用的onLayoutChild方法中计算高度。您是否尝试过通过调用 requestLayout() 来使其无效?
  • code.google.com/p/android/issues/detail?id=205226 这里报告了状态为 FutureRelease 的问题。所以也许他们有一天会修好。

标签: android android-appcompat


【解决方案1】:

RelativeLayout 与我的底页有同样的问题。高度不会重新计算。我不得不通过重新计算的新值来设置高度并调用BottomSheetBehavior.onLayoutChild

这是我的临时解决方案:

coordinatorLayout = (CoordinatorLayout)findViewById(R.id.coordinator_layout);
bottomSheet = findViewById(R.id.bottom_sheet);

int accountHeight = accountTextView.getHeight();
accountTextView.setVisibility(View.GONE);

bottomSheet.getLayoutParams().height = bottomSheet.getHeight() - accountHeight;
bottomSheet.requestLayout();
behavior.onLayoutChild(coordinatorLayout, bottomSheet, ViewCompat.LAYOUT_DIRECTION_LTR);

【讨论】:

  • 我很想看到一个更好的解决方案,但现在这似乎是唯一的方法。
  • 对我来说 bottomSheet.requestLayout() 在改变视图后就足够了
  • 收回我之前的评论,instant-run在骗我
  • 如果你得到了 LayoutParams,首先修改它们并重新设置它们,那么 requestLayout 将在内部为你调用。
  • 我在 onCreateView 中使用它,而 setPeekHeight、requestLayout 和 setState 没有做任何事情。我应该把这段代码放在哪里?
【解决方案2】:

您可以为此使用BottomSheetBehavior#setPeekHeight

FrameLayout bottomSheet = (FrameLayout) findViewById(R.id.bottom_sheet);
BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setPeekHeight(newHeight);

这不会自动将底部工作表移动到窥视高度。您可以致电BottomSheetBehavior#setState 将您的底页调整为新的窥视高度。

behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);

【讨论】:

  • 不,我正在寻找的是扩展尺寸。我已经在使用具有窥视高度的折叠状态。但是,我的窥视大小始终限于预览标题,扩展大小取决于可用内容的数量。 (例如对象的描述。)
  • 我在 onCreateView 中使用它,而 setPeekHeight 和 setState 没有做任何事情。我应该把这段代码放在哪里?
【解决方案3】:

虽然该问题已在 >=24.0.0 支持库中得到解决,但如果由于某种原因您仍需要使用旧版本,这里有一个解决方法。

mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull final View bottomSheet, int newState) {
            bottomSheet.post(new Runnable() {
                @Override
                public void run() {
                    //workaround for the bottomsheet  bug
                    bottomSheet.requestLayout();
                    bottomSheet.invalidate();
                }
            });
        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        }
    });

【讨论】:

    【解决方案4】:

    对于底部工作表对话框片段,请阅读:Bottom Sheet Dialog Fragment Expand Full Height

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
       super.onActivityCreated(savedInstanceState);
    
       BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
    
       FrameLayout bottomSheet = dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
       BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
       behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
       behavior.setPeekHeight(0);
    }
    

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题,当尝试根据其内容更新窥视高度时,找到了之前布局的高度。这是有道理的,因为新的布局还没有发生。通过在 UI 线程上发布,在新布局之后计算布局高度,并发出另一个布局请求以将底部工作表更新到正确的高度。

      void show() {
          setVisibility(View.VISIBLE);
          post(new Runnable() {
              @Override
              public void run() {
                  mBottomSheetBehavior.setPeekHeight(findViewById(R.id.sheetPeek).getHeight());
                  requestLayout();
              }
          })
      }
      

      【讨论】:

        【解决方案6】:

        当我在 BottomSheet 中使用 recyclerview 并且项目动态更改时,我遇到了同样的问题。正如@sosite 在他的评论中提到的那样,该问题已记录在案,并且他们已在最新版本中修复了该问题。 Issue log here

        只需将您的设计支持库更新到版本 24.0.0 并检查。

        【讨论】:

        • 我遇到了同样的问题,我正在使用新的 AndroidX 库
        【解决方案7】:

        我听从了@HaraldUnander 的建议,它给了我一个切实可行的想法。如果您在以编程方式将BottomSheetBehavior.state 设置为STATE_COLLAPSED 之后运行一个线程(无法像他一样使用post 方法),那么您已经可以获得视图的高度并设置peekHeight 取决于关于它的内容。

        所以首先你设置BottomSheetBehavior:

        BottomSheetBehavior.from(routeCaptionBottomSheet).state = BottomSheetBehavior.STATE_COLLAPSED
        

        然后动态设置 peekHeight:

        thread {
            activity?.runOnUiThread {
                val dynamicHeight = yourContainerView.height
                BottomSheetBehavior.from(bottomSheetView).peekHeight = dynamicHeight
            }
        }
        

        如果使用 Java(我将 Kotlin 和 Anko 用于线程),可以这样做:

        new Thread(new Runnable() {
            public void run() {
                int dynamicHeight = yourContainerView.getHeight();
                BottomSheetBehavior.from(bottomSheetView).setPeekHeight(dynamicHeight);
            }
        }).start();
        

        【讨论】:

          【解决方案8】:

          下面的代码 sn-p 帮助我解决了这个问题,我在布局中不同视图的可见性之间切换,并且底部工作表的高度自动更改。

          override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
              return inflater.inflate(R.layout.your_bottom_sheet_layout, container, false)
          }
          
          override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
              val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
              dialog.setContentView(R.layout.your_bottom_sheet_layout)
          
              dialog.setOnShowListener {
                  val castDialog = it as BottomSheetDialog
                  val bottomSheet = castDialog.findViewById<View?>(R.id.design_bottom_sheet)
                  val behavior = BottomSheetBehavior.from(bottomSheet)
                  behavior.state = BottomSheetBehavior.STATE_EXPANDED
                  behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
                      override fun onStateChanged(bottomSheet: View, newState: Int) {
                          if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                              behavior.state = BottomSheetBehavior.STATE_EXPANDED
                          }
                      }
          
                      override fun onSlide(bottomSheet: View, slideOffset: Float) {}
                  })
              }
          
              return dialog
          }
          

          【讨论】:

            【解决方案9】:

            我一直在努力解决与您类似的问题。

            手动设置 bottomSheet 的高度是我的解决方案。

            拥有一个具有 BottomSheetBehaviour 的视图 viewA 和一个修改视图高度的自定义方法 modifyHeight()

            viewA?.modifyHeight()
            viewA?.measure(
                        MeasureSpec.makeMeasureSpec(
                            width,
                            MeasureSpec.EXACTLY
                        ),
                        MeasureSpec.makeMeasureSpec(
                            0,
                            MeasureSpec.UNSPECIFIED
                        )
                    )
            val layoutParams = LayoutParams(viewA.measuredWidth, viewA.measuredHeight)
            val bottomSheet = BottomSheetBehavior.from(viewA)
            layoutParams.behavior = bottomSheet
            viewA.layoutParams = layoutParams
            

            我的布局是这样的:

                <com.yourpackage.ViewA
                android:id="@+id/viewA"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:behavior_peekHeight="50dp"
                app:layout_behavior="@string/bottom_sheet_behavior" />
            

            重用旧 layoutParams 的 bottomSheetBehaviour 很重要,因为它包含您可能附加的 peekHeight 和侦听器。

            【讨论】:

              【解决方案10】:

              这是我实现的切换按钮单击侦听器,用于设置带有动画的底部工作表的拾取高度

              FrameLayout standardBottomSheet = findViewById(R.id.standardBottomSheet);
              
              BottomSheetBehavior<FrameLayout> bottomSheetBehavior = BottomSheetBehavior.from(standardBottomSheet);    
              
              btnToggleBottomSheet.setOnClickListener(new HPFM_OnSingleClickListener() {
                              @Override
                              public void onSingleClick(View v) {
                                  if (bottomSheetBehavior.getPeekHeight() == 0) {
                                      ObjectAnimator.ofInt(bottomSheetBehavior, "peekHeight", 200).setDuration(300).start();
                                  }
                                  else {
                                      ObjectAnimator.ofInt(bottomSheetBehavior, "peekHeight", 0).setDuration(300).start();
                                  }
                              }
                          });
              

              【讨论】:

                猜你喜欢
                • 2020-05-04
                • 2012-12-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-03-19
                • 1970-01-01
                • 2016-06-17
                相关资源
                最近更新 更多