Динамически изменять высоту BottomSheetBehavior
Я использую BottomSheetBehavior
от Google недавно выпустила AppCompat v23.2. Высота моего нижнего листа зависит от содержимого, отображаемого внутри нижнего листа (аналогично тому, что Google делает сами в своем приложении "Карты").
Он отлично работает с первоначально загруженными данными, но мое приложение изменяет содержимое, отображаемое во время выполнения, и когда это происходит, нижний лист остается на своей старой высоте, что приводит либо к неиспользуемому пространству внизу, либо к срезу обзора.
Есть ли способ сообщить макет нижнего листа, чтобы пересчитать высоту, используемую для расширенного состояния (когда высота ViewGroup
установлен в MATCH_HEIGHT
) или каким-либо способом вручную установить необходимую высоту?
РЕДАКТИРОВАТЬ: Я также пытался вручную позвонить invalidate()
на ViewGroup
и родитель этого, но без какого-либо успеха.
10 ответов
У меня была такая же проблема с 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);
Ты можешь использовать 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);
Приведенный ниже фрагмент кода помог мне решить эту проблему, когда я переключаюсь между видимостью разных представлений в макете, а высота автоматически изменяется для моего нижнего листа.
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
}
Хотя проблема была решена в библиотеке поддержки>=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) {
}
});
Для фрагмента диалогового окна нижнего листа прочтите следующее: Фрагмент диалогового окна нижнего листа развернуть на всю высоту
@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);
}
Я борюсь с проблемой, похожей на вашу.
Решением для меня была ручная установка высоты bottomSheet.
Наличие представления viewA, имеющего BottomSheetBehaviour и настраиваемый метод 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" />
Важно повторно использовать bottomSheetBehaviour старого layoutParams, потому что он содержит peekHeight и слушателей, которые вы, возможно, прикрепили.
Я столкнулся с той же проблемой, когда пытался обновить высоту просмотра на основе его содержимого, была найдена высота из предыдущего макета. Это имеет смысл, поскольку новый макет еще не состоялся. Посредством публикации в потоке пользовательского интерфейса высота макета вычисляется после нового макета, и делается еще один запрос макета, чтобы обновить нижний лист до нужной высоты.
void show() {
setVisibility(View.VISIBLE);
post(new Runnable() {
@Override
public void run() {
mBottomSheetBehavior.setPeekHeight(findViewById(R.id.sheetPeek).getHeight());
requestLayout();
}
})
}
Я столкнулся с той же проблемой, когда использовал BistomSheet в представлении реселлера, и элементы менялись динамически. Как упомянул @sosite в своем комментарии, проблема зарегистрирована, и они исправили ее в последней версии. Журнал проблемы здесь
Просто обновите вашу библиотеку поддержки дизайна до версии 24.0.0 и проверьте.
Я следовал совету @HaraldUnander, и он дал мне идею, которая действительно сработала. Если вы запустили поток (не смог заставить его работать с методом post как он) после BottomSheetBehavior.state
программно настроен на STATE_COLLAPSED
, тогда вы уже можете получить высоту ваших просмотров и установить 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() {
val dynamicHeight = yourContainerView.height
BottomSheetBehavior.from(bottomSheetView).peekHeight = dynamicHeight
}
}).start();
Вот прослушиватель нажатия кнопки переключения, который у меня есть, чтобы установить высоту выбора нижнего листа с анимацией.
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();
}
}
});