FloatingActionButton escapes the anchor view's bounds - android

I have a layout like this:
<CoordinatorLayout>
<NestedScrollView
android:id="#+id/contentScrollView
android:layout_width="600dp"
android:layout_Height="match_parent"
android:fillViewport="true"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<ViewStub
android:id="#+id/viewStub"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</NestedScrollView>
<FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_anchor="#id/contentScrollView"
app:layout_anchroGravity="bottom|right|end" />
</CoordinatorLayout>
Now, the FAB works OK otherwise, but it escapes the bounds of the nested scroll view. It's aligned correctly with the bottom edge, but it's laid out right in the middle of the content's right edge (i.e. half of the FAB is inside the bounds while the other half is not).
If the width of the nested scroll view is set to match_parent, it works correctly - and this is how it's laid out on phones. However, I want the scrolling content to be of constant width on tablets and the FAB should be inside the content, not outside of it.
How do I set the FAB to be inside the bounds of the nested scroll view? Margins and paddings seem to do nothing about the horizontal alignment.

Try setting android:layout_gravity on the FloatingActionButton to the opposite values, i.e. top|left|start

Related

Constraint layout Not constraining correctly

I have a layout activity in my project in which I am using a constraint layout and basically only 3 view, first is custom toolbar second a Recycler View and last one is a fragment view. I constrained recycler view to top toolbar and top of fragment view but I goes below fragment and even under screen.
This is my layout code.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="#style/Theme.Music.Font">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/bold"
android:text="#string/app_name"
android:textSize="26sp" />
<androidx.appcompat.widget.SearchView
android:id="#+id/searchView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
app:searchIcon="#drawable/search_icon" />
</androidx.appcompat.widget.Toolbar>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbarThumbVertical="#drawable/scroll_bar"
android:scrollbars="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nowPlaying"
android:name="com.example.music.NowPlaying"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:layout="#layout/fragment_now_playing" />
</androidx.constraintlayout.widget.ConstraintLayout>
And when I constrained with fragment also it goes on top overriding toolbar.
As shown in picture.
When you use wrap_content for the height or width, you're saying the view should expand to be as large as necessary to "display" the content. "Display" in air quotes because if there's a lot of content, parts of the view might end up off the screen.
Your constraints don't limit the size here, just the positioning. If you constrain one edge to something, you'll pin it, and it will expand off the screen at the other end. If you constrain opposite edges, then you're basically centering it around those constraints.
If you want to match those constraints, you need to set the height to 0dp. That way your constrained edges will actually be pinned where you want them, and that makes the view a certain height. It fits the space between its constrained edges.
Looking at your layout, I'm guessing the "now playing" part at the bottom is your FragmentContainerView. That's set to wrap_content but its contents seem to be a fixed size, so that's fine - exactly what you want! Same goes for the toolbar - it's wrap_content, but its contents are a fixed size.
So your RecyclerView also needs its bottom to be constrained to the top of nowPlaying, and it needs a layout_height of 0dp. That way, it's constrained to the space between the toolbar and the now playing bit, and its size fills that area. The other two take up as much space as they need, and the RecyclerView fills what's left - which is what you want, right?
Generally this is how it works for anything that scrolls, like a RecyclerView or ScrollView - the view in the layout is a window into the scrolling content, so the actual size of the view (layout_height and layout_width) controls the size of that window. The content just scrolls up and down behind it. If that window is the same size as the content, wrap_content, then the whole thing is visible (but possibly off the screen) and there's nothing to scroll! So for scrolling stuff, you always want to limit the size of the view.

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.)

Sticking a view to bottom of scrollview

