Android 动态更改底部挡板的高度

Android 动态更改底部挡板的高度,android,android-appcompat,Android,Android Appcompat,我使用的是谷歌最近发布的AppCompat v23.2的BottomSheetBehavior。我的底页的高度取决于底页内部显示的内容(类似于谷歌自己在地图应用程序中所做的) 它可以很好地处理最初加载的数据,但我的应用程序会在运行时更改显示的内容,当这种情况发生时,底部工作表仍保持原来的高度,这会导致底部未使用的空间或视图的剪切 是否有任何方法通知底部图纸布局重新计算展开状态所用的高度(当视图组的高度设置为匹配高度时)或手动设置所需高度 EDIT:我还试图手动调用ViewGroup及其父对象上

我使用的是谷歌最近发布的AppCompat v23.2的
BottomSheetBehavior
。我的底页的高度取决于底页内部显示的内容(类似于谷歌自己在地图应用程序中所做的)

它可以很好地处理最初加载的数据,但我的应用程序会在运行时更改显示的内容,当这种情况发生时,底部工作表仍保持原来的高度,这会导致底部未使用的空间或视图的剪切

是否有任何方法通知底部图纸布局重新计算展开状态所用的高度(当
视图组的高度设置为
匹配高度时)或手动设置所需高度



EDIT:我还试图手动调用
ViewGroup
及其父对象上的
invalidate()
,但没有成功。

您可以使用
BottomSheetBehavior#setpeek height
来实现这一点

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

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

当我在底部工作表中使用recyclerview时,我也面临着同样的问题,而这些项目是动态变化的。正如@sosite在他的评论中提到的,这个问题已经被记录下来,他们已经在最新版本中修复了它。


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

尽管问题已在>=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) {
        }
    });

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

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

我遵循了@Haraldunader的建议,它给了我一个实际有效的想法。如果在
BottomSheetBehavior.state
被编程设置为
state\u COLLAPSED
之后运行线程(无法使其与post方法一起工作),则您已经可以获得视图的高度,并根据其内容设置
peekeheight

因此,首先要设置
BottomSheetBehavior

BottomSheetBehavior.from(routeCaptionBottomSheet).state = BottomSheetBehavior.STATE_COLLAPSED
然后动态设置“偷看高度”:

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

对于底部工作表对话框片段,请阅读以下内容:

@覆盖
ActivityCreated上的公共无效(@Nullable Bundle savedinStateCState){
super.onActivityCreated(savedInstanceState);
BottomSheetDialog=(BottomSheetDialog)getDialog();
FrameLayout bottomSheet=dialog.findviewbyd(com.google.android.material.R.id.design\u bottom\u sheet);
BottomSheetBehavior=BottomSheetBehavior.from(bottomSheet);
behavior.setState(behavior.STATE_扩展);
行为。设置高度(0);
}

下面的代码片段帮助我解决了这个问题,我在布局中不同视图的可见性和底部图纸的高度之间切换

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
}
override fun onCreateView(充气机:布局充气机,容器:ViewGroup?,savedInstanceState:Bundle?):视图?{
返回充气机。充气(右布局。您的底部图纸布局,容器,假)
}
重写FunonCreateDialog(savedInstanceState:Bundle?:对话框){
val dialog=super.onCreateDialog(savedInstanceState)作为BottomSheetDialog
对话框.setContentView(R.layout.your\u bottom\u sheet\u布局)
dialog.setOnShowListener{
val castDialog=它作为BottomSheetDialog
val bottomSheet=castDialog.findViewById(R.id.design\u bottom\u sheet)
val behavior=BottomSheetBehavior.from(bottomSheet)
behavior.state=BottomSheetBehavior.state\u已展开
setBottomSheetCallback(对象:BottomSheetBehavior.BottomSheetCallback()){
覆盖状态更改(底部工作表:视图,新闻状态:Int){
if(newState==BottomSheetBehavior.STATE\u拖动){
behavior.state=BottomSheetBehavior.state\u已展开
}
}
覆盖滑轨上的乐趣(底页:视图,滑轨偏移:浮动){}
})
}
返回对话框
}

我一直在努力解决一个与你类似的问题

手动设置底部板材的高度是我的解决方案

具有具有BottomSheetBehavior的视图视图a和修改视图高度的自定义方法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的BottomSheetBehavior非常重要,因为它包含您可能附加的peekHeight和侦听器

这里是切换按钮点击监听器,我用它来设置底部板材的拾取高度

FrameLayout standardBottomSheet=findViewById(R.id.standardBottomSheet);
BottomSheetBehavior BottomSheetBehavior=BottomSheetBehavior.from(standardBottomSheet);
btnToggleBottomSheet.setOnClickListener(新的HPFM_OnSingleClickListener(){
@凌驾
单个单击上的公共无效(视图v){
if(bottomSheetBehavior.getPeekHeight()==0){
ObjectAnimator.ofInt(bottomSheetBehavior,“peekHeight”,200).setDuration(300).start();
}
否则{
ObjectAnimator.ofInt(bottomSheetBehavior,“peekHeight”,0).setDuration(300).start();
}
}
});

您可以将视图高度设置为包裹父视图,然后在加载内容后使视图无效view heig
    <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" />
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();
                    }
                }
            });