Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีสร้าง HorizontalScrollView แบบ Gallery ใน Android

ภาพรวม

HorizontalScrollView เป็น ViewGroup ที่เลื่อนเนื้อหาในแนวนอน (Horizontal) ใช้แสดงรายการภาพ, Card, หรือ Item แบบ Carousel/Gallery เหมาะสำหรับรายการที่ไม่คงที่หรือไม่ต้องการ RecyclerView

ขั้นตอนการสร้าง

1. Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Horizontal Gallery Demo"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_marginBottom="16dp" />

    <HorizontalScrollView
        android:id="@+id/horizontalScrollView"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scrollbars="none"
        android:fillViewport="false"
        android:overScrollMode="never">

        <LinearLayout
            android:id="@+id/container"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:paddingStart="16dp"
            android:paddingEnd="16dp">

            <!-- Item Views จะเพิ่มด้วยโค้ด หรือ Hardcode ได้ -->

        </LinearLayout>

    </HorizontalScrollView>

</LinearLayout>

2. Item Layout (res/layout/item_image.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="150dp"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_marginEnd="12dp"
    android:background="@drawable/item_background"
    android:padding="8dp">

    <ImageView
        android:id="@+id/ivImage"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:scaleType="centerCrop"
        android:contentDescription="@string/image_desc" />

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:textSize="12sp"
        android:maxLines="1"
        android:ellipsize="end"
        android:gravity="center" />

</LinearLayout>

3. Drawable Background (res/drawable/item_background.xml)

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="https://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF" />
    <corners android:radius="12dp" />
    <stroke android:width="1dp" android:color="#E0E0E0" />
    <shadow android:color="#80000000" android:radius="4dp" android:dx="0" android:dy="2" />
</shape>

4. MainActivity.java

package com.example.myapplication;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private LinearLayout container;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        container = findViewById(R.id.container);

        // ข้อมูลตัวอย่าง
        List items = Arrays.asList(
            new Item(R.drawable.img1, "ภาพที่ 1"),
            new Item(R.drawable.img2, "ภาพที่ 2"),
            new Item(R.drawable.img3, "ภาพที่ 3"),
            new Item(R.drawable.img4, "ภาพที่ 4"),
            new Item(R.drawable.img5, "ภาพที่ 5"),
            new Item(R.drawable.img6, "ภาพที่ 6")
        );

        // Inflate และเพิ่ม Item เข้า Container
        LayoutInflater inflater = LayoutInflater.from(this);
        for (Item item : items) {
            View itemView = inflater.inflate(R.layout.item_image, container, false);
            ImageView iv = itemView.findViewById(R.id.ivImage);
            TextView tv = itemView.findViewById(R.id.tvTitle);
            
            iv.setImageResource(item.imageRes);
            tv.setText(item.title);
            
            itemView.setOnClickListener(v -> 
                Toast.makeText(this, "คลิก: " + item.title, Toast.LENGTH_SHORT).show()
            );
            
            container.addView(itemView);
        }
    }

    static class Item {
        int imageRes;
        String title;
        Item(int imageRes, String title) {
            this.imageRes = imageRes;
            this.title = title;
        }
    }
}

ปรับปรุง UX (Enhancements)

1. Snap to Center (Android 11+ / API 30+)

// ใน onCreate หลัง setContentView
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    horizontalScrollView.setOnScrollChangeListener((v, scrollX, scrollY, oldX, oldY) -> {
        // Logic snap หรือใช้ RecyclerView แทน (แนะนำ)
    });
}

แนะนำ: ใช้ **RecyclerView + LinearLayoutManager.HORIZONTAL + SnapHelper** แทน HorizontalScrollView สำหรับ Production

2. RecyclerView แทน HorizontalScrollView (Best Practice)

// Layout
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:orientation="horizontal"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

// Activity
RecyclerView rv = findViewById(R.id.recyclerView);
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
rv.setAdapter(new MyAdapter(items));

// Snap to center
SnapHelper snapHelper = new PagerSnapHelper(); // หรือ LinearSnapHelper
snapHelper.attachToRecyclerView(rv);

3. เพิ่ม Gradient Fade ที่ขอบ (Edge Fade Effect)

<HorizontalScrollView
    ...
    android:requiresFadingEdge="horizontal"
    android:fadingEdgeLength="40dp"
    android:scrollbars="none" />

เปรียบเทียบ: HorizontalScrollView vs RecyclerView Horizontal

คุณสมบัติHorizontalScrollViewRecyclerView (Horizontal)
View Recyclingไม่มี (Inflate ทั้งหมด)มี (ViewHolder Pattern)
Memory (Item เยอะ)สูง (OOM Risk)ต่ำคงที่
Snap/Fling PhysicsManual/ไม่มีSnapHelper, PagerSnapHelper
Adapter Patternไม่มีมี (Adapter, DiffUtil)
Nested Scrollingยากรองรับดี
ใช้งานเหมาะกับItem น้อย (<10), StaticItem เยอะ, Dynamic, List

สรุป

HorizontalScrollView ใช้งานง่ายสำหรับ **Item น้อย (<10-15), Static Content** แต่ **ไม่มี View Recycling** → เสี่ยง OOM หาก Item เยอะ สำหรับ Production/Gallery จริง ให้ใช้ **RecyclerView + LinearLayoutManager.HORIZONTAL + SnapHelper** ซึ่งเป็นมาตรฐานปัจจุบัน ประสิทธิภาพดี รองรับ Animation, DiffUtil, Paging ได้ครบครัน