I want to center align an ImageButton
in a ScrollView
and I'm quite happy with the result in portrait mode. However, I can't say the same for landscape mode and any help would be greatly appreciated.
I'm using two layout files here; the first one is the one I use for my MainActivity
class, called activity_main.xml
. The second one (called fragment_main.xml
) gets nested into into the activity_main.xml
when the MainActivity
loads the MainFragment
.
The preview for fragment_main.xml
looks quite promising for portrait and landscape mode:
fragment_main.xml in landscape
fragment_main.xml in portrait
And the rendering of activity_main.xml
in portrait mode looks just how I want it to look:
activity_main.xml in portrait
However, activity_main.xml
in landscape mode looks odd and I can't figure out why:
activity_main.xml in landscape
Here is the XML of activity_main.xml
with fragment_main.xml
merged into it as a child of the ScollView
:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:titleTextAppearance="@style/Toolbar.TitleText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<ScrollView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<!-- Content of fragment_main.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingStart="50dp"
android:paddingEnd="50dp"
tools:context=".MainFragment">
<ImageButton
android:id="@+id/overlay_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@null"
android:contentDescription="@string/start_speedometer"
android:scaleType="fitCenter"
android:src="@drawable/btn_circle_green"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:id="@+id/overlay_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:text="@string/start_speedometer"
android:textColor="@color/white"
android:textSize="40sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="false"
app:menu="@menu/nav_drawer_view"
app:headerLayout="@layout/nav_drawer_header"
app:itemIconTint="@drawable/nav_drawer_item_icon_color"
app:itemTextColor="@drawable/nav_drawer_item_text_color" />
</androidx.drawerlayout.widget.DrawerLayout>
I'm looking for a solution where I only have to make changes to fragment_main.xml
(that is, everything inside the ScollView
). The solution should be involving XML only.
I've tried fiddling with app:layout_constraintDimensionRatio
(W,1:1
, H,1:1
), app:layout_constrainedHeight
, app:layout_constraintHeight_max
, layout_constraintHeight_min
, many other constraints and even tried nesting the content of fragment_main.xml
in other layouts, moved from ConstraintLayout
to RelativeLayout
, LinearLayout
and FrameLayout
but none of them give me the desired result and ConstraintLayout
is the one that came the closest.
The only solution that worked so far was using app:layout_constraintWidth_default="percent"
and app:layout_constraintWidth_percent="0.5"
resulting in
desired look of activity_main.xml in landscape
While this might look great on a 16:9 phone it might result in the same issues as before on phones with different aspect ratio. So I'm looking for a more generic approach.
This is the content of the drawable btn_circle_green.xml
I'm using for the button:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/ic_circle_green_pressed" />
<item android:state_focused="true" android:drawable="@drawable/ic_circle_green_focused" />
<item android:state_selected="true" android:drawable="@drawable/ic_circle_green_focused" />
<item android:drawable="@drawable/ic_circle_green_default" />
</selector>
With each ic_circle_green_*
being a vector asset like this with differing colors:
<vector
android:viewportWidth="300"
android:viewportHeight="300"
android:height="120dp"
android:width="120dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/green"
android:pathData="M150,150m-140,0a140,140 0,1 1,280 0a140,140 0,1 1,-280 0"
android:strokeWidth="5"
android:strokeColor="@color/green_light"/>
</vector>
Many thanks in advance!
After I understood that the ScrollView
is the element that's causing me headaches (thanks again, @glucaio!) I found this answer and was able to solve my problem.
The ScrollView
received android:fitsSystemWindows="true"
, the inner ConstraintLayout
had its attributes for width and height switched (android:layout_width="wrap_content"
and android:layout_height="match_parent"
), received a paddingTop
and paddingBottom
(instead of paddingStart
and paddingEnd
) and was wrapped in a LinearLayout
:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center" />
This solution only works for landscape mode. So I'm using the one in this answer for landscape and the one from my question for portrait mode.
This is how the final XML for landscape mode looks like:
And this is the actual XML:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:titleTextAppearance="@style/Toolbar.TitleText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<ScrollView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<!-- Content of fragment_main.xml -->
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:paddingTop="30dp"
android:paddingBottom="30dp">
<ImageButton
android:id="@+id/overlay_button"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@null"
android:contentDescription="@string/start_speedometer"
android:scaleType="fitCenter"
android:src="@drawable/btn_circle_green"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:id="@+id/overlay_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:text="@string/start_speedometer"
android:textColor="@color/white"
android:textSize="40sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="false"
app:menu="@menu/nav_drawer_view"
app:headerLayout="@layout/nav_drawer_header"
app:itemIconTint="@drawable/nav_drawer_item_icon_color"
app:itemTextColor="@drawable/nav_drawer_item_text_color" />
</androidx.drawerlayout.widget.DrawerLayout>