Inside a ScrollView I am dynamically switching between two fragments with different heights. Unfortunately that leads to jumping. One can see it in the following animation:
- I am scrolling down until I reach the button "show yellow".
- Pressing "show yellow" replaces a huge blue fragment with a tiny yellow fragment. When this happens, both buttons jump down to the end of the screen.
I want both buttons to stay at the same position when switching to the yellow fragment. How can that be done?
Source code available at https://github.com/wondering639/stack-dynamiccontent respectively https://github.com/wondering639/stack-dynamiccontent.git
Relevant code snippets:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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/myScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="800dp"
android:background="@color/colorAccent"
android:text="@string/long_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button_fragment1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:text="show blue"
app:layout_constraintEnd_toStartOf="@+id/button_fragment2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<Button
android:id="@+id/button_fragment2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:text="show yellow"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/button_fragment1"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button_fragment2">
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.example.dynamiccontent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// onClick handlers
findViewById<Button>(R.id.button_fragment1).setOnClickListener {
insertBlueFragment()
}
findViewById<Button>(R.id.button_fragment2).setOnClickListener {
insertYellowFragment()
}
// by default show the blue fragment
insertBlueFragment()
}
private fun insertYellowFragment() {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, YellowFragment())
transaction.commit()
}
private fun insertBlueFragment() {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, BlueFragment())
transaction.commit()
}
}
fragment_blue.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#0000ff"
tools:context=".BlueFragment" />
fragment_yellow.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="20dp"
android:background="#ffff00"
tools:context=".YellowFragment" />
HINT
Please note that this is of course a minimum working example to show off my issue. In my real project, I also have views below the @+id/fragment_container
. So giving a fixed size to @+id/fragment_container
is not an option for me - it would cause a large blank area when switching to the low, yellow fragment.
UPDATE: Overview of proposed solutions
I implemented the proposed solutions for testing purposes and added my personal experiences with them.
answer by Cheticamp, https://stackoverflow.com/a/60323255
-> available in https://github.com/wondering639/stack-dynamiccontent/tree/60323255
-> FrameLayout wraps content, short code
answer by Pavneet_Singh, https://stackoverflow.com/a/60310807
-> available in https://github.com/wondering639/stack-dynamiccontent/tree/60310807
-> FrameLayout gets the size of the blue fragment. So no content wrapping. When switching to the yellow fragment, there's a gap between it and the content following it (if any content follows it). No additional rendering though! ** update ** a second version was provided showing how to do it without gaps. Check the comments to the answer.
answer by Ben P., https://stackoverflow.com/a/60251036
-> available in https://github.com/wondering639/stack-dynamiccontent/tree/60251036
-> FrameLayout wraps content. More code than the solution by Cheticamp. Touching the "show yellow" button twice leads to a "bug" (buttons jump down to the bottom, actually my original issue). One could argue about just disabling the "show yellow" button after switching to it, so I wouldn't consider this a real issue.