I have a weird behavior I cant figure out - I'm trying to stick a view to bottom of scrollview with no luck. I've already tried clipToPadding and fillViewport but none of them help. Any help?
My xml layout is -
<LinearLayout>
<FrameLayout/>
<ScrollView>
<LinearLayout>
<LinearLayout/>
<RelativeLayout/> <-- This is the problematic view
</LinearLayout>
</ScrollView>
</LinearLayout>
I want to stick the relative layout to bottom even when the scroll view is shorter then the screen length, it does fit the screen when clipToPadding is set to false however the relativelayout just laid in middle of screen after the linearlayout which ontop of him, when I set the fillviewport to true on the scrollview (and remove the cliptopadding) the scrollview is longer than screen but unscrollable which lead to the relativelayout being "invisible", any suggestions?
You can try using ConstraintLayout inside ScrollView:
Set fillviewport in ScrollView to True
Last element must be attached on the bottom with constraints
Last but one element (one who goes before last) should have constraint set to last element. Also you can add margin to have minimum distance between this and last element and manage its position with constraintVertical_bias.
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
… Some elements with Constraints …
<TextView
android:id="#+id/last_but_one_element”
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="#+id/some_previous_element"
app:layout_constraintTop_toBottomOf="#+id/last_element"
android:layout_marginBottom=“40dp"
app:layout_constraintVertical_bias="0.1"/>
<Button
android:id="#+id/last_element”
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Try using a Relative layout instead of Linear layout inside scroll view and align the relative layout to bottom. But I'm not sure whether it will scroll after there is content.
<LinearLayout>
<FrameLayout/>
<ScrollView>
<RelativeLayout>
<LinearLayout android:alignParentTop="true" android:id="#+id/linearLayout" />
<RelativeLayout android:alignParentBottom="true" android:layoutBelow="#+id/linearLayout"/> <-- This is the problematic view
</LinearLayout>
</ScrollView>
Check if this works for you or not.

Why does view with layout_marginTop set and gravity=bottom placed inside horizontal LinearLayout cause margin to appear at the bottom?

I've tried to add view with layout_marginTop set and gravity=bottom inside a horizontal LinearLayout and it causes margin to appear at the bottom for no reason.
I'm aware that kind of layout could be build differently but I can't understand why I'm getting such result.
Here is the xml layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginTop="8dp"
android:background="#0000ff"
android:textSize="24sp"
android:text="TEST"/>
</LinearLayout>
And here is the result:
Any ideas with this one?
After digging through the source code, it seems that the root cause of this behavior is the fact that LinearLayout aligns its child Views by their baselines by default. When it measures out the vertical offsets for the child Views, it takes into account the sum of the vertical margins. These offsets are then applied after the "normal" top (y) coordinates are calculated for the child Views.
The upshot of all this is, if you want your TextView aligned right at the bottom, set the LinearLayout's baselineAligned attribute to false.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:background="#ff0000"
android:orientation="horizontal">
...
TextView is set with Its own LAYOUT GRAVITY to bottom, i.e. TextView's whole body is aligned to bottom, it won't make difference if orientation is horizontal, because it is linearLayout it won't work with marginTop when layoutGravity of View is set to Bottom, remove layoutGravity and keep marginTop it will come near to the top with mentioned margin.!
Why it is not on the base.?
because you have given it a margin and when you are giving it some value provided you have given it layoutGravity of Bottom already so it takes the bottom as it base align and gives margin from it.!
Hope i helped you partially if not completely.

Grow LinearLayout inside Scrollview for bigger phones on Android

I have a Scrollview for the complete screen, so in small phones you can scroll and see the complete form.
For big screen/hdpi phones, the screen has enough size so it fits.
The problem is that since its a LinearLayout, all views are at the top, and there is white space at the bottom.
I set the weight on one of the items items inside the linear layout, but it does not grow.
my code:
<ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
HEADER STUFF
</RelativeLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical" >
THIS PART I NEED TO GROW AS MUCH AS POSSIBLE SO THE FOOTER IS AT THE BOTTOM OF THE PAGE.
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/LightBlue"
android:orientation="vertical"
android:paddingBottom="10dp" >
FOOTER STUFF. NEED THIS TO BE AT THE FOOTER IF THE PHONE IS BIG ENOUGH.
</RelativeLayout>
<LinearLayout>
</ScrollView>
Simple answer: In the LinearLayout, change android:layout_height="wrap_content" to android:layout_height="match_parent".
Reason: By wrapping the content, you aren't giving the middle RelativeLayout with the most weight a chance to grow and fill the white space. Changing it to match the parent height gives it its space to blossom, so to speak.
Also, per Indiandroid's comment, you may want to put the ScrollView inside the LinearLayout and around the middle RelativeLayout that hosts the content so that the header and footer are fixed.
EDIT: If you intend to stretch the middle RelativeLayout by using weight in the LinearLayout, you will, after applying my previous part about switching the height to match_parent, have to move to ScrollView inside the LinearLayout around the middle RelativeLayout or it will grow indefinitely. This is because the ScrollView has no vertical bounds and by matching it's height with the LinearLayout, the LinearLayout also has no bounds.

Categories

Resources