Custom ListView: แสดงรูปภาพ + ข้อความ ด้วย Custom Adapter
ใช้ Custom Adapter + Custom Layout สำหรับแต่ละ Row (Image + Text)
ขั้นตอนที่ 1: Layout หลัก (activity_main.xml)
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#000"
android:dividerHeight="1dp"
android:footerDividersEnabled="false"
android:headerDividersEnabled="false" />
ขั้นตอนที่ 2: Row Layout (res/layout/list_row.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="wrap_content"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:id="@+id/list_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_gravity="center_vertical"
android:textSize="18sp" />
</LinearLayout>
ขั้นตอนที่ 3: Data Model (SubjectData.java)
public class SubjectData {
public String subjectName;
public String link;
public String imageUrl;
public SubjectData(String subjectName, String link, String imageUrl) {
this.subjectName = subjectName;
this.link = link;
this.imageUrl = imageUrl;
}
}
ขั้นตอนที่ 4: Custom Adapter (CustomAdapter.java)
public class CustomAdapter extends BaseAdapter {
private Context context;
private List<SubjectData> items;
public CustomAdapter(Context context, List<SubjectData> items) {
this.context = context;
this.items = items;
}
@Override public int getCount() { return items.size(); }
@Override public Object getItem(int position) { return items.get(position); }
@Override public long getItemId(int position) { return position; }
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.list_row, parent, false);
holder = new ViewHolder();
holder.title = convertView.findViewById(R.id.title);
holder.image = convertView.findViewById(R.id.list_image);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
SubjectData data = items.get(position);
holder.title.setText(data.subjectName);
// Load Image with Picasso
Picasso.get().load(data.imageUrl).into(holder.image);
return convertView;
}
static class ViewHolder {
TextView title;
ImageView image;
}
}
ขั้นตอนที่ 5: MainActivity - Setup
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = findViewById(R.id.list);
List<SubjectData> items = new ArrayList<>();
items.add(new SubjectData("JAVA", "https://tutorialspoint.com/java/", "https://tutorialspoint.com/java/images/java-mini-logo.jpg"));
items.add(new SubjectData("Python", "https://tutorialspoint.com/python/", "https://tutorialspoint.com/python/images/python-mini.jpg"));
items.add(new SubjectData("JavaScript", "https://tutorialspoint.com/javascript/", "https://tutorialspoint.com/javascript/images/javascript-mini-logo.jpg"));
// ... add more
CustomAdapter adapter = new CustomAdapter(this, items);
listView.setAdapter(adapter);
listView.setOnItemClickListener((parent, view, position, id) -> {
SubjectData data = (SubjectData) parent.getItemAtPosition(position);
// Open Link in Browser
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(data.link));
startActivity(intent);
});
}
}
ขั้นตอนที่ 6: Dependencies (build.gradle)
dependencies {
implementation "com.squareup.picasso:picasso:2.71828"
// หรือ Glide: implementation "com.github.bumptech.glide:glide:4.16.0"
}
ขั้นตอนที่ 7: Internet Permission
<uses-permission android:name="android.permission.INTERNET" />
⚠️ แนะนำ: ใช้ RecyclerView แทน (Modern Standard)
ListView + BaseAdapter = Legacy Pattern RecyclerView + ViewHolder + DiffUtil = Current Best Practice
// RecyclerView.Adapter with ViewHolder (Similar Pattern but Better)
public class SubjectAdapter extends RecyclerView.Adapter<SubjectAdapter.ViewHolder> {
// ... similar ViewHolder pattern
// DiffUtil for efficient updates
// Built-in animations
}
Image Loading Libraries Comparison
| Library | Size | Features | Kotlin Support |
|---|---|---|---|
| Picasso | ~200KB | Simple, Auto-resize, Placeholder | Good |
| Glide | ~400KB | GIF, Video Thumbnail, Lifecycle Integration | Excellent |
| Coil | ~300KB | Kotlin-first, Coroutines, Compose Support | Best |
| Fresco | ~2MB | Progressive JPEG, WebP, Memory Management | Good |