layout_gravity="center_vertical" not working - android

My problem
I can't seem to figure out why android:layout_gravity="center_vertical" is not working in my case.
Please note that I am not interested in solving the problem of making it vertically aligned, so much so that I am trying to understanding exactly what is the cause of this behavior itself for pedagogical reasons.
My code
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.LinearLayoutCompat
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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/blue_100">
<android.support.v7.widget.AppCompatTextView
android:background="#color/red_100"
android:text="HELLO WORLD"
android:textSize="18sp"
android:textColor="#color/black"
android:gravity="center_vertical"
android:maxLines="2"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.v7.widget.LinearLayoutCompat>

A LinearLayout with vertical orientation will ignore the vertical component of any child's android:layout_gravity attribute. Similarly, a LinearLayout with horizontal orientation will ignore the horizontal component of a child's android:layout_gravity.
For the special case of the center gravity value, it's helpful to know that this behaves identically to center_horizontal|center_vertical, down to the actual integer value of the constants (0x11 vs 0x01|0x10). As such, using android:layout_gravity="center" in a vertical linear layout will be equivalent to just android:layout_gravity="center_horizontal".
As for why, consider this:
<LinearLayout orientation=vertical>
<View layout_gravity=top/>
<View layout_gravity=bottom/>
</LinearLayout>
What would you do here? If you position the first view at the top of the screen and the second view at the bottom of the screen, then you're not really a linear layout.
Even though you said you're not interested in how to solve the problem, you can do so with the android:gravity attribute on the LinearLayout itself. This will cause the LinearLayout to stack all the views together at the top, center, or bottom of the linearlayout.

Related

How can I ignore a child element's margins?

Background
I'm developing a custom notification layout by injecting an OS-generated notification view into my own layout. My layout must be as short as possible, which is 50dp in Android 10.
Problem
The view that I'm injecting into my view has margins that cause it to stretch my layout from 50dp to 66dp.
Code
The following layout is a simplification of what's going on to demonstrate the problem:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="#+id/activity_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="50dp"
android:orientation="horizontal"
>
<TextView
android:id="#+id/full_height_view"
android:layout_width="10dp"
android:layout_height="match_parent"
android:background="#android:color/holo_blue_bright"
/>
<TextView
android:id="#+id/bad_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:layout_marginBottom="20dp"
android:background="#android:color/holo_red_light"
>
</TextView>
</LinearLayout>
</FrameLayout>
Note that container has minHeight of 50dp, which I don't want to be exceeded. The problem is the margins from bad_view sum up to 66dp and stretch the parent to 66dp.
Question
How can I prevent the margins on bad_view from stretching the parent beyond its minimum height? I cannot set a fixed height on the parent because the exact height is OS-dependent. And I cannot modify bad_view because it's generated by the OS.
I ended up solving this by setting the visibility of bad_view to gone.
I found another solution: use GridLayout vertical weights to override the problematic view's height. This gave me the flexibility of a weighted horizontal LinearLayout but with the addition of vertical weights.

Nested View forcing it's parent's size to match_parent instead of wrap_content

I am wondering if it is possible to make one View adjust it's height to it's parent height when the parent has layout_height="wrap_content". In following example:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/red"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/time"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello"/>
</RelativeLayout>
the nested ImageView makes RelativeLayout expand it's height to match it's own parent. What I would like to achieve is that RelativeLayout has height equal to the one of the inner TextView and ImageView should work here as a background spanning only behind the text. Is this possible in pure XML without Java code tricks?
If you use a RelativeLayout (as you do), you can have Views or ViewGroups layout referencing other Views or ViewGroups by id.
In your case, if you assign an id to your TextView android:ud="#+id/tvHello" then you can align the top and bottom of your ImageView to that TextView:
android:layout_alignTop="#+id/tvHello"
android:layout_alignBottom="#+id/tvHello"
For completeness, here's your layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/red"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_alignTop="#+id/tvHello"
android:layout_alignBottom="#+id/tvHello"
android:src="#drawable/time"/>
<TextView
android:id="#+id/tvHello"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello"/>
</RelativeLayout>
As you can see, I made the height of the ImageView 0dp. I could've chosen for wrap_content or maybe even match_parent, but because the alignTop and alignBottom overrules it's, it is better to give the view a fixed height. This is due to performance. Now android doesn't have to measure the height of the ImageView, before it's to decide to make it the same height as your TextView after all.
One other way (and perhaps better - depending on how you want your images to scale), is to attach a drawable as background to your RelativeLayout. As such:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/time"
android:orientation="vertical">
I hope this helps you out a little bit and it's helpful (it is my first time answering on stackoverflow).
happy coding!

ScrollView not scrolling to the end of inner LinearLayout's bottom margin

