Android BottomSheet like Share Sheet with anchored view at bottom - android

I'm trying to use the support library's BottomSheetDialogFragment to replicate the standard sheet that shows when you tap a Share button (see below). How would I achieve a similar layout, where there's a title at the top, independently scrollable content in the center, but an bottom anchored view with buttons that always stay on top.

You need to build a custom layout for the bottomSheet, for example share_bottom.xml. Within that layout you could
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="#string/bottom_sheet_behavior"
app:behavior_hideable="true"
app:behavior_peekHeight="200dp">
<TextView
android:layout_width="match_parent"
android:text="header"
android:layout_height="wrap_content"/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<LinearLayout
android:layout_weight="0"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
And then include it at he bottom of your fragment's layout:
After that you can control the visibility of this sheet
//retrieve the bottomsheet
bottomSheet = (LinearLayout) findViewById(R.id.bottomSheet);
//get the behaviour controller
bsb = BottomSheetBehavior.from(bottomSheet);
//hide the sheet
bsb.setState(BottomSheetBehavior.STATE_HIDDEN);
//showthe sheet
bsb.setState(BottomSheetBehavior.STATE_EXPANDED);

I was also having the same problem. I solved using these steps.
Make FrameLayout as your root layout.
Include the view to be anchored as second child and first child should contain all the content of activity.
Animate the second child to be visible when bottom sheet is expanded and make it invisible when bottom sheet is collapsed.
The independently scrolling content inside bottom sheet can be acheived by using a scroll view or nested scroll view inside bottom sheet.
To dim background refer this link
This is just a workaround. Basically what I did is replicate the persistent bottom sheet to behave like modal sheet.

Related

RecyclerView obscurs items below it in a BottomSheetFragment

I'm using a BottomSheetDialogFragment with a custom layout. I'm trying to have the following setup:
<TextView> -> pinned to the top of the bottom sheet
<RecyclerView> -> wrap_content
<Button> -> pinned to the bottom of the bottom sheet
Both TextView and Button must be visible at all time (sticky), while the RecyclerView should stay in the middle and scroll without obscuring other views.
This is my layout so far:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Title"
app:layout_constraintBottom_toTopOf="#id/recyclerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/title" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is what it looks like with a small list of items, where the RecyclerView has no need to scroll.
This is what it looks like with a large list of items.
The title stays pinned to the top, but the button doesn't.
The button is actually not even visible, even if I scroll down all the way.
What's strange to me is that this same layout works with a regular full screen activity, but it somehow fails with a BottomSheetFragment.
I've already looked at other posts, but none of them helped e.g.
RecyclerView (wrap_content) inside of a BottomSheetDialogFragment
The height of recycler view shouldn't be wrap_content
If you want recyler in-between your title and footer, the better approach is set height = 0 and pin its top to the bottom title and its bottom to the top of footer (like you already did), it will auto stretch for you
What ended up being the solution is setting the state of bottom sheet to expanded e.g.
val bottomSheetDialog = requireDialog() as BottomSheetDialog
bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
I suppose that the bottom sheet never expanded fully, so the layout was never fully visible. I thought that the bottom sheet would expand automatically based on the height of the content, but I was wrong. The layout itself is fine, I didn't have to make any changes to it.

BottomNavigationView does not follow layout heirarchy

I am having a bottom navigation view and I would want to hide it by showing a dummy view on top of it. In the relative layout, I am placing the dummy view on top of the bottom navigation view. But always the bottom navigation view shows on top. Why does the bottom navigation view does not follow layout hierarchy?
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#color/white"
android:theme="#style/BottomBar"
android:visibility="visible"
app:itemTextAppearanceActive="#style/BottomNavigationViewTextStyle"
app:itemTextAppearanceInactive="#style/BottomNavigationViewTextStyle"
app:labelVisibilityMode="labeled"
app:menu="#menu/bottom_navigation_menu" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:background="#color/black" />
</RelativeLayout>
I expect the View to be shown on top of the bottom navigation view(So that the bottom navigation view is invisible), but bottom navigation view always shown on top
Based on the following link https://material.io/components/bottom-navigation/android, the bottom navigation view has a default elevation of 8dp. Changing the elevation of dummy layout will fix the issue.

