This is a very simple question.
I want to have an ImageView and TextView centered, so that the TextView (and optionally more views with it) is below the ImageView, yet should always be shown (ImageView can scale down if needed, just keep aspect ratio).
For some reason, all of the ways I've thought about don't work. There is always a scenario (usually easy to reproduce by changing orientation to have less space available) that the TextView isn't shown.
ic_android_red.xml - sample image instead of the original one.
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="400dp"
android:tint="#FF0009" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="400dp">
<path android:fillColor="#FF000000"
android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
</vector>
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent" android:gravity="center"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_android_red"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
</LinearLayout>
On portrait, it worked fine:
but on landscape, it could avoid showing the TextView completely:
I tried to wrap it with NestedScrollView
with android:fillViewport="true"
, but other than at least scrolling to it, it doesn't help any more.
I tried to have the LinearLayout split into 2 halves (using weights and height of 0px), so that the top will have the ImageView set to the bottom of it (I used a FrameLayout to contain it), and the bottom will have the rest with its content aligned to the top and centered horizontally. But then it looks weird too, maybe because they are not really together anymore.
I tried to use ConstraintLayout
, so that the views will be chained together using app:layout_constraintVertical_chainStyle="packed"
, and that the bottom part (the TextView) would have app:layout_constraintHeight_min="wrap"
. Didn't work either.
Is there an easy way to overcome it? To show both of the Views in the center, but with priority to the TextView ?
Currently, the only solution that is close to be considered as working is the 2 halves solution.
This can be accomplished with ConstraintLayout
using a vertical packed chain and an ImageView
with match_constraints
(0dp
) for its width and wrap_content
for its height. The image view will also have the following set to keep the height within constraints when the image is taller than it is wide. (See "Dimension Constraints")
app:layout_constrainedHeight="true"
The vertical packed chain will group the ImageView
and TextView
together and centered vertically within the layout.
match_constraints
on the ImageView
will permit the image to expand and contract to match boundaries while permitting room for the TextView
that is placed directly beneath the image. If you don't want the image to change size unless needed, then set the width of the ImageView
to wrap_content
instead of match_constraints
and set app:layout_constrainedWidth="true"
.
An earlier post was predicated on a 1:1 aspect ratio of the image. This solution does not place a restriction on the aspect ratio. Here are some images of the layout working:
Portrait mode with image width > height
Landscape mode with image width > height
Portrait mode with image height > width
Landscape mode with image height > width
I also added some margins to better see the true extents of the views but the margins can be removed. The size of the text in the TextView
was also increased to make it more visible but can be any size.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ImageView width could also be android:layout_height="wrap_content"
with app:layout_constrainedWidth="true"-->
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="@drawable/vertical_image" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Hello World!"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
</androidx.constraintlayout.widget.ConstraintLayout>
The key aspect of the layout is the use of app:layout_constrainedHeight=”true" which limits the height of the
ImageView`. From the
documentation for ConstraintLayout::
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”