I'm having issues with a Fragment consisting of a ScrollView containing a LinearLayout. I'm trying to create an effect where the LinearLayout has a white background and looks like a piece of paper scrolling on a coloured background. The way that I'm trying to achieve this is by having the ScrollView occupy the full space of the fragment and then the LinearLayout inside has android:layout_margin="16dp" to create the space around the "paper".
This way, the scroll bar of the ScrollView appears in the coloured background area, the margin at the top scrolls away along with the content and the margin at the bottom only scrolls in when one reaches the end.
Unfortunately in this configuration the ScrollView won't scroll all the way to the end and in fact cuts off a very small amount of the text at the bottom. I suspect that the ScrollView isn't taking into account its child's margins in its vertical scrolling distance. To solve this I've wrapped the LinearLayout in a FrameLayout which solves the issue, but seems superfluous. Any pointers on how to eliminate this unneeded container would be appreciated.
Note: setting android:padding="16dp" on the ScrollView and scrapping the margins doesn't have the desired effect, as then the padding appears on all four edges continuously, regardless of scroll position.
<?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="fill_parent"
android:layout_height="wrap_content"
tools:context=".ArticleFragment" >
<!-- This FrameLayout exists purely to force the outer ScrollView to respect
the margins of the LinearLayout -->
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:layout_margin="16dp"
android:background="#color/page_background" >
<TextView
android:id="#+id/article_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textIsSelectable="true" />
<TextView
android:id="#+id/article_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true" />
</LinearLayout>
</FrameLayout>
</ScrollView>
I remember having trouble with the ScrollView somehow not making it to the end of it's contents when the content layout had a top margin. I solved the problem with a little hack, adding an empty view to the end of the LinearLayout:
<?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="fill_parent"
android:layout_height="fill_parent"
tools:context=".ArticleFragment" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:layout_margin="16dp" >
<TextView
android:id="#+id/article_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textIsSelectable="true"
android:background="#color/page_background" />
<TextView
android:id="#+id/article_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:background="#color/page_background" />
<!-- Add a little space to the end -->
<View
android:layout_width="fill_parent"
android:layout_height="30dp" />
</LinearLayout>
</ScrollView>
I used a similar empty view also in the beginning of the LinearLayout to avoid top/bottom margins completely.
EDIT: I just realised that you also wanted the end of the "paper" to show up when reaching the end of the view. In that case you might want to set the background color to the TextViews instead of the layout itself. Then make sure there's no margin between the title and the article (use padding as separation).
EDIT2: I should learn to check when the questions were asked... Well, maybe this still helps someone. :)
The question is old, but I've had issues with ScrollView being ill-behaved when wrapping FrameLayout. It also doesn't seem to consider the margins of contained layouts. You could replace the FrameLayout with another single-child LinearLayout, which should measure correctly. I would've removed the FrameLayout completely and simply added summed margin with padding on the inner layout:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="26dp"
android:background="#color/page_background" >
<!--...-->
I'd get rid of the FrameLayout and make the LinearLayout the child of the ScrollView with match_parent set as the layout height. If that still doesn't work, consider replacing the bottom margin with another arbitrary View with the desired height and background as the last item in the LinearLayout.

Left aligning images in LinearLayout using gravity or linear_gravity in XML

First off, this is not a duplicate question, to best of my ability I've tried all (there are many) similar questions. Solutions to such problems appear to be very subjective, specific to a given scenario.
My layout currently appears as follows. Black boxes are images (logo and body, respectively), colours represent each layout:
My XML:
<?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="#000"
android:padding="0px"
android:layout_margin="0px"
android:orientation="vertical">
<LinearLayout
android:layout_weight="16"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFF"
android:gravity="top|center"
android:orientation="horizontal">
<ImageView
android:id="#+id/logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/logo"
android:layout_gravity="top|center" />
</LinearLayout>
<LinearLayout
android:layout_weight="4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00F"
android:gravity="bottom|left"
android:orientation="vertical">
<ImageView
android:id="#+id/body"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/body"
android:layout_gravity="bottom|left" />
</LinearLayout>
</LinearLayout>
Here you can see I have a parent linear layout, split into two children linear layouts. This is because I need the images to be positioned differently within that part of the page.
In a nutshell, I need logo to be vertically aligned to the top, and body horizontally aligned to bottom-left.
Now, a few things that I've tried:
Using RelativeLayout rather than Linear
Switching gravity with layout_gravity for both LinearLayout and ImageView, along with combinations of excluding each
Fairly confident match_parent for width and height is what I want, but I have tried different combinations with wrap_content
What I've come to understand:
gravity:top requires the parent view use orientation:horizontal
gravity:left requires the parent view use orientation:vertical
gravity applies to the children of the view
linear_gravity applies how the child aligns with it's parent
Using the same value for gravity on the parent and linear_gravity on the child might have the same effect (when using one instead of the other)?
Hopefully this is enough information. I'm having a very difficult time wrapping my head around how these layouts work.
Thank you SO much for the help!
I think your problem is you are setting dimensions of the image views to match_parent. I would use a RelativeLayout as it seems to be the most efficient in your case (pseudo-XML-code):
RelativeLayout (width=match_parent, height=match_parent)
ImageView (width=wrap_content, height=wrap_content,
alignParentTop=true, centerHorizontal=true)
ImageView (width=wrap_content, height=wrap_content,
alignParentBottom=true, alignParentLeft=true)
You don't need any gravity setting here. You might want to play with the scaleType attribute depending on your image sizes.

LinearLayout, RelativeLayout, etc. margins do not work as expected

Margins in group layouts do not seem to work.
For example,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_margin="40dip"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="I'm a button" />
</LinearLayout>
should display a button with 40p margins on all sides. However, it has 80p margins on the right and bottom.
Am I doing something wrong?
Is this a bug?
A workaround would be to use gravity, but this only works with even margins.
BTW, there is a similar question posted here but has not been answered.
android:padding="40dp" on the LinearLayout or android:layout_margin="40dp" on the Button will give you the effect you want. Padding defines the space between a views edges and its content, layout margin defines extra space on the sides of a view.
The problem is actually the way FrameLayout interprets margins. setContentView() attaches your "main" layout to a FrameLayout, which is the actual root of the view hierarchy (you can see that with Hierarchy Viewer) and is offered to you by the phone.
Margins are managed by the parent layout, so in this case that main FrameLayout. I don't know if it's a feature or a bug, but that's how this layout interprets margins.
So well, the solution was already posted while I was typing: use padding instead.
if you need set margin for a layout, simply wrap it with another linear or relative layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_margin="40dip"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="I'm a button" />
</LinearLayout>
</LinearLayout>
Wrapping the Linear Layout with another layout is the best strategy.

Categories

Resources