Android/Java การสร้าง RecyclerView

ใช้ RecyclerView แสดงรายการแบบ list ทำให้สามารถแสดงรายการจำนวนมากๆได้

  1. ใช้งาน RecyclerView
  2. ปรับแต่งเส้นคั่นด้วยไลบรารี่ RecyclerView-FlexibleDivider
  3. วาดตัวอักษรด้วยไลบรารี่ TextDrawable

ไฟล์ที่เกี่ยวข้อง

  • build.gradle (Module: app)
  • activity_main.xml
  • MainActivity.java
  • values/colors.xml
  • drawable/item_state.xml
  • layout/custom_layout.xml
  • CustomItem.java
  • CustomHolder.java
  • CustomAdapter.java

1.ใช้งาน RecyclerView

build.gradle (Module: app)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.phaisarn.myapplication"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

วาง RecyclerView ใน activity_main.xml

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
ในหน้าออกแบบ RecyclerView แสดงเป็นรายการ

MainActivity.java

package com.phaisarn.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

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

        final ArrayList<CustomItem> items = new ArrayList<>();
        items.add(new CustomItem("แมว", "Cat"));
        items.add(new CustomItem("สุนัข", "Dog"));
        items.add(new CustomItem("หนู", "Rat"));
        items.add(new CustomItem("สิงโต", "Lion"));
        items.add(new CustomItem("เสือ", "Tiger"));
        items.add(new CustomItem("หมี", "Bear"));
        items.add(new CustomItem("ไก่", "Chicken"));
        items.add(new CustomItem("นก", "Bird"));
        items.add(new CustomItem("ลิง", "Monkey"));
        items.add(new CustomItem("ช้าง", "Elephant"));
        items.add(new CustomItem("ยีราฟ", "Giraffe"));
        items.add(new CustomItem("เป็ด", "Duck"));
        items.add(new CustomItem("หมู", "Pig"));
        items.add(new CustomItem("จิงโจ้", "Kangaroo"));
        items.add(new CustomItem("ผีเสื้อ", "Butterfly"));

        CustomAdapter adapter = new CustomAdapter(this, items);
        RecyclerView rcv = findViewById(R.id.recyclerView);

        rcv.setAdapter(adapter);
        rcv.setLayoutManager(new LinearLayoutManager(this));

        adapter.setOnClickListener(new CustomAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View item, int position) {
                String str = items.get(position).text1 + " - " + items.get(position).text2;
                Toast.makeText(getBaseContext(), str, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

กำหนด state ของรายการปกติ และรายการที่ถูกกด ให้แสดงสีต่างกัน

values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>

    <color name="item_active">#ffffc5</color>
    <color name="item_normal">#ffffff</color>
</resources>

drawable/item_state.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:exitFadeDuration="500">
    
    <item android:drawable="@color/item_active" android:state_pressed="true" />
    <item android:drawable="@color/item_normal" />
</selector>

กำหนด layout ของแต่ละรายการใน RecyclerView

layout/custom_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="3dp"
    android:background="@drawable/item_state"
    android:orientation="horizontal"
    android:padding="5dp">

    <TextView
        android:id="@+id/textView1"
        android:layout_columnWeight="1"
        android:layout_gravity="center_vertical"
        android:layout_margin="10dp"
        android:text="สัตว์"
        android:textColor="#007"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/textView2"
        android:layout_gravity="center_vertical"
        android:layout_margin="10dp"
        android:text="Animal"
        android:textColor="#aaa"
        android:textSize="18sp" />
</GridLayout>

CustomItem.java

package com.phaisarn.myapplication;

public class CustomItem {
    public String text1;
    public String text2;

    public CustomItem(String text1, String text2) {
        this.text1 = text1;
        this.text2 = text2;
    }
}

CustomHolder.java

package com.phaisarn.myapplication;

import android.view.View;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

public class CustomHolder extends RecyclerView.ViewHolder {
    public TextView textView1;
    public TextView textView2;

    public CustomHolder(View view) {
        super(view);
        textView1 = view.findViewById(R.id.textView1);
        textView2 = view.findViewById(R.id.textView2);
    }
}

CustomAdapter.java

package com.phaisarn.myapplication;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class CustomAdapter extends RecyclerView.Adapter<CustomHolder> {
    private Context mContext;
    private List<CustomItem> mItems;

    public CustomAdapter(Context context, List<CustomItem> items) {  //กำหนดเป็น ArrayList เหมือนเดิมก็ได้
        mContext = context;
        mItems = items;
    }

    public interface OnItemClickListener {
        void onItemClick(View item, int position);
    }

    private OnItemClickListener mListener;

    public void setOnClickListener(OnItemClickListener listener) {
        mListener = listener;
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    @Override
    public CustomHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        final View view = inflater.inflate(R.layout.custom_layout, parent, false);
        final CustomHolder viewHolder = new CustomHolder(view);

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mListener != null) {
//                    int pos = viewHolder.getAdapterPosition();
//                    String str = viewHolder.textView1.getText().toString();
//                    str += "  :  " + viewHolder.textView2.getText().toString();
//                    if(pos != RecyclerView.NO_POSITION) {
//                        Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();
//                    }

                    int pos = viewHolder.getAdapterPosition();
                    if (pos != RecyclerView.NO_POSITION) {
                        mListener.onItemClick(view, pos);
                    }
                }
            }
        });
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(CustomHolder viewHolder, int position) {
        CustomItem item = mItems.get(position);
        viewHolder.textView1.setText(item.text1);
        viewHolder.textView2.setText(item.text2);
    }
}