How to make a layout fill the screen vertically but start to grow when content doesn't fit

I wish I had an example to show if it seems confusing, but basically what I want to do is show a bottom toolbar with a Right navigator button (e.g. go to next page) always anchored to bottom of the screen, but if the content on the page (middle section) grows, the toolbar will push down so that you have to scroll to get to it. I know it seems strange, but this is the requirements. What would be the best approach for this?
You can achieve this by combining a ScrollView using android:fillViewport="true" and a vertical LinearLayout that has a Space with android:layout_weight="1" between your content and your navigation bar.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#ccc"/>
<Space
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<View
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#eee"/>
</LinearLayout>
</ScrollView>
That first view is a placeholder for whatever content you want to display, and the second is a placeholder for your navigation bar. You can see that when the first view is short (200dp tall), you get the "content" at the top and the navigation bar at the bottom:
This works because the fillViewport attribute will "stretch" the child LinearLayout to fill the screen, at which point there is "extra" space and the layout_weight attribute on the Space element will consume all that space. (Note that, despite being a ScrollView, you can't actually scroll anything in this state, since the view is stretched to be the exact size of the screen.)
However, when the "content" is tall enough to fill the screen, the fillViewport attribute will have no effect, the LinearLayout won't be stretched, and there won't be any extra space to consume. So, once you scroll to the bottom, your content will fill right up to the edge of the navigation bar:
(Scrolled to the top on the left, and scrolled to the bottom on the right.)

Android UI: align a fragment above another fragment that is aligned bottom

I haven't developed for Android in more than a year and I'm a bit rusty with it. I'm trying to setup a kinda simple UI: a bottom bar at the bottom of the screen and a fragment above it (but without filling the whole height). Something like this:
I thought this would be quite simple, but after a while struggling with it I can't manage to make it work without some "hacks".
The bottom bar is also implemented as a Fragment. This is my XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/bottomBar"
android:name="com.mytestpackage.BottomBarFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
<FrameLayout
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#id/bottomBar"
android:layout_alignParentBottom="true" />
</RelativeLayout>
The Fragment in fragmentContainer is dynamically loaded from code. With the code paste above, fragmentContainer is aligned bottom but it's not above bottom bar, they're overlapped. If I remove the alignParentBottom from fragmentContainer, then it's placed in top of the screen.
Like I said, I found two "hacks" to solve it but I don't like them much:
1- Set a padding/margin bottom to fragmentContainer.
2- Use a filler empty layout on top of the screen and set fragmentContainer to be below that one.
Is there any way to achieve the layout I want without having to use some tricks like the ones I said?
Thanks!
Add to the relative layout:
android:gravity="bottom"
Ah, and android:orientation="vertical" is meaningless for RelativeLayout
A simpler solution would be to use a LinearLayout with vertical orientation and gravity bottom instead of the RelativeLayout.

Force an ExpandableListView to move other items down

My layout is a ScrollView with some TextView and other controls and also an ExpandableListView.
What I want to do is When Expanding the ExpandableListView the controls which are below it move down and again upon collapsing, all the controls move up and only Group of Expendables become Visible.
The problem is when using wrap_content for expandView's Height the expanding of it shows nothing and just the indicator (little arrow) shows that it's expanded, and when explicitly use some numbers eg. 200dp, the lowest items of expandView not shown. (Because of using two Scrolling widget together).
Here's my simplified layout.
<ScrollView
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="HardcodedText" >
<LinearLayout>
//some controls
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ExpandableListView
android:id="#+id/lstDrugs"
android:layout_width="match_parent"
//HERE is the point that wrap_content only shows the groups header
android:layout_height=**"wrap_content"**
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:divider="#drawable/dividergradient"
android:dividerHeight="2dp" >
</ExpandableListView>
<Button
android:id="#+id/btnAddDrug"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/lstDrugs"
android:text="Add" />
</RelativeLayout>
</LinearLayout>
I've just found my answer in this thread.
The problem is with using List inside ScrollView. To handle it you should use a layout other than the ScrollView (eg. LinearLayout) and add other widgets as Header and Footer of the list. This would enable the scrolling of the view and also expanding of the List.

Categories

Resources