Android "Top Sheet" equivalent of "Bottom Sheet"?
Solution 1
Here is the basis of my solution that I commented about above. I will come back and flesh it out later.
@Override
protected void onCreate(
@Nullable
Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (isFinishing())
{
return;
}
setContentView(R.layout.activity_home);
...
mGroupBottomSheetFiller = (ViewGroup) findViewById(R.id.groupBottomSheetFiller);
final NestedScrollView bottomSheetMap = (NestedScrollView) findViewById(R.id.bottomSheetMap);
mBottomSheetMapBehavior = BottomSheetBehavior.from(bottomSheetMap);
mBottomSheetMapBehavior.setBottomSheetCallback(new BottomSheetCallback()
{
@Override
public void onStateChanged(
@NonNull
View bottomSheet,
int newState)
{
//Log.e(TAG, "mBottomSheetMapBehavior.onStateChanged(bottomSheet, newState=" +
// bottomSheetBehaviorStateToString(newState) + ')');
int visibility = isBottomSheetExpanded(mBottomSheetMapBehavior) ? View.VISIBLE : View.GONE;
mImageBottomSheetMapClose.setVisibility(visibility);
}
@Override
public void onSlide(
@NonNull
View bottomSheet,
float slideOffset)
{
//Log.e(TAG, "mBottomSheetMapBehavior.onStateChanged(bottomSheet, slideOffset=" + slideOffset + ')');
resizeMap();
}
});
bottomSheetMap.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
//Log.e(TAG, "onGlobalLayout()");
bottomSheetMap.getViewTreeObserver().removeOnGlobalLayoutListener(this);
resizeMap();
}
});
...
}
private void resizeMap()
{
int screenHeightPixels = PbPlatformUtils.getScreenHeightPixels();
//Log.e(TAG, "resizeMap: screenHeightPixels=" + screenHeightPixels);
int[] location = new int[2];
mGroupMap.getLocationInWindow(location);
//Log.e(TAG, "resizeMap: getLocationInWindow=" + Arrays.toString(location));
LayoutParams groupMapLayoutParams = mGroupMap.getLayoutParams();
groupMapLayoutParams.height = screenHeightPixels - location[1];
mGroupMap.requestLayout();
}
public static String bottomSheetBehaviorStateToString(int state)
{
String s;
switch (state)
{
case BottomSheetBehavior.STATE_COLLAPSED:
s = "STATE_COLLAPSED";
break;
case BottomSheetBehavior.STATE_DRAGGING:
s = "STATE_DRAGGING";
break;
case BottomSheetBehavior.STATE_EXPANDED:
s = "STATE_EXPANDED";
break;
case BottomSheetBehavior.STATE_HIDDEN:
s = "STATE_HIDDEN";
break;
case BottomSheetBehavior.STATE_SETTLING:
s = "STATE_SETTLING";
break;
default:
s = "UNKNOWN";
break;
}
return s + '(' + state + ')';
}
private static boolean isBottomSheetExpanded(
@NonNull
BottomSheetBehavior bottomSheetBehavior)
{
return bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED;
}
private void bottomSheetMapExpand()
{
mGroupBottomSheetFiller.setVisibility(View.VISIBLE);
int peekHeightPx = getResources().getDimensionPixelSize(R.dimen.home_bottom_sheet_map_peek_height);
mBottomSheetMapBehavior.setPeekHeight(peekHeightPx);
mBottomSheetMapBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
mBottomSheetMapBehavior.setHideable(false);
}
private void bottomSheetMapCollapse()
{
mGroupBottomSheetFiller.setVisibility(View.VISIBLE);
int peekHeightPx = getResources().getDimensionPixelSize(R.dimen.home_bottom_sheet_map_peek_height);
mBottomSheetMapBehavior.setPeekHeight(peekHeightPx);
mBottomSheetMapBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
mBottomSheetMapBehavior.setHideable(false);
}
private void bottomSheetMapHide()
{
mBottomSheetMapBehavior.setHideable(true);
mBottomSheetMapBehavior.setPeekHeight(0);
mBottomSheetMapBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
mGroupBottomSheetFiller.setVisibility(View.GONE);
}
Solution 2
I found a TopSheetBehavior
implementation and have tried to keep it up-to-date: https://github.com/carlos-mg89/TopSheetBehavior
It has proved to work pretty well in my case. I found many other TopSheetBehavior
that were incomplete or that crashed, but this one doesn't crash and works out of the box by only replacing the behavior parameter:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:behavior_hideable="true"
app:behavior_peekHeight="56dp"
app:layout_behavior="your.package.components.TopSheetBehavior">
<!-- Your content goes here -->
</LinearLayout>
Related videos on Youtube
swooby
Updated on October 14, 2022Comments
-
swooby over 1 year
I am wanting to implement a "Bottom Sheet" type of layout, but with a twist where the "bottom" sheet will be a MapFragment, which won't work very well as an up/down draggable view.
I had a probably naive thought to "flip" the logic to a "Top Sheet" design, where you drag the Top Sheet up/down to show more/less of the bottom MapFragment.
...to [something like] this...
Is this possible given the Support Design Tools, or will I have to roll something like this on my own?
-
apelsoczi over 7 yearsI think you are going to sign up to do a substantial amount of heavy lifting to do two things. First, to create your custom implementation. Second, to identify how your implementation conflicts with the android implementation and to defend against those scenarios. My personal opinion, the Material Design Language has been put in place to visually communicate the ways your users can expect to interact with the app. Something like this may be great as a personal endeavour for learning, but once you hit the market - you need to expect that every single person won't understand how to use your app.
-
swooby over 7 yearsI ended up just adding a bottomsheet layout that has a Toolbar at its top that allows the user to drag the toolbar up. The trick is then to resize the Map as the user drags the toolbar. I can post my code if anyone is interested.
-
-
carlosavoy almost 7 yearsdid you encounter compatibility issues with this solution? in case you have a working app in the playstore using this functionality it'd be great to check it out :)
-
xarlymg89 over 5 yearsYou can check my answer @swooby , perhaps it's nearer to what you were looking for. At least, it behaves exactly as I imagined a TopSheet to be equivalent to a
BottomSheetBehavior
. -
swooby over 5 yearsLooks good @CarlosAlbertoMartínezGadea, but the implementation in my answer current fits my needs for an existing production app, so I haven't experimented with porting over to yours. If I do one day I'll mark yours as the preferred answer! Thanks!
-
unownsp over 2 yearsdo you have a complete solution...i am doing something similar and i am unable to find useful material for this...