Mobile Development 9 min read

AutoHeightViewPager: Dynamically Adjusting ViewPager Height in Android

The article introduces AutoHeightViewPager, a custom ViewPager subclass that measures the current page’s view height and overrides onMeasure to resize itself, interpolating between pages during scroll via an AutoHeightPager‑compatible adapter, thereby eliminating blank space or clipping and providing smooth height transitions for variable‑sized content.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
AutoHeightViewPager: Dynamically Adjusting ViewPager Height in Android

In many Android projects, the default ViewPager has a fixed height, which cannot adapt to varying content heights of its pages. This often results in unnecessary blank space or clipped content when page heights differ.

To address this, a custom AutoHeightViewPager is designed to measure the height of the currently displayed page and adjust its own height dynamically, ensuring that content is fully visible and eliminating extra whitespace.

Effect

The solution provides a smooth transition between pages with different heights, eliminating layout gaps. (Screenshots in the original article illustrate the before‑and‑after effect.)

Implementation Idea

Dynamic height adjustment: create a custom ViewPager subclass ( AutoHeightViewPager ) and override onMeasure to compute height based on the current page.

Listen to page scroll events: during scrolling, interpolate the height between the source and destination pages to achieve a smooth height transition.

Custom adapter: define a PagerAdapter that implements an AutoHeightPager interface, allowing the AutoHeightViewPager to obtain the view for any position and measure its height.

Implementation Code

AutoHeightViewPager (Kotlin)

package com.yxlh.androidxy.demo.ui.viewpager.vp

import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager

interface AutoHeightPager {
    fun getView(position: Int): View?
}

class AutoHeightViewPager @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : ViewPager(context, attrs) {

    private var lastWidthMeasureSpec: Int = 0
    private var currentHeight: Int = 0
    private var lastPosition: Int = 0
    private var isScrolling: Boolean = false

    init {
        addOnPageChangeListener(object : SimpleOnPageChangeListener() {
            override fun onPageScrolled(
                position: Int,
                positionOffset: Float,
                positionOffsetPixels: Int
            ) {
                if (positionOffset == 0f) {
                    isScrolling = false
                    requestLayout()
                    return
                }

                val srcPosition = if (position >= lastPosition) position else position + 1
                val destPosition = if (position >= lastPosition) position + 1 else position

                val srcHeight = getViewHeight(srcPosition)
                val destHeight = getViewHeight(destPosition)

                currentHeight = (srcHeight + (destHeight - srcHeight) *
                        if (position >= lastPosition) positionOffset else 1 - positionOffset).toInt()

                isScrolling = true
                requestLayout()
            }

            override fun onPageScrollStateChanged(state: Int) {
                if (state == SCROLL_STATE_IDLE) {
                    lastPosition = currentItem
                }
            }
        })
    }

    override fun setAdapter(adapter: PagerAdapter?) {
        require(adapter == null || adapter is AutoHeightPager) { "PagerAdapter must implement AutoHeightPager." }
        super.setAdapter(adapter)
    }

    private fun getViewHeight(position: Int): Int {
        val adapter = adapter as? AutoHeightPager ?: return 0
        return run {
            val view = adapter.getView(position) ?: return 0
            view.measure(
                lastWidthMeasureSpec,
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
            )
            view.measuredHeight
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        lastWidthMeasureSpec = widthMeasureSpec
        var heightSpec = heightMeasureSpec
        if (isScrolling) {
            heightSpec = MeasureSpec.makeMeasureSpec(currentHeight, MeasureSpec.EXACTLY)
        } else {
            getViewHeight(currentItem).takeIf { it > 0 }?.let {
                heightSpec = MeasureSpec.makeMeasureSpec(it, MeasureSpec.EXACTLY)
            }
        }
        super.onMeasure(widthMeasureSpec, heightSpec)
    }
}

AutoHeightPagerAdapter (Kotlin)

package com.yxlh.androidxy.demo.ui.viewpager

import android.view.View
import android.view.ViewGroup
import androidx.viewpager.widget.PagerAdapter
import com.yxlh.androidxy.demo.ui.viewpager.vp.AutoHeightPager

class AutoHeightPagerAdapter : PagerAdapter(), AutoHeightPager {

    private val viewList = mutableListOf
()

    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        val view = viewList[position]
        val parent = view.parent as? ViewGroup
        parent?.removeView(view)
        container.addView(view)
        return view
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        container.removeView(`object` as View)
    }

    fun setViews(views: List
) {
        viewList.clear()
        viewList.addAll(views)
        notifyDataSetChanged()
    }

    override fun getView(position: Int): View? {
        return viewList.getOrNull(position)
    }

    override fun getCount(): Int {
        return viewList.size
    }

    override fun isViewFromObject(view: View, `object`: Any): Boolean {
        return view == `object`
    }
}

Activity Usage (Kotlin)

package com.yxlh.androidxy.demo.ui.viewpager

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.yxlh.androidxy.R
import com.yxlh.androidxy.demo.ui.viewpager.vp.AutoHeightViewPager

class VpActivity : AppCompatActivity() {
    private var mAutoHeightVp: AutoHeightViewPager? = null
    private var mAdapter: AutoHeightPagerAdapter? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_vp)

        val viewList: MutableList
= ArrayList()
        viewList.add(LayoutInflater.from(this).inflate(R.layout.view_demo_1, null))
        viewList.add(LayoutInflater.from(this).inflate(R.layout.view_demo_2, null))

        mAutoHeightVp = findViewById(R.id.viewpager)
        mAutoHeightVp?.setAdapter(AutoHeightPagerAdapter().also { mAdapter = it })
        mAdapter?.setViews(viewList)
        mAutoHeightVp?.setCurrentItem(1)
    }
}

Layout XML

<?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">

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <com.yxlh.androidxy.demo.ui.viewpager.vp.AutoHeightViewPager
                android:id="@+id/viewpager"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:scaleType="fitXY"
                android:src="@drawable/vp_content" />
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>

Summary

By customizing ViewPager to adjust its height dynamically, layout problems caused by inconsistent page content heights are resolved. The approach provides smooth height transitions and is well‑suited for scenarios where page content varies in size.

AndroidKotlinCustom ViewDynamic HeightViewPager
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.