Android textview: text flowing over RHS of screen - android

I have created a text view (mostly auto-created by Android Studio's 'new Tabbed Activity' helper) and populated it with some text stored as raw resources. I have allowed the view to scroll, and it's all working fine except that the last letter or two of many lines disappears off the edge of the screen. It's the same with two devices, with different screen sizes.
here's my resource file:
<?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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.main.PlaceholderFragment">
<TextView
android:id="#+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginTop="#dimen/activity_vertical_margin"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
android:layout_marginBottom="#dimen/activity_vertical_margin"
android:textSize="20sp"
android:scrollbars = "vertical"
android:justificationMode="inter_word"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#+id/constraintLayout"
tools:layout_constraintLeft_creator="1"
tools:layout_constraintTop_creator="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
I have tried enlarging the marginEnd (from 20 to 50dp) - no effect, and also tried android:gravity="center", but that made it worse!
[Edit]
As a result of suggested answers, the view has changed a little; a margin has appeared down the RHS so the text no longer disappears 'off the edge' of the screen, but it disappears 'behind' the margin. Does this help? Also , does the fact that this is a tabbed activity (as I mentioned earlier) have any bearing? If I scroll sideways, to see the next tab, the missing parts of the words still don't appear.

This happens because your TextView is not constrained to the right edge of the parents.
You can fix this by adding the following to your TextView
app:layout_constraintRight_toRightOf="parent"
But you should avoid using left and right in your layouts. So the correct way would be to add
app:layout_constraintEnd_toEndOf="parent"
And also change width to 0dp so that the TextView can take up the space defined by the constraints.

Use app:layout_constrainedWidth="true" on your textView with start and end constraint set to parent.

Related

How to place two texts in the middle, but so that the location depends on their length

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>

How to position a view in Android constraint layout outside the screen at the beginning

I have a View in a Constraint Layout and I would like that at the very beginning it should be outside of the screen (and then later slowly move into the scree from right to left). Now, I kind of need something like negative bias or margins.
I had a look at this question How to achieve overlap/negative margin on Constraint Layout?. The accepted answer using android:layout_marginTop="-25dp" does not have any effect (altough the top of the view is constrained and I use"androidx.constraintlayout:constraintlayout:2.1.3").
I tried the second most upvoted answer and used the code:
view.setTranslationX(view.getWidth() - 20);
This actually works. However, the problem is that when the Fragment is created you first see that the view is not on the left for a short period of time. This is not what I want. I would like to have the view beyond the right rim of the layout at the very very beginning such that it can later move into the layout.
Do you have any idea how I can do that? Ideally I would like to do this programmatically.
Update: Here is the code of the XML layout where a negative margin does not have any effect:
<?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:background="#drawable/game_test_background"
tools:context=".MainActivity"
android:id="#+id/constraintLayout">
<ImageView
android:id="#+id/imageView_RedRectange_Test"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_marginTop="-1250dp"
app:layout_constraintWidth_percent="0.25"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.048"
app:srcCompat="#drawable/red_rectangle" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.102"
app:layout_constraintHorizontal_bias="0.373"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.745"
app:layout_constraintWidth_percent="0.12" />
</androidx.constraintlayout.widget.ConstraintLayout>
Okay so to have a negative margin you can use translateX, translateY or TranslationZ.
in xml like so:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!"
android:translationX="-60dp"
android:translationY="-90dp"
android:translationZ="-420dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
or programmatically like so:
View view = ...;
view.setTranslationX(-60);
view.setTranslationY(-90);
view.setTranslationZ(-420);
Then in order to slowly bring it in from right to left you can use the animate() method like so:
View view = ...;
view.animate().setDuration(1000).translationX(-600).start();
There is a problem with setting the width of the button using app:layout_constraintWidth_percent when the ImageView has a negative margin. The problem should go away if you can set a definite width to the button (instead of 0dp).
The problem should also resolve if you set app:layout_constraintWidth_percent to a value such that the text of the button shows completely on one line.
Here is a simplified layout to demonstrate this issue. The ConstraintLayout has two views that are simply constrained to the parent to appear in vertical center of the layout. These two views have no dependencies on each other. In addition, the ImageView has a top margin of -250dp, so it should appear above the layout's vertical center.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/holo_green_light">
<ImageView
android:id="#+id/redRectangle"
android:layout_width="100dp"
android:layout_height="30dp"
android:layout_marginTop="-250dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/red_rectangle" />
<Button
android:id="#+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.12" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is what happens then the width of the ImageView is changed from 0dp to a non-zero value of 1dp. When the width is set to 0dp, the negative margin seems to be ignored. It is only when the width is set to a non-zero value that the ImageView is correctly placed.
Changing the width of the button should have no effect on the placement of the ImageView; however, the button only appears in the proper position when the button has a non-zero width.
Here is what happens when the app:layout_constraintWidth_percent is increased so that the word "Button" is not cutoff.
Again, the placement of the ImageView should be independent of the width of the button. Instead, the button only appears in the correct position when the app:layout_constraintWidth_percent is set such that the word "Button" is not cutoff.
This is only an issue with negative margins. Positive margins work as expected.
This is a strange problem, so you may want to use one of the other solutions mentioned.
(ConstraintLayout version 2.1.3)

ConstraintLayout hides the last line of TextView with app:layout_constrainedHeight="true"

I have noticed weird behavior of ConstraintLayout (version 1.1.3) that hides the last line of TextView whenever I try to use height with wrap_content property and layout_constrainedHeight is set to true.
With layout_constrainedHeight:
With no layout_constrainedHeight:
Source 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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="Lorem..."
app:layout_constrainedHeight="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
I thought that whenever I want to use wrap_content with ConstraintLayout I have to set layout_constrainedHeight to true, but this sometimes gives me weird bugs. Am I missing something?
EDIT
If I remove the margin around TextView, it works fine. It seems that ConstraintLayout does something wrong with wrap_content and margins.
According to documentation:
WRAP_CONTENT : enforcing constraints (Added in 1.1)
If a dimension is set to WRAP_CONTENT, in versions before 1.1 they will be treated as a literal dimension -- meaning, constraints will not limit the resulting dimension.
While in general this is enough (and faster), in some situations, you might want to use WRAP_CONTENT, yet keep enforcing constraints to limit the resulting dimension. In that case, you can add one of the corresponding attribute:
app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”
I'll emphasise a piece of this quote:
keep enforcing constraints to limit the resulting dimension
So basically, setting layout_constrainedHeight or layout_constrainedWidth to true will preserve constraints and reduce view size by the specified margin instead of pushing all the other views around and increasing current view height/width to fit the content.
Here is an example with app:layout_constrainedHeight="true" and app:layout_constrainedWidth=”true” and different margins. Red TextView wrapped it's content and after that was reduced in size. Green TextView has no app:layout_constrained...="true" attributes and margins set. Their height is equal, but the width is different in the end.
The layout:
<?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/top_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="86dp"
android:layout_marginEnd="26dp"
android:background="#ff2356"
android:text="#string/lorem_kind_of"
android:textColor="#android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedHeight="true"/> <!-- This line is the only difference -->
<TextView
android:id="#+id/bottom_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="86dp"
android:layout_marginEnd="26dp"
android:background="#009900"
android:text="#string/lorem_kind_of"
android:textColor="#android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/top_text_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
My guess is that you probably do not need to use app:layout_constrainedHeight attribute. You are welcome to leave a comment and we will elaborate on this further if my answer doesn't solve your problem.
UPDATE (22 May 2020)
Looks like the behaviour that you want is achievable only without the app:layout_constrainedHeight="true". I may be wrong, it depends on the final desired result, but according to my "experiments" looks like app:layout_constrainedHeight constraints a view from growing further it's minimum size.
I've updated the XML code and recorded a little video to see the difference.
Try adding one more constraint for bottom of your TextView - app:layout_constraintBottom_toBottomOf="parent" or app:layout_constraintBottom_toTopOf="#id/someOtherView" and this someOtherView should have bottom constraint to the parent. There should be a vertical chain in order app:layout_constrainedHeight to work.
Hope it helps.

Flat ConstraintLayout with layout_constraintHeight_min for each row doesn't work

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>

Constraint layout: Cannot separate elements and keep them all on screen

I have a simple constraint layout like this:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ListView
android:id="#+id/main_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/rescan"
/>
<android.support.v7.widget.AppCompatButton
android:id="#+id/rescan"
android:layout_width="wrap_content"
android:text="#string/rescan_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintBottom_toBottomOf="parent"
/>
</android.support.constraint.ConstraintLayout>
Despite the layout_constraintBottom_toTopOf="#id/rescan", the listview potentially extends halfway through the button.
To try and correct that, I added hardcoded dimensions (which I prefer not to do); to the listview:
android:layout_marginBottom="50sp"
And to the button:
android:layout_height="40sp"
android:textSize="20sp"
android:layout_margin="10sp"
However, I then got this (emulator pic, the design view also corresponds to this):
The button is halfway off the screen.
So I decided to remove the hardcoded dimensions and use a barrier. The developer page is ambiguous about how barrierDirection is supposed to work, but this
"constraintlayout.com" example makes it clear the direction should be the side you want the barrier on in relation to the elements listed in referenced_ids. Based on that, here's what I have inside the constraint layout:
<ListView
android:id="#+id/main_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/bottomBarrier"
/>
<android.support.constraint.Barrier
android:id="#+id/bottomBarrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="top"
app:constraint_referenced_ids="#id/rescan"
/>
<android.support.v7.widget.AppCompatButton
android:id="#+id/rescan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/rescan_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.5"
/>
But things are not really getting better...
It's impossible to tell, but here the barrier is at the very bottom. Which explains why the listview, with Bottom_toTopOf the barrier, also now extends all the way to the bottom.
However, that makes the whole barrier, who's direction is top and who's constrained id is the button, totally pointless. In no sense is it keeping the listview on one side and the button on the other. It's below both of them.
Even more berserk: If I change the direction to bottom and leave everything else as it, the barrier jumps to the very top, the listview jumps halfway off the screen upward, and the button stays in place.
I'm totally flummoxed. My two biggest questions are:
Why, in the first picture, does the listview extend halfway through the button, when it is set bottom-to-top of the button?
Why, in the last picture, is the barrier, with a direction of "top" and referencing the button id, below the button?
1.
Your ListView's height is set to wrap_content which means the view will compute its own size and constraints will not limit the dimension. ConstraintLayout-1.1.0 introduced new attributes that allow using wrap_content yet keep enforcing the constraints to limit the specified dimension. These attribues are:
app:layout_constrainedWidth="true"
app:layout_constrainedHeight="true"
2.
As for the Barrier, there's an error in the way you're referencing the view's id:
app:constraint_referenced_ids="#id/rescan"
This should be changed to:
app:constraint_referenced_ids="rescan"

Categories

Resources