如何判断 BottomSheetDialogFragment 何时展开以填满屏幕?
How to tell when BottomSheetDialogFragment expanded to fill the screen?
我正在尝试按照 Material 设计 specification 使用 Android 设计支持库向模态底部 sheet 添加关闭可供性工具栏。
When full-screen, bottom sheets can be internally scrolled to reveal additional content. A toolbar should be used to provide a collapse or close affordance to exit this view.
我使用 BottomSheetBehavior.BottomSheetCallback
根据 BottomSheetBehavior
的 expanded/collapsed 状态切换工具栏的可见性。问题是当我尝试向上拖动时工具栏出现,即使 BottomSheetDialogFragment
内容无法填满整个屏幕。我如何判断底部 sheet 何时全屏显示而底部 sheet 正在展开?
public class BottomSheetToolbarToggleCallback : BottomSheetBehavior.BottomSheetCallback
{
public BottomSheetToolbarToggleCallback(BottomSheetDialogFragment bottomSheetDialogFragment)
{
this.bottomSheetDialogFragment = bottomSheetDialogFragment ?? throw new System.ArgumentNullException(nameof(bottomSheetDialogFragment));
}
public override void OnSlide(View bottomSheet, float slideOffset)
{
}
public override void OnStateChanged(View bottomSheet, int newState)
{
switch (newState)
{
case BottomSheetBehavior.StateCollapsed:
ShowToolbar(bottomSheet, ViewStates.Gone);
break;
case BottomSheetBehavior.StateExpanded:
ShowToolbar(bottomSheet, ViewStates.Visible);
break;
case BottomSheetBehavior.StateHidden:
bottomSheetDialogFragment.Dismiss();
break;
}
}
private void ShowToolbar(View bottomSheet, ViewStates viewState)
{
var toolbar = bottomSheet.FindViewById<Toolbar>(Resource.Id.toolbar);
if (toolbar != null)
{
toolbar.Visibility = viewState;
}
}
private readonly BottomSheetDialogFragment bottomSheetDialogFragment;
}
public abstract class BaseBottomSheetDialogFragment<TViewModel> : MvxBottomSheetDialogFragment<TViewModel> where TViewModel : BaseViewModel
{
protected BaseBottomSheetDialogFragment()
{
}
protected BaseBottomSheetDialogFragment(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void SetupDialog(Dialog dialog, int style)
{
base.SetupDialog(dialog, style);
this.EnsureBindingContextIsSet(); // This is required to use this.BindingInflate()
var view = this.BindingInflate(LayoutResourceId, null);
dialog.SetContentView(view);
// Add support to handle material design specification to dynamically show a toolbar with an 'X' button.
var layoutParams = (CoordinatorLayout.LayoutParams)((View)view.Parent).LayoutParameters;
var behavior = layoutParams.Behavior;
if (behavior != null && behavior is BottomSheetBehavior)
{
var toolbar = view.FindViewById<Toolbar>(Droid.Resource.Id.toolbar);
if (toolbar != null)
{
toolbar.SetNavigationIcon(Droid.Resource.Drawable.clear);
((BottomSheetBehavior)behavior).SetBottomSheetCallback(new BottomSheetToolbarToggleCallback(this));
if (CloseCommand != null)
{
toolbar.SetNavigationOnClickListener(new MvxAsyncCommandOnClickListener(CloseCommand));
}
}
}
}
/// <summary>
/// The Android layout resource id of the layout to show in the modal bottom sheet.
/// </summary>
protected abstract int LayoutResourceId { get; }
/// <summary>
/// Optional <see cref="MvxAsyncCommand"/> to call when the optional toolbar navigation button is clicked.
/// </summary>
protected abstract IMvxAsyncCommand CloseCommand { get; }
}
由于我不知道你的具体代码,这里是我试过的一个简单例子的片段:
public View mView;
public override void SetupDialog(Dialog dialog, int style)
{
base.SetupDialog(dialog, style);
this.EnsureBindingContextIsSet(); // This is required to use this.BindingInflate()
mView = this.BindingInflate(LayoutResourceId, null);
dialog.SetContentView(view);
...
}
在BottomSheetCallback
中:
public override void OnStateChanged(View bottomSheet, int newState)
{
switch (newState)
{
case BottomSheetBehavior.StateExpanded:
var height = bottomSheetDialogFragment.mView.Height;//need you convert to your BottomSheetDialogFragment
break;
}
}
这是我最终使用的解决方案。如果 BottomSheetDialogFragment
占据了 window 的整个高度,它只会显示接近的可供性。它已经在 multi-window 模式、cutouts/insets、横向和纵向方向上进行了测试。
public class BottomSheetDialogFragmentCloseAffordanceCallback : BottomSheetBehavior.BottomSheetCallback
{
public BottomSheetDialogFragmentCloseAffordanceCallback(BottomSheetDialogFragment bottomSheetDialogFragment)
{
this.bottomSheetDialogFragment = bottomSheetDialogFragment ?? throw new System.ArgumentNullException(nameof(bottomSheetDialogFragment));
}
public override void OnSlide(View bottomSheet, float slideOffset)
{
}
public override void OnStateChanged(View bottomSheet, int newState)
{
switch (newState)
{
case BottomSheetBehavior.StateCollapsed:
ShowToolbar(bottomSheet, ViewStates.Gone);
break;
case BottomSheetBehavior.StateExpanded:
if (IsViewSameHeightAsWindow(bottomSheet))
{
ShowToolbar(bottomSheet, ViewStates.Visible);
}
break;
case BottomSheetBehavior.StateHalfExpanded:
break;
case BottomSheetBehavior.StateHidden:
bottomSheetDialogFragment.Dismiss();
break;
}
}
private void ShowToolbar(View bottomSheet, ViewStates viewState)
{
var toolbar = bottomSheet.FindViewById<Toolbar>(Resource.Id.toolbar);
if (toolbar != null)
{
toolbar.Visibility = viewState;
}
}
private bool IsViewSameHeightAsWindow(View view)
{
int[] locationInWindow = new int[2];
view.GetLocationInWindow(locationInWindow);
return locationInWindow[1] == view.RootWindowInsets.StableInsetTop;
}
private readonly BottomSheetDialogFragment bottomSheetDialogFragment;
}
我正在尝试按照 Material 设计 specification 使用 Android 设计支持库向模态底部 sheet 添加关闭可供性工具栏。
When full-screen, bottom sheets can be internally scrolled to reveal additional content. A toolbar should be used to provide a collapse or close affordance to exit this view.
我使用 BottomSheetBehavior.BottomSheetCallback
根据 BottomSheetBehavior
的 expanded/collapsed 状态切换工具栏的可见性。问题是当我尝试向上拖动时工具栏出现,即使 BottomSheetDialogFragment
内容无法填满整个屏幕。我如何判断底部 sheet 何时全屏显示而底部 sheet 正在展开?
public class BottomSheetToolbarToggleCallback : BottomSheetBehavior.BottomSheetCallback
{
public BottomSheetToolbarToggleCallback(BottomSheetDialogFragment bottomSheetDialogFragment)
{
this.bottomSheetDialogFragment = bottomSheetDialogFragment ?? throw new System.ArgumentNullException(nameof(bottomSheetDialogFragment));
}
public override void OnSlide(View bottomSheet, float slideOffset)
{
}
public override void OnStateChanged(View bottomSheet, int newState)
{
switch (newState)
{
case BottomSheetBehavior.StateCollapsed:
ShowToolbar(bottomSheet, ViewStates.Gone);
break;
case BottomSheetBehavior.StateExpanded:
ShowToolbar(bottomSheet, ViewStates.Visible);
break;
case BottomSheetBehavior.StateHidden:
bottomSheetDialogFragment.Dismiss();
break;
}
}
private void ShowToolbar(View bottomSheet, ViewStates viewState)
{
var toolbar = bottomSheet.FindViewById<Toolbar>(Resource.Id.toolbar);
if (toolbar != null)
{
toolbar.Visibility = viewState;
}
}
private readonly BottomSheetDialogFragment bottomSheetDialogFragment;
}
public abstract class BaseBottomSheetDialogFragment<TViewModel> : MvxBottomSheetDialogFragment<TViewModel> where TViewModel : BaseViewModel
{
protected BaseBottomSheetDialogFragment()
{
}
protected BaseBottomSheetDialogFragment(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void SetupDialog(Dialog dialog, int style)
{
base.SetupDialog(dialog, style);
this.EnsureBindingContextIsSet(); // This is required to use this.BindingInflate()
var view = this.BindingInflate(LayoutResourceId, null);
dialog.SetContentView(view);
// Add support to handle material design specification to dynamically show a toolbar with an 'X' button.
var layoutParams = (CoordinatorLayout.LayoutParams)((View)view.Parent).LayoutParameters;
var behavior = layoutParams.Behavior;
if (behavior != null && behavior is BottomSheetBehavior)
{
var toolbar = view.FindViewById<Toolbar>(Droid.Resource.Id.toolbar);
if (toolbar != null)
{
toolbar.SetNavigationIcon(Droid.Resource.Drawable.clear);
((BottomSheetBehavior)behavior).SetBottomSheetCallback(new BottomSheetToolbarToggleCallback(this));
if (CloseCommand != null)
{
toolbar.SetNavigationOnClickListener(new MvxAsyncCommandOnClickListener(CloseCommand));
}
}
}
}
/// <summary>
/// The Android layout resource id of the layout to show in the modal bottom sheet.
/// </summary>
protected abstract int LayoutResourceId { get; }
/// <summary>
/// Optional <see cref="MvxAsyncCommand"/> to call when the optional toolbar navigation button is clicked.
/// </summary>
protected abstract IMvxAsyncCommand CloseCommand { get; }
}
由于我不知道你的具体代码,这里是我试过的一个简单例子的片段:
public View mView;
public override void SetupDialog(Dialog dialog, int style)
{
base.SetupDialog(dialog, style);
this.EnsureBindingContextIsSet(); // This is required to use this.BindingInflate()
mView = this.BindingInflate(LayoutResourceId, null);
dialog.SetContentView(view);
...
}
在BottomSheetCallback
中:
public override void OnStateChanged(View bottomSheet, int newState)
{
switch (newState)
{
case BottomSheetBehavior.StateExpanded:
var height = bottomSheetDialogFragment.mView.Height;//need you convert to your BottomSheetDialogFragment
break;
}
}
这是我最终使用的解决方案。如果 BottomSheetDialogFragment
占据了 window 的整个高度,它只会显示接近的可供性。它已经在 multi-window 模式、cutouts/insets、横向和纵向方向上进行了测试。
public class BottomSheetDialogFragmentCloseAffordanceCallback : BottomSheetBehavior.BottomSheetCallback
{
public BottomSheetDialogFragmentCloseAffordanceCallback(BottomSheetDialogFragment bottomSheetDialogFragment)
{
this.bottomSheetDialogFragment = bottomSheetDialogFragment ?? throw new System.ArgumentNullException(nameof(bottomSheetDialogFragment));
}
public override void OnSlide(View bottomSheet, float slideOffset)
{
}
public override void OnStateChanged(View bottomSheet, int newState)
{
switch (newState)
{
case BottomSheetBehavior.StateCollapsed:
ShowToolbar(bottomSheet, ViewStates.Gone);
break;
case BottomSheetBehavior.StateExpanded:
if (IsViewSameHeightAsWindow(bottomSheet))
{
ShowToolbar(bottomSheet, ViewStates.Visible);
}
break;
case BottomSheetBehavior.StateHalfExpanded:
break;
case BottomSheetBehavior.StateHidden:
bottomSheetDialogFragment.Dismiss();
break;
}
}
private void ShowToolbar(View bottomSheet, ViewStates viewState)
{
var toolbar = bottomSheet.FindViewById<Toolbar>(Resource.Id.toolbar);
if (toolbar != null)
{
toolbar.Visibility = viewState;
}
}
private bool IsViewSameHeightAsWindow(View view)
{
int[] locationInWindow = new int[2];
view.GetLocationInWindow(locationInWindow);
return locationInWindow[1] == view.RootWindowInsets.StableInsetTop;
}
private readonly BottomSheetDialogFragment bottomSheetDialogFragment;
}