如何设置底部抽屉的初始高度只显示特定部分?

7
假设我的底部表格有像以下这样的小部件行。如果我想最初仅显示前两行(即前两个LinearLayout),而不显示下面的其他小部件。我不希望最初看到它们。我该如何设置正确的“Peek Height”?硬编码app:behavior_peekHeight可能行不通,因此我需要以编程方式设置它,但如何计算高度呢?
或者是否有更推荐的方法来获得相同的结果?我的意思是,如果我测试Google Maps,长按位置首先仅显示标题部分作为底部表格,但当我尝试向上滚动底部表格时,感觉就好像标题部分(可能不是真正的底部表格)被一个包含所有元素的真正的底部表格所替换。如果我的解释不够清楚,请自行尝试使用Google Maps。
<android.support.v4.widget.NestedScrollView
    android:id="@+id/bottom_sheet"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView/>
            <android.support.v7.widget.AppCompatSpinner/>
        </LinearLayout>
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView/>
            <TextView/>
        </LinearLayout>
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView/>
            <TextView/>
        </LinearLayout>
        <android.support.v7.widget.RecyclerView/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>
3个回答

19

我会使用 ViewTreeObserver.OnGlobalLayoutListener 监听底部面板的布局,然后调用 BottomSheetBehavior.setPeekHeight() 并设置第一个不想看到的视图的 y 坐标来解决这个问题。

private BottomSheetBehavior<View> behavior;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

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

    final LinearLayout inner = findViewById(R.id.inner);
    inner.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            inner.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            View hidden = inner.getChildAt(2);
            behavior.setPeekHeight(hidden.getTop());
        }
    });
}
在这种情况下,我的底部表单是一个NestedScrollView,它包含一个LinearLayout,其中包含许多TextView。通过将弹出高度设置为第三个TextView的顶部(通过getChildAt(2)获得),我的底部表单在折叠时正好显示两个TextView

输入图像描述 输入图像描述


我能获取这段代码的布局文件吗?如果可能的话,也能提供一下代码吗?我正在尝试将peekheight设置为我拖动bottomsheet的位置。但是我找不到当前拖动位置的值。 - ShresthaGanesh

5

对@Ben P.的回答进行了自定义,以视图ID作为peekHeight的引用,并创建了一个函数:

/**
 * Gets the bottom part of the target view and sets it as the peek height of the specified @{BottomSheetBehavior}
 *
 * @param layout - layout of the bottom sheet.
 * @param targetViewId - id of the target view. Must be a view inside the 'layout' param.
 * @param behavior - bottom sheet behavior recipient.
 */
private fun <T : ViewGroup> getViewBottomHeight(layout: ViewGroup,
                                                targetViewId: Int,
                                                behavior: BottomSheetBehavior<T>) {
    layout.apply {
        viewTreeObserver.addOnGlobalLayoutListener(
                object : ViewTreeObserver.OnGlobalLayoutListener {
                    override fun onGlobalLayout() {
                        viewTreeObserver.removeOnGlobalLayoutListener(this)
                        behavior.peekHeight = findViewById<View>(targetViewId).bottom
                    }
                })
    }
}

在我们的使用情况下,我们需要针对视图的底部部分进行定位,因此我们设置了这种方式。根据使用情况可以进行调整。

1

真聪明! 我的问题是在错误的时机尝试 getTop() 或者 getHeight(),如果视图没有准备好,它会返回 0

是的,使用 viewTreeObserver 可以避免这种情况。

实际上,这与 @Ben P. 之前的回答并没有什么区别,只是 Kotlin 版本而已:


class MyBottomSheetDialog() : BottomSheetDialogFragment(){

    private val binding by viewBinding(SomeLayoutViewBinding::bind)
    ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        (this.dialog as BottomSheetDialog).behavior.let { behavior ->
            /* Set "pivotView" as interested target and make it the pivot of peek */
            binding.pivotView.viewTreeObserver.addOnGlobalLayoutListener(object :
                OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    binding.pivotView.viewTreeObserver.removeOnGlobalLayoutListener(this)
                    behavior.peekHeight = binding.pivotView.top
                }
            })
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接