I need to do exactly the same as in the picture below:
I have two TextView: Title and Description. The description is always short, so you can ignore it. But with the title it is more difficult. It can be both short and long. Moreover, with an increase in the number of characters, it should go up without shifting the description. How can this be implemented?
If you want to make it reactive, you can set it up in a ConstraintLayout like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:minHeight="300dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:padding="16dp"
android:background="#CCCCCC"
>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stuff"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/divider"
app:layout_constraintVertical_bias="1"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Description"
app:layout_constraintTop_toBottomOf="#id/divider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
How it basically works:
the Guideline is there to position the bottom of the text / top of the description. Obviously there are a lot of ways you could do this, depending on the description's length and appearance, and how you want the whole layout to look - this way always puts it right in the middle
the description is constrained between the Guideline and the bottom of the containing View, with a bias of 0% so it's pushed up against the Guideline
the text is constrained between the top of the View and the top of the Guideline, but with a bias of 100% so it's pushed down against the guideline.
the text uses a wrap_content height so it can expand. Because the bias keeps the bottom of the view against the Guideline, it expands up
the parent ConstraintLayout has a height of wrap_content so it can grow if there's enough text that it needs to expand to show it. It also has a minHeight to set a baseline height - basically you can define how it looks by default, but allow it to grow as necessary
If you don't want it to grow, and want the text area to be scrollable instead, you need to wrap it in a ScrollView instead - basically replacing the TextView in the layout
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:fillViewport="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/divider"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stuff"
android:gravity="bottom"
/>
</ScrollView>
And now you have a scrolling area that fills the space between the constraints, and the TextView in there has its content aligned to the bottom. The fillViewport property on the ScrollView makes it so the TextView expands to fill the visible area even when it doesn't have enough content, which keeps its bottom pinned to the bottom of the ScrollView where you expect the text to be
Give the TextView a fixed amount of dp of height, big enough to accommodate a long title. Also give it gravity="bottom|left".
Related
I need to limit the position of the TextView, depending on how much space it takes up (example in the screenshot below)
As you can see, in the left example, the Title is quite short, so both TextView are located in the middle.
In the case like the example on the right, the Title takes up a lot of space and goes up, but the Description goes down, but both TextView still remain in the middle. How can I do this using ConstraintLayout?
P.S.: Do not pay attention to the fact that I did not accurately draw examples, I used Paint :)
You'd need to chain both TextViews as "packed" to make both in the middle of the height. This would be applied to the top TV.
This also requires to constraints both TextViews to one another, the top TV to the screen top end, and the bottom TV to the screen bottom end.
As the top TV would be lengthy, you'd constraint its height using layout_constrainedHeight attribute so that you guarantee it won't go beyond the top screen end, or not to be greedy on the bottom TV.
Here it's assumed that the bottom TV text is short or it would be greedy on the top TV otherwise.
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/long_text"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#+id/description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="#+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="description"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#+id/title"
app:layout_constraintTop_toBottomOf="#+id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>
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.
I have what I think is a pretty common use case: I have multiple rows of information on the screen. I'm hoping to implement these views without having to use nested ViewGroups. Each row should have a minimum height, but expand if the contents are larger than the minimum height. The contents should be nested vertically.
It seems like this simplified example should work:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Row"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/view"
app:layout_constraintBottom_toBottomOf="#+id/view"/>
<View
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#8800ff00"
app:layout_constraintHeight_min="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="#id/textView"/>
</android.support.constraint.ConstraintLayout>
Instead of creating a ViewGroup for each row, I have a flat layout with constraints. A View for each row sets the row height and can be clickable. The views inside the row are siblings in the layout hierarchy, but center themselves vertically in the middle of the View for that row. Since I've specified app:layout_constraintHeight_min and anchor the bottom of the View to the bottom of the contents, it should grow with the contents.
But there's a problem:
ConstraintLayout adds undesired spacing above the row! Note that the unwanted spacing above the row is equal to the correct spacing between the bottom of the contents and the bottom of the row.
My theory is this: since the View's bottom is anchored to the bottom of the contents (the TextView) it wants to stick tightly to that and be right next to it. If I force it to move further away, it adds something like a bottom margin to accomplish that, it adds a similar top margin to be symmetrical.
How do I make it stop? If it wasn't for that unwanted spacing on the top, I'd have exactly what I need. Perhaps there's some special ConstraintLayout trick, some magical attribute to fix this behavior. Or maybe there's a completely different way to use ConstraintLayout to accomplish the UI I want.
I realize that using fixed-height rows would make this much simpler, but I don't like doing that if the contents can grow.
I could change my UI to have a nested ConstraintLayout for each row, but I'd rather not do that after working so hard to make a complex layout completely flat, without multiple layers of ViewGroups. But that's what I'll do if I can't find a better solution, which I hope to find here.
I think removing the min height and adding some margin in the TextView will do the thing and instead of using match_parent you can use constraints if possible
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="50dp"
android:text="Row is not column"
android:textColor="#283858"
app:layout_constraintBottom_toBottomOf="#+id/view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/view"/>
<View
android:id="#+id/view"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#8800ff00"
app:layout_constraintBottom_toBottomOf="#id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
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" />
Today I have been playing a bit with the LinearLayout and have been suprised with the results:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Big Text"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/textView2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium" />
<Button
android:id="#+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="Button"
/>
</LinearLayout>
This is a simple layout with a text view header, then a text view that I want it to cover all parent (but the space occupied by the bottom button) and a button that is placed at the botton side with the layout_gravity="bottom".
This produces a layout where header is shown correctly, center text view covers all remaining free space and the button does not appear. Why is this? Shouldn't the center text view just calculate its size taking into account the bottom button size?.
Use layout_weight="1" in your center TextView.
Always remember thumb rule
If you are using linear layout with vertical orientation as soon as it finds the control
with
android:layout_height="match_parent"
The layout will ignore all the controls present below it.
Hope this help
Vipul
In place of this
android:layout_width="match_parent"
android:layout_height="match_parent"
use 'wrap_content' like this
android:layout_width="wrap_content"/"fill_parent"
android:layout_height="wrap_content"/"fill_parent"
Shouldn't the center text view just calculate its size taking into account the bottom button size?.
No, because you tell the second TextView to match its parent's height, thus FILL_PARENT and hence it will fill up all remaining space, leaving none for the last TextView.
(...) and a button that is placed at the botton side with the layout_gravity="bottom".
Unfortunately, that's not how a LinearLayout works. If you set the orientation to vertical, basically only the left and right gravities will have effect. Vice versa, with the (default) horizontal orientation, only top and bottom work. The orientation determines in which direction the View children are dynamically positioned in order, which implies you cannot manually change the 'position' in that direction.
Now, to get the desired effect, you can give the second TextView a height of 0dp and a weight of 1, resulting in it dynamically filling up all remaining space without pushing the third TextView off the bottom. Alternatively, you can use a RelativeLayout, with which you can directly set the position, and simply instruct the middle TextView to sit below the first, but above the last.