I am trying to rework one of my layouts using GridLayout and a bit stuck with one of the problems. Here is what I have now:
And this is my layout:
<GridLayout
style="#style/Widget.Card"
android:layout_width="match_parent"
android:layout_height="#dimen/card_height"
android:background="#color/news_card_background"
android:clipChildren="false"
android:clipToPadding="false"
android:columnCount="3"
android:rowCount="2" >
<ImageView
android:id="#+id/thumbnail"
android:layout_width="#dimen/card_thumbnail_width"
android:layout_height="match_parent"
android:layout_rowSpan="2"
android:background="#drawable/news_card_thumbnail_background"
android:contentDescription="#string/thumbnail"
android:cropToPadding="true"
android:scaleType="centerCrop"
android:visibility="visible" />
<com.inrix.twc.view.RobotoTextView
android:id="#+id/title"
style="#style/TextAppearenceNewsTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/news_title_horizontal_margin"
android:layout_marginRight="#dimen/news_title_horizontal_margin"
android:text="Title" />
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="right"
android:layout_rowSpan="2"
android:src="#drawable/audio_mrss"
tools:ignore="ContentDescription" />
<com.inrix.twc.view.RobotoTextView
android:id="#+id/footer"
style="#style/TextAppearenceNewsFooter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginLeft="#dimen/news_description_horizontal_margin"
android:layout_marginRight="#dimen/news_description_horizontal_margin"
android:text="description" />
</GridLayout>
But when one of the text views goes very long, it pushes my last column out of the screen:
The question is - how do I restrict my text to go beyond row's bounds, so my icon always stays right-aligned. So basically I'm trying to mimic RelativeLayout's toLeftOf attribute.
I realize I can easily do that with RelativeLayout, but it doesn't quite work for me because of other reasons. I hope it is possible to do with GridLayout
Thats not the target of gridlayout. I don't think that is even (easily) possible. Blockqoute from GridLayout limitations.
GridLayout does not provide support for the principle of weight, as defined in weight. In general, it is not therefore possible to configure a GridLayout to distribute excess space between multiple components.
Some common use-cases may nevertheless be accommodated as follows. To place equal amounts of space around a component in a cell group; use CENTER alignment (or gravity). For complete control over excess space distribution in a row or column; use a LinearLayout subview to hold the components in the associated cell group. When using either of these techniques, bear in mind that cell groups may be defined to overlap.
TextView needs weight to define his own size on the screen (using wrap content).
You can try to use relative layout or build a tree of linearlayout like this:
LinearLayout horizontal
ImageView #thumbnail
LinearLayout vertical weight 1
TextView #title
TextView #footer
ImageView #icon
Related
I'm trying to layout some images using GridLayout, in uneven sizes (I'm trying to implement StaggeredGrid
), but when I'm placing the first image in the first cell it stretches all over the screen.
How do I prevent it from doing this ?
<?xml version="1.0" encoding="UTF-8"?>
<GridLayout 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:columnCount="4"
android:rowCount="4"
tools:context=".MainActivity" >
<ImageView
android:layout_columnSpan="1"
android:layout_rowSpan="2"
android:src="#drawable/pic1" />
<ImageView
android:layout_columnSpan="2"
android:layout_rowSpan="1"
android:src="#drawable/pic2" />
</GridLayout>
Thanks,
Shmulik
Please use layout_column and layout_row to specify in which row and in which column of that row you want to place your ImageView.
<?xml version="1.0" encoding="UTF-8"?>
<GridLayout 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:columnCount="4"
android:rowCount="4"
tools:context=".MainActivity" >
<ImageView
android:layout_column="1"
android:layout_row="0"
android:src="#drawable/pic1" />
<ImageView
android:layout_column="2"
android:layout_row="0"
android:src="#drawable/pic2" />
</GridLayout>
Also reading the doc I found that we can have control over the stretch using Gravity. It says,
Excess Space Distribution:
GridLayout's distribution of excess space is based on priority rather than weight. A child's ability to stretch is inferred from the alignment properties of its row and column groups (which are typically set by setting the gravity property of the child's layout parameters). If alignment was defined along a given axis then the component is taken as flexible in that direction. If no alignment was set, the component is instead assumed to be inflexible.
Multiple components in the same row or column group are considered to act in parallel. Such a group is flexible only if all of the components within it are flexible. Row and column groups that sit either side of a common boundary are instead considered to act in series. The composite group made of these two elements is flexible if one of its elements is flexible.
To make a column stretch, make sure all of the components inside it define a gravity. To prevent a column from stretching, ensure that one of the components in the column does not define a gravity.
AND
To place equal amounts of space around a component in a cell group; use CENTER alignment (or gravity). For complete control over excess space distribution in a row or column; use a LinearLayout subview to hold the components in the associated cell group. When using either of these techniques, bear in mind that cell groups may be defined to overlap.
I imagine this should be a fairly easy one to answer, if you understand XML Layouts better than I do that is. I don't seem to get what I was thinking I should when using the match_parent layout_height.
I have a LinearLayout root element with android:orientation="vertical". Inside this LinearLayout I want three elements:
- TextView
- ListView
- TextView
For both the TextViews I set android:layout_height="wrap_content" so that they will be only as tall as is necessary to display their contents. The thing is, I want the one TextView to sit at the top of the form, the other one to sit at the bottom of the form while the ListView fills up whatever space is available on the form. So here is what my xml layout looks like:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="Top TextView" />
<ListView
android:id="#+id/listView_Species"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="Bottom TextView" />
But it doesn't work. Here's what I get. I've selected the ListView so that it will be highlighted. Notice how it extends all the way to the bottom of the form, pushing the bottom TextView off the form.
When I change the layout_height property of the ListView to some fixed value, like 180dp, this is what the form looks like. I'm just posting this to prove that the bottom TextView is there but I still don't know how to get it to be fixed to the bottom of the screen while the ListView takes up whatever space remains, but in between the two TextViews.
Thanks in advance.
While the other answers try to fix your problem (which they don't actually--they suggest you do something that looks similar but may or may not look good on different devices), no one has filled in the gaps in your knowledge of LinearLayouts and match_parent. And these gaps are very common--Google's documentation is still far below stellar.
First, how do Views work within a LinearLayout? Let's go through the process of drawing a LinearLayout, using orientation="vertical" for simplicity.
Examine the height of the first child of the LinearLayout (LL for short). If the height is match_parent or fill_parent (old name for the same thing) then the height of the View is stretched to fill the entire viewing area. If the height is wrap_content, then measure the vertical space the View takes and use that space for the View. If the height is a non-zero number, use exactly that many pixels for the View's height (may clip if too small). If the height is 0 see below.
Put the next view below the view in 1. Check its height and act accordingly.
Continue for all the Views. If a View is pushed off the bottom, go ahead and stop calculating because no one will see it or any succeeding Views (assuming no ScrollView).
If the height of a View is 0, check it's gravity. This requires a second pass, storing the gravity of all the views and then allocating their heights proportionally. As you can guess, the second pass doubles the time layout takes, which isn't significant for simple layouts.
Explanation of your example: The first child of the LL (the first TextView) is measured and takes a certain amount of pixels. Then your ListView takes all the remaining space (via match_parent). And then your second TextView is not drawn at all as it's off the bottom of the screen. Which is pretty much what you observed, but now you understand why.
Solution: Use RelativeLayout. Works perfectly in this case.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/top_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:textSize="30sp"
android:text="Top TextView" />
<TextView
android:id="#+id/bottom_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:textSize="30sp"
android:text="Bottom TextView" />
<ListView
android:id="#+id/listView_Species"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/top_tv"
android:layout_above="#id/bottom_tv"
/>
</RelativeLayout>
The RelativeLayout tells the layout inflater to draw the first TextView at the top, then draw the second TextView at the bottom, and then fill the rest of the space with your ListView. I believe this is exactly what you want.
Welcome to Android. You'll be using this pattern a LOT!
Change the ListView height to 0dp and add weight=1
i.e.:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="Top TextView" />
<ListView
android:id="#+id/listView_Species"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="Bottom TextView" />
use android:layout_weight to define weights to your widgets inside the outermost layout. Declare their height as 0dp and then define android:layout_weight to each one of them .
Total weigh sum of the three of them should be 1. According to your need you can deine 0.1 weight to both top and bottom TextView's and define 0.8 to ListView.
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight = "0.1"
android:textSize="30sp"
android:text="Top TextView" />
<ListView
android:id="#+id/listView_Species"
android:layout_width="match_parent"
android:layout_weight = "0.8"
android:layout_height="0dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:textSize="30sp"
android:layout_weight = "0.1"
android:text="Bottom TextView" />
I have a ListView in which each row is a GridLayout with two rows. I want each list item to be at least 48dp high (recommended minimum size for press targets) because each can be pressed, and I want the GridLayout to be vertically centered in the row if it doesn't expand it.
To do this, I've put the GridLayout inside a FrameLayout, set the FrameLayout to a minimum height of 48dp, and set the layout_gravity of the GridLayout to center. If I don't use the FrameLayout and just set the minimum height of the GridLayout, then I can't get the whole thing nicely vertically centered because of GridLayout's bogus space distribution.
So, using the FrameLayout gives me the desired outcome, but generates a Lint warning in my XML file, "This GridLayout layout or its FrameLayout parent is useless." Obviously it's not useless since it lines stuff up right, but is there a better way to do it that I missed?
Here is my XML (I cut out a bunch of TextViews in the GridLayout because they're not relevant to this situation):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="48dp"
>
<GridLayout
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical"
android:rowCount="2"
android:useDefaultMargins="true"
>
<TextView android:id="#+id/number"
android:layout_gravity="center_vertical"
android:layout_marginLeft="2dp"
android:layout_rowSpan="2"
android:minEms="1"
/>
<TextView android:id="#+id/a"
android:layout_gravity="center_vertical"
android:layout_rowSpan="2"
android:minEms="2"
/>
<TextView android:id="#+id/b"
android:layout_marginBottom="0dp"
android:minEms="3"
/>
<TextView android:id="#+id/c"
android:layout_marginTop="0dp"
android:minEms="3"
/>
...
</GridLayout>
</FrameLayout>
Unless you're seeing a performance problem, I'd just keep it that way. If you're seeing a performance problem, and it's because of the additional FrameLayout (you can tell that if the problem goes away if you use GridLayout directly), then (and only then) I'd try fixing the GridLayout directly.
If that's the case, then what I know is that GridLayout distributes excess space according to the principle of flexibility. If that principle is not enough to disambiguate, then you'd see the behavior where it gives the excess space to the rightmost column and bottom row. I'd try to play with the row/col attributes to make sure that it understands how to distribute that evenly. You can control that by setting the gravity of the children of the GridLayout, in your case the TextViews. Try setting them to 'center', and see if that helps. If not, more info at:
http://developer.android.com/reference/android/widget/GridLayout.html
We are writing an app targeting ICS+ and believe a GridLayout is the best layout paradigm, but it seems very little has been written about it, and we are having some alignment issues.
<GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/row_background"
android:rowCount="1"
android:columnCount="3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:useDefaultMargins="true"
android:background="#drawable/list_item_bg">
<ImageView
android:id="#+id/visibilityIcon"
android:layout_row="0"
android:layout_column="0"
android:src="#drawable/visibility_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
<ImageView
android:id="#+id/windIcon"
android:layout_row="0"
android:layout_column="1"
android:src="#drawable/wind_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
<ImageView
android:id="#+id/crosswindIcon"
android:layout_row="0"
android:layout_column="2"
android:src="#drawable/cloud_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</GridLayout>
However, the left 2 icons remain left-aligned, and the right-most icon centers with the remaining space.
Essentially what we need to do is specify the size of each column to be 1/3 (since 3 columns) of the total screen size. I thought this is what GridLayout did, but it appears 'wrap_content' causes this behavior (makes sense), but 'match_parent' causes the first column to fill the entire screen, rather than fill its cell which is the behavior I would have expected.
We seem to have tried every combination of gravity, layout_gravity, etc., but either we fundamentally are doing something wrong, or have found a limitation of the GridLayout.
Thanks for your help!
Only one row and one column is allowed to grow in a GridLayout, and that is the one with gravity along that axis. If more than one row or column specify gravity only one will get it (if I remember it is the "last" one). Choose another layout or write your own. If you only want a row with equally split icons you can use a LinearLayout where the widths of the components are 0px and the weight are all the same, e.g. 1.
I'm confused and frustrated that I can't get my EditText field to take up a rational amount of space in the layout without explicitly telling it how many pixels to be.
I'm *sure I'm missing something obvious, but my experience is that EditText totally ignores layout_weight and either grows/shrinks dynamically with the text that is entered into it if I give it a layout_weight of "wrap_content" or takes up most of the space in its parent layout if I give it a weight of fill_parent.
So... what is the correct path to having an EditText field that occupies some portion of its parent layout (in my case Linear, but I'm flexible) so that it can have a label next to it and look like:
Name: [ EDIT TEXT HERE ]
Phone:[ EDIT TEXT HERE ]
etc.
TIA
You can do a couple different things. As mentioned, you should be using dp instead of pixels for layout. Using dp allows your views to scale by the screen's physical size rather than resolution.
Here's an example of specifying the edit boxes to appear to the right of each label and take up the remainder of a the screen:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/name_label"
android:layout_width="100dp"
android:layout_height="50dp"
android:text="Name:" />
<TextView
android:id="#+id/phone_label"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_below="#id/name_label"
android:text="Phone:" />
<EditText
android:id="#+id/name_text"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_toRightOf="#id/name_label" />
<EditText
android:id="#+id/phone_text"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_toRightOf="#id/phone_label"
android:layout_below="#id/name_text" />
</RelativeLayout>
Here's an example of a LinearLayout where weight is used:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="Name:"
android:layout_weight="1"/>
<EditText
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="5"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="Phone:"
android:layout_weight="1"/>
<EditText
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="5"/>
</LinearLayout>
</LinearLayout>
Note that the LinearLayout has 7 views while the RelativeLayout accomplishes something similar with 5 views. LinearLayouts hare handy, but they're more complex. As your layouts get more complicated, they will perform worse than RelativeLayouts, especially when you nest them.
For each line use a horizontal LinearLayout.
Inside that, add a horizontal LinearLayout to 'wrap' the TextView. Give that LinearLayout a layout_weight of 20 (for example).
Use another horizontal LinearLayout to 'wrap' the EditText and set the EditText to fill_parent but give its outer LinearLayout a layout_weight of 80 (or whatever value based on 20+80 = 100% if you see what I mean).
EDIT: Also if you need to have multiple lines then to simplify the overall layout file, you can define a 'single line' layout file and use it as a custom layout entry.
//for your edittext set min width and max length
android:minWidth="40"
android:maxLength="30"
android:layout_width="wrap_content"
so that it will be always shows minimum width and your characters wont exceed more than 30.
You need to work with the Layout. LinearLayout is not the right Layout for your purposes. Have a look at TableLayout, which I think might fulfill your requirements. Have a look at the TableLayout tutorial.