ภาพรวม
ScrollView (Vertical ScrollView) เป็น ViewGroup ที่เลื่อนเนื้อหาในแนวตั้ง (Vertical) รองรับ **Child เดียวเท่านั้น** (ต้องห่อด้วย LinearLayout/ConstraintLayout) ใช้สำหรับหน้าที่เนื้อหายาวกว่าหน้าจอ
ขั้นตอนการสร้าง
1. Layout (res/layout/activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:scrollbars="vertical"
android:fadeScrollbars="true"
android:overScrollMode="always">
<!-- Child เดียว: LinearLayout แนวตั้ง -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="หัวข้อใหญ่: คู่มือการใช้งาน ScrollView"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="16dp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@drawable/sample_image_1"
android:contentDescription="รูปภาพตัวอย่าง 1"
android:layout_marginBottom="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="เนื้อหายาวๆ หลายๆ ย่อหน้า...\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
android:textSize="16sp"
android:lineSpacingExtra="4dp"
android:layout_marginBottom="24dp" />
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@drawable/sample_image_2"
android:contentDescription="รูปภาพตัวอย่าง 2"
android:layout_marginBottom="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="เนื้อหาต่อไป...\n\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
android:textSize="16sp"
android:lineSpacingExtra="4dp"
android:layout_marginBottom="24dp" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ปุ่มท้ายหน้า" />
</LinearLayout>
</ScrollView>
Attributes สำคัญ:
android:fillViewport="true"- ขยาย Child ให้เต็ม Viewport (ป้องกัน ScrollView สั้นกว่าจอ)android:scrollbars="vertical"- แสดง Scrollbarandroid:fadeScrollbars="true"- Scrollbar จางหายอัตโนมัติandroid:overScrollMode="always|ifContentScrolls|never"- พฤติกรรม Overscroll Effect
NestedScrollView (สำหรับ Nested Scrolling)
หาก ScrollView อยู่ใน CoordinatorLayout หรือมี Nested Scrolling (เช่น RecyclerView ใน ScrollView) ให้ใช้ **NestedScrollView**:
<androidx.core.widget.NestedScrollView
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout ...>
<!-- เนื้อหา รวมถึง RecyclerView ที่ตั้งค่า setNestedScrollingEnabled(false) -->
</LinearLayout>
</androidx.core.widget.NestedScrollView>
โค้ดควบคุม Scroll (Programmatic Scrolling)
ScrollView scrollView = findViewById(R.id.scrollView);
// เลื่อนไปด้านล่างสุด (Smooth)
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
// เลื่อนไปด้านบนสุด
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_UP));
// เลื่อนไปตำแหน่งเฉพาะ (Smooth)
scrollView.smoothScrollTo(0, targetY);
// เลื่อนไป View เฉพาะ
View targetView = findViewById(R.id.targetView);
scrollView.post(() -> {
int[] location = new int[2];
targetView.getLocationOnScreen(location);
// คำนวณ offset ตาม ScrollView
scrollView.smoothScrollTo(0, location[1] - scrollView.getTop());
});
Scroll Listener (ฟังเหตุการณ์ Scroll)
scrollView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
// scrollY = ตำแหน่งปัจจุบันจากด้านบน
// ใช้สำหรับ: Parallax, Hide/Show Toolbar, Load More, Progress Bar
if (scrollY == 0) {
// อยู่ด้านบนสุด
}
// ตรวจสอบถึงด้านล่าง
View child = scrollView.getChildAt(0);
if (child != null && scrollY >= (child.getHeight() - scrollView.getHeight())) {
// ใกล้ถึงด้านล่าง → Load more
}
});
ปัญหาที่พบบ่อยและแก้ไข
| ปัญหา | สาเหตุ | แก้ไข |
|---|---|---|
| Scroll ไม่ได้ / ไม่เลื่อน | Child height ≤ ScrollView height | เพิ่มเนื้อหา, หรือ android:fillViewport="true" |
| RecyclerView ใน ScrollView Scroll ไม่ได้ | Nested Scrolling Conflict | rv.setNestedScrollingEnabled(false) หรือใช้ NestedScrollView |
| Keyboard บังเนื้อหา | Soft Input Mode | AndroidManifest: android:windowSoftInputMode="adjustResize" |
| Scroll จERKY/Lag | Layout ซับซ้อน, รูปใหญ่ | ใช้ RecyclerView, ลด Nesting, ใช้ ConstraintLayout |
Best Practices
- Child เดียวเท่านั้น (ห่อด้วย LinearLayout/ConstraintLayout)
- ใช้
fillViewport="true"เพื่อให้ Child ขยายเต็มจอ - หลีกเลี่ยง ScrollView ซ้อน ScrollView / RecyclerView ใน ScrollView (ใช้ NestedScrollView)
- รูปภาพขนาดใหญ่ → ใช้ Glide/Picasso + override size, หรือ WebP
- สำหรับ List ข้อมูลเยอะ → ใช้ **RecyclerView** แทน (มี View Recycling)
สรุป
ScrollView = Vertical Scrolling Container สำหรับเนื้อหาคงที่/ไม่มาก ใช้ fillViewport=true, Child เดียว, หลีกเลี่ยง Nested Scrolling สำหรับ List ข้อมูล Dynamic ให้ใช้ RecyclerView (ซึ่ง Scroll เองได้แล้ว) NestedScrollView สำหรับ CoordinatorLayout/Collapsing Toolbar