可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have the following test bottom sheet implementation.
When I set the peekHeight to a value less than 500, it works. After some value, any increase in peek height will not change how the bottom sheet is expanded. It Just remains there to only drag manually. How do we set the peekHeight programmatically to ensure that the bottom sheet is auto expanded to the peek height.
bottom_sheet_dialog_main
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/locUXCoordinatorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/locUXView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:orientation="vertical"
app:behavior_hideable="false"
app:behavior_peekHeight="0dp"
app:layout_behavior="@string/bottom_sheet_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="1 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="2 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="3 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="4 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="5 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="6 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="7 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="8 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="9 Value" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="First Value" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
Java code
public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment {
private static BottomSheetBehavior bottomSheetBehavior;
private static View bottomSheetInternal;
private static MyBottomSheetDialogFragment INSTANCE;
@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;
CoordinatorLayout coordinatorLayout = (CoordinatorLayout)d.findViewById(R.id.locUXCoordinatorLayout);
bottomSheetInternal = d.findViewById(R.id.locUXView);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal);
bottomSheetBehavior.setPeekHeight(bottomSheetInternal.getHeight());
bottomSheetInternal.requestLayout();
coordinatorLayout.getLayoutParams().height = bottomSheetInternal.getHeight();
Toast.makeText(getActivity(), "Height is" + bottomSheetInternal.getHeight() + " " + coordinatorLayout.getLayoutParams().height, Toast.LENGTH_LONG).show();
}
});
INSTANCE = this;
return inflater.inflate(R.layout.bottom_sheet_dialog_main, container, false);
}
}
回答1:
Using this code in onCreateView.
getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.design_bottom_sheet);
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) bottomSheet.getParent();
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setPeekHeight(bottomSheet.getHeight());
coordinatorLayout.getParent().requestLayout();
}
});
回答2:
I've found another solution. Maybe for future readers it can be useful.
@Override
public void setupDialog(Dialog dialog, int style) {
super.setupDialog(dialog, style);
final View root = View.inflate(getContext(), R.layout.fragment_bottom_sheet_choose_time, null);
dialog.setContentView(root);
initView(root);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) root.getParent()).getLayoutParams();
CoordinatorLayout.Behavior behavior = params.getBehavior();
if (behavior != null && behavior instanceof BottomSheetBehavior) {
mBottomSheetBehavior = (BottomSheetBehavior) behavior;
mBottomSheetBehavior.setBottomSheetCallback(mBottomSheetBehaviorCallback);
root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = root.getMeasuredHeight();
mBottomSheetBehavior.setPeekHeight(height);
}
});
}
}
As @Anthonyeef mentioned, here ViewTreeObserver
is aimed to get the exact measure height after the view is really measured and the GlobalOnLayoutListener
is removed for better performance.
But please, before using in production, test this solution on different devices and screens, because if your content in bottom sheet is higher than your screen it can produce some strange swipe behavior.
回答3:
By deeper UI inspection, we find that there is another CoordinatorLayout
that wraps our coordinator layout. The parent CoordinatorLayout
has a FrameLayout
with a BottomSheetBehavior
with the id design_bottom_sheet
. The peek height set from our code above was getting constrained due the match_parent
height of the FrameLayout
with the id design_bottom_sheet
By setting the peek height of the FrameLayout
with the id design_bottom_sheet , this issue was resolved
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;
coordinatorLayout = (CoordinatorLayout) d.findViewById(R.id.locUXCoordinatorLayout);
bottomSheetInternal = d.findViewById(R.id.locUXView);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal);
bottomSheetBehavior.setHidable(false);
BottomSheetBehavior.from((View)coordinatorLayout.getParent()).setPeekHeight(bottomSheetInternal.getHeight());
bottomSheetBehavior.setPeekHeight(bottomSheetInternal.getHeight());
coordinatorLayout.getParent().requestLayout();
}
});
回答4:
Thanks @athysirus for the neat approach. Here is the version I ended up with, in case somebody wants to have a working kotlin sample.
Important to note is, that you should also remove the global layout listener, once done.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
val bottomSheet = (dialog as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
BottomSheetBehavior.from<View>(bottomSheet).apply {
state = BottomSheetBehavior.STATE_EXPANDED
peekHeight = 0
}
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
回答5:
this is how I set peek_height
and layout_height
to bottom sheet view in BottomSheetDialogFragment
dialog?.setOnShowListener {
val dialog = dialog as BottomSheetDialog
val bottomSheet = dialog.findViewById<FrameLayout>(R.id.design_bottom_sheet)
val coordinatorLayout = bottomSheet?.parent as? CoordinatorLayout
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheet?.viewTreeObserver?.addOnGlobalLayoutListener {
bottomSheet.viewTreeObserver.removeOnGlobalLayoutListener {}
bottomSheetBehavior.peekHeight = getPopupHeight(.5f)
val params = bottomSheet.layoutParams
params.height = getPopupHeight(1f)
bottomSheet.layoutParams = params
coordinatorLayout?.parent?.requestLayout()
}
}
this method to get percent of screen height
private fun getPopupHeight(percent: Float): Int {
val displayMetrics = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(displayMetrics)
return (displayMetrics.heightPixels * percent).toInt()
}
回答6:
Solution in Kotlin inspired by Nandish A's post in more detail. First the layout:
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/container_root"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:behavior_hideable="false"
app:behavior_peekHeight="0dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<!-- content of the bottom sheet -->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And then put this into your BottomSheetDialogFragment
:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// ...
dialog.setOnShowListener {
val root = dialog.find<CoordinatorLayout>(R.id.container_root)
val bottomSheetInternal = root.find<ConstraintLayout>(R.id.bottom_sheet)
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal)
bottomSheetBehavior.isHideable = false
BottomSheetBehavior.from(root.parent as View).peekHeight = root.height
bottomSheetBehavior.peekHeight = root.height
root.parent.requestLayout()
}
// ...
}
回答7:
Kotlin safe way of achieving this is:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog.setOnShowListener {
val dialog = it as BottomSheetDialog
val bottomSheet = dialog.findViewById<View>(R.id.design_bottom_sheet)
bottomSheet?.let { sheet ->
dialog.behavior.peekHeight = sheet.height
sheet.parent.parent.requestLayout()
}
}
}
NOTE: No need to wrap your layout in coordinator layout.
Works like charm.