Horizontal and Vertical ScrollView

ScrollView scrolls vertically, HorizontalScrollView sideways, each around exactly ONE direct child (so wrap a layout), and neither should ever swallow a ListView.

10 min read · 9 cards · 2 checks

Read in: English · हिन्दी · ગુજરાતી


Theory

The rules nobody could read

The Garba Night details screen grew: poster, description, timing, dress code, 12 rules. On your emulator it fit. On a classmate's smaller phone, everything after rule 4 simply did not exist: no error, no hint, just clipped.

Android layouts do not scroll by themselves; content past the screen's edge is silently invisible. The fix is a wrapper whose whole job is scrolling: ScrollView: plus its sideways twin, and one strict law about children.

Theory

The two scrollers, formally

ScrollView scrolls its content vertically: the reading direction: long forms, detail pages, rules.

HorizontalScrollView is a separate widget for sideways travel: photo strips, sponsor logos, a row of filter chips.

One widget, one axis: vertical ScrollView does not scroll sideways, and there is no both-axes scroller in the classic toolkit (nest one inside the other for the rare map-like need).

Sizing habit: the scroller gets match_parent height; its content wraps: wrap_content.

Formula

The one-child law

A ScrollView (and its horizontal twin) may hold exactly ONE direct child.

Two TextViews placed directly inside it will not even inflate: IllegalStateException: ScrollView can host only one direct child.

The pattern, always: put ONE layout inside (a vertical LinearLayout for ScrollView, a horizontal one for HorizontalScrollView), and stack everything inside THAT. One wrapper, one child, unlimited grandchildren.

Practical

event_details.xml: the scrolling page + sponsor strip

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- the ONE direct child -->
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView android:id="@+id/poster"
            android:layout_width="match_parent"
            android:layout_height="220dp" />

        <TextView android:id="@+id/description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <!-- sideways strip inside the vertical page -->
        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <LinearLayout android:orientation="horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <!-- sponsor ImageViews line up here -->
            </LinearLayout>
        </HorizontalScrollView>

        <TextView android:id="@+id/rules"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </LinearLayout>
</ScrollView>

This example runs in Gri-Learn on the web, where you can edit it and see the output.

Quiz

A layout places 2 TextViews directly inside a ScrollView. What happens when the screen opens?

  1. Both show; the ScrollView stacks them vertically like a LinearLayout
  2. The app crashes at inflate time: ScrollView can host only one direct child
  3. Only the first TextView shows; the second is ignored with a warning
  4. They overlap, both anchored to the top
Show the answer

The app crashes at inflate time: ScrollView can host only one direct child

The one-child law is enforced with an IllegalStateException the moment the XML inflates: the screen never appears. ScrollView is a scrolling WRAPPER, not a stacking layout: arranging children is a LinearLayout's job, which is exactly why the canonical structure is ScrollView holding one LinearLayout holding everything. Option A grants ScrollView abilities it deliberately lacks; options C and D describe mercies (silent ignoring, overlap) that other containers show but ScrollView refuses. One wrapper, one child: the grandchildren are unlimited.

Think first

The scroller that ate the ListView

A classmate wraps last lesson's event ListView inside the details ScrollView so the whole page scrolls together. Two distinct things go wrong. Reason them out from how each widget scrolls, then tap.

Show the answer

Gesture war: both widgets claim vertical swipes; the touch goes to one and the other never scrolls properly: the list becomes a stunted strip. Recycling dies: inside a ScrollView, the ListView is measured to its FULL content height (the scroller must know its child's size), so all 10000 rows inflate at once: the thali system from the ListView lesson collapses into 10000 plates. The rule: a ListView scrolls ITSELF, never wrapped in a scroller; long mixed pages either put extra content in header/footer views of the list, or avoid the mix.

Watch out

Scroll traps

The invisible clipping: no scroller means content past the screen edge silently vanishes: test on a SMALL screen, not just your emulator.

Wrong axis: text in a HorizontalScrollView will not wrap and walk off sideways; reading content wants the vertical one.

Short content, ugly gap: a ScrollView whose content is shorter than the screen leaves dead space; android:fillViewport="true" stretches the child to fill.

Scroller inside scroller (same axis): the ListView story again: any two same-axis scrollers fight.

Theory

Scrolling is a wrapper, not a property

The design insight worth keeping: in Android, scrolling is not a checkbox on a layout: it is a CONTAINER you deliberately add, one axis at a time, around exactly one child. (Flutter, waiting in Unit 3, makes the same move with SingleChildScrollView: the name confesses the same law.) Next lesson the search box learns to think: AutoCompleteTextView returns from BCA305-02 for a deeper second pass, with TextWatcher's full machinery.

Summary

Key takeaways

  • ScrollView scrolls vertically; HorizontalScrollView is the separate sideways widget: one axis each.
  • Each may hold exactly ONE direct child: wrap content in a single LinearLayout inside it.
  • Two direct children = IllegalStateException at inflate: the screen never opens.
  • Never wrap a ListView in a ScrollView: gestures fight and recycling breaks.
  • fillViewport="true" stretches short content to fill the screen.
  • Content past the screen edge without a scroller is silently clipped: test small screens.
  • Memory hook: one wrapper, one axis, one child.

Study this properly

This page is the lesson to read. In Gri-Learn the same topic is a graded deck: the self-checks are scored and your weak topics are tracked. Free to start.

Start this topic

Already have an account? Sign in

More from Basic Attributes and Events of Important Android Widgets (UI)

Gri-Learn · syllabus-mapped B.C.A. lessons in English, Hindi and Gujarati

Horizontal and Vertical ScrollView · Mobile Application Development - 2 (option B) · Gri-Learn