บรรทัดที่ 56 ทำให้เกิดอีเวนต์ onItemClick()

แต่ถ้าต้องการจะทำงานในนี้เลยเช่น Toast จากในนี้ ให้ uncomment บรรทัดที่ 47-52 และ comment บรรทัดที่ 54-57 (และแก้ส่วนอื่นๆที่เกี่ยวช้อง)

2.ปรับแต่งเส้นคั่นด้วยไลบรารี่ RecyclerView-FlexibleDivider

Android library providing simple way to control divider items (ItemDecoration) of RecyclerView

build.gradle (Module: app)

...
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'com.yqritc:recyclerview-flexibledivider:1.4.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

MainActivity.java

package com.phaisarn.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

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

        final ArrayList<CustomItem> items = new ArrayList<>();
        items.add(new CustomItem("แมว", "Cat"));
        items.add(new CustomItem("สุนัข", "Dog"));
        items.add(new CustomItem("หนู", "Rat"));
        items.add(new CustomItem("สิงโต", "Lion"));
        items.add(new CustomItem("เสือ", "Tiger"));
        items.add(new CustomItem("หมี", "Bear"));
        items.add(new CustomItem("ไก่", "Chicken"));
        items.add(new CustomItem("นก", "Bird"));
        items.add(new CustomItem("ลิง", "Monkey"));
        items.add(new CustomItem("ช้าง", "Elephant"));
        items.add(new CustomItem("ยีราฟ", "Giraffe"));
        items.add(new CustomItem("เป็ด", "Duck"));
        items.add(new CustomItem("หมู", "Pig"));
        items.add(new CustomItem("จิงโจ้", "Kangaroo"));
        items.add(new CustomItem("ผีเสื้อ", "Butterfly"));

        CustomAdapter adapter = new CustomAdapter(this, items);
        RecyclerView rcv = findViewById(R.id.recyclerView);

        rcv.setAdapter(adapter);
        rcv.setLayoutManager(new LinearLayoutManager(this));

        rcv.addItemDecoration(
                new HorizontalDividerItemDecoration.Builder(this)
                        .color(Color.LTGRAY)
                        .size(4)
                        .margin(30, 30)    //left, right
                        .build());

        adapter.setOnClickListener(new CustomAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View item, int position) {
                String str = items.get(position).text1 + " - " + items.get(position).text2;
                Toast.makeText(getBaseContext(), str, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

บรรทัดที่ 48 กำหนดสีเส้นคั่น
บรรทัดที่ 49 กำหนดขนาดเส้นคั่น
บรรทัดที่ 50 กำหนด margin ด้านซ้ายและขวาของเส้นคั่น

3.วาดตัวอักษรด้วยไลบรารี่ TextDrawable

TextDrawable This light-weight library provides images with letter/text like the Gmail app. It extends the Drawable class thus can be used with existing/custom/network ImageView classes. Also included is a fluent interface for creating drawables and a customizable ColorGenerator.

นำตัวอักษรตัวแรกของข้อความภาษาไทยมาวาดเป็นภาพ

build.gradle (Module: app)

...
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.recyclerview:recyclerview:1.0.0'
    implementation 'com.yqritc:recyclerview-flexibledivider:1.4.0'
    implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

MainActivity.java

package com.phaisarn.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;

import java.util.ArrayList;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

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

        final ArrayList<CustomItem> items = new ArrayList<>();
        int i = R.mipmap.ic_launcher;
        items.addAll(Arrays.asList(
                new CustomItem(i, "แมว", "Cat"),
                new CustomItem(i, "สุนัข", "Dog"),
                new CustomItem(i, "หนู", "Rat"),
                new CustomItem(i, "สิงโต", "Lion"),
                new CustomItem(i, "เสือ", "Tiger"),
                new CustomItem(i, "หมี", "Bear"),
                new CustomItem(i, "ไก่", "Chicken"),
                new CustomItem(i, "นก", "Bird"),
                new CustomItem(i, "ลิง", "Monkey"),
                new CustomItem(i, "ช้าง", "Elephant"),
                new CustomItem(i, "ยีราฟ", "Giraffe"),
                new CustomItem(i, "เป็ด", "Duck"),
                new CustomItem(i, "หมู", "Pig"),
                new CustomItem(i, "จิงโจ้", "Kangaroo"),
                new CustomItem(i, "ผีเสื้อ", "Butterfly")
        ));

        CustomAdapter adapter = new CustomAdapter(this, items);
        RecyclerView rcv = findViewById(R.id.recyclerView);

        rcv.setAdapter(adapter);
        rcv.setLayoutManager(new LinearLayoutManager(this));

        rcv.addItemDecoration(
                new HorizontalDividerItemDecoration.Builder(this)
                        .color(Color.LTGRAY)
                        .size(4)
                        .margin(200, 0)    //left, right
                        .build());

        adapter.setOnClickListener(new CustomAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View item, int position) {
                String str = items.get(position).text1 + " - " + items.get(position).text2;
                Toast.makeText(getBaseContext(), str, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

layout/custom_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:layout_marginBottom="10dp"
    android:columnCount="2">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_rowSpan="3"
        android:layout_marginRight="15dp"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/textView1"
        android:layout_columnWeight="1"
        android:layout_marginBottom="5dp"
        android:textColor="#00b"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/textView2"
        android:layout_columnWeight="1"
        android:layout_marginBottom="5dp"
        android:textColor="#050"
        android:textSize="18sp" />
</GridLayout>

CustomItem.java

package com.phaisarn.myapplication;

public class CustomItem {
    public int imgId;
    public String text1;
    public String text2;

    public CustomItem(int imgId, String text1, String text2) {
        this.imgId = imgId;
        this.text1 = text1;
        this.text2 = text2;
    }
}

CustomHolder.java

package com.phaisarn.myapplication;

import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

public class CustomHolder extends RecyclerView.ViewHolder {
    public ImageView imageView;
    public TextView textView1;
    public TextView textView2;

    public CustomHolder(View view) {
        super(view);

        imageView = view.findViewById(R.id.imageView);
        textView1 = view.findViewById(R.id.textView1);
        textView2 = view.findViewById(R.id.textView2);
    }
}

CustomAdapter.java

package com.phaisarn.myapplication;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.recyclerview.widget.RecyclerView;

import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;

import java.util.List;

public class CustomAdapter extends RecyclerView.Adapter<CustomHolder> {
    private Context mContext;
    private List<CustomItem> mItems;

    public CustomAdapter(Context context, List<CustomItem> items) {  //กำหนดเป็น ArrayList เหมือนเดิมก็ได้
        mContext = context;
        mItems = items;
    }

    public interface OnItemClickListener {
        void onItemClick(View item, int position);
    }

    private OnItemClickListener mListener;

    public void setOnClickListener(OnItemClickListener listener) {
        mListener = listener;
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    @Override
    public CustomHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        final View view = inflater.inflate(R.layout.custom_layout, parent, false);
        final CustomHolder viewHolder = new CustomHolder(view);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mListener != null) {
//                    int pos = viewHolder.getAdapterPosition();
//                    String str = viewHolder.textView1.getText().toString();
//                    str += "  :  " + viewHolder.textView2.getText().toString();
//                    if(pos != RecyclerView.NO_POSITION) {
//                        Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();
//                    }
                    int pos = viewHolder.getAdapterPosition();
                    if (pos != RecyclerView.NO_POSITION) {
                        mListener.onItemClick(view, pos);
                    }
                }
            }
        });
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(CustomHolder viewHolder, int position) {
        CustomItem item = mItems.get(position);
        String letter = String.valueOf(item.text1.charAt(0));

        ColorGenerator generator = ColorGenerator.MATERIAL;
        TextDrawable drawable = TextDrawable.builder()
                .buildRound(letter, generator.getRandomColor());
        //.buildRect(letter, generator.getRandomColor())
        //.buildRoundRect(letter, generator.getRandomColor(), 50);
        viewHolder.imageView.setImageDrawable(drawable);
        viewHolder.textView1.setText(item.text1);
        viewHolder.textView2.setText(item.text2);
    }
}

Link