web analytics

Android Code : Multiple layout listView การทำ layout หลายๆแบบใน ListView

cover

บทความนี้ผมจะบันทึก เกี่ยวกับการทำ layout ใน listView หลายๆแบบ

ถายังไม่เคยทำเกี่ยวกับ custom view แนะนำให้อ่านบทความเรื่อง CustomViewGroup และ CustomViewGroup on ListView ก่อนนะ

Android Code : Custom Viewgroup บน Android เบื้องต้น

Android Code : ListView และ Custom Groupview บน Android เบื้องต้น

 

รู้จักกับ Multiple layout ListView

ปกติแล้วใน List view จะประกอบไปด้วยข้อมูลลักษณะคล้ายๆ รูปแบบแถว ซึ่งพบเจอได้บ่อยมาก แต่ว่ารูปแบบของแถวก็ไม่จำเป็นต้องมีเพียงลักษณะเดียว อาจจะมีหลายๆแบบ ที่เจอบ่อยๆคือ แอปแชท เช่น LINE , Messager

มีแถวของข้อความ แถวของรูปสติกเกอร์ เป็นต้น

2-1

 

เราจะลองทำ ListView ให้รองรับ แบบ Multiple layout กัน เอาแค่พอเป็นแนวทางไปประยุกต์ใช้ต่อได้ก็พอ
โดยจะมี Layout 3 แบบ คือ ข้อความของเพื่อน ข้อความของเรา และรูปสติกเกอร์ของเพื่อน เท่านี้ก่อน สมมุติว่าเราส่งสติกเกอร์ไม่ได้ ฮ่าๆ

1-1

 

สร้าง Layout

เริ่มจากใส่ listView ใน layout ของ activity หรือ fragment

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
        <ListView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </ListView>
</LinearLayout>

 

ต่อมาจะสร้าง layout ของทั้งสามแบบ

3

 

layout_row_image_friend.xml
จะมีแต่ imageView เอาไว้แสดงสติกเกอร์

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:padding="16dp">
    <ImageView
        android:id="@+id/imgV"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:src="@mipmap/ic_launcher"/>
</LinearLayout>

4

 

layout_row_message_friend.xml

มีข้อความ เป็น TextView

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:padding="16dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp"
        android:layout_gravity="left">
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="First name"
            android:textSize="18sp"/>
        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="First name"
            android:textSize="22sp"/>
    </LinearLayout>
</FrameLayout>

5

 

layout_row_message_me.xml

มี TextView เป็นข้อความธรรมดา แต่ให้มันชิดขวา

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:padding="16dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp"
        android:layout_gravity="right">
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="First name"
            android:layout_gravity="right"
            android:textSize="18sp"/>
        <TextView
            android:id="@+id/tv_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="First name"
            android:layout_gravity="right"
            android:textSize="22sp"/>
    </LinearLayout>
</FrameLayout>

6

 

สร้าง CustomView class ใน java

ส่วนต่อมาก็เอา layout ที่ทำไว้ทั้งสามอัน มาเขียนใน java , code จะซ้ำๆกันนิดหน่อย

CustomRowImageFriend.java

public class CustomRowImageFriend extends FrameLayout {

    ImageView imgView;

    public CustomRowImageFriend(Context context) {
        super(context);
        initInflate();
        initInstance();
    }

    public CustomRowImageFriend(Context context, AttributeSet attrs) {
        super(context, attrs);
        initInflate();
        initInstance();
    }

    public CustomRowImageFriend(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initInflate();
        initInstance();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomRowImageFriend(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initInflate();
        initInstance();
    }

    public void initInflate(){
        inflate(getContext(), R.layout.layout_row_image_friend,this);

    }

    public void initInstance(){
        imgView = (ImageView) findViewById(R.id.imgV);

    }

    public void setImage(int res){
        imgView.setImageResource(res);
    }

}

 

CustomRowMessageFriend.java

public class CustomRowMessageFriend extends FrameLayout {

    TextView tvName;
    TextView tvMessage;

    public CustomRowMessageFriend(Context context) {
        super(context);
        initInflate();
        initInstance();
    }

    public CustomRowMessageFriend(Context context, AttributeSet attrs) {
        super(context, attrs);
        initInflate();
        initInstance();
    }

    public CustomRowMessageFriend(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initInflate();
        initInstance();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomRowMessageFriend(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initInflate();
        initInstance();
    }

    public void initInflate(){
        inflate(getContext(), R.layout.layout_row_message_friend,this);

    }

    public void initInstance(){
        tvName = (TextView) findViewById(R.id.tv_name);
        tvMessage = (TextView) findViewById(R.id.tv_message);

    }

    public void setName(String name){
        tvName.setText(name);
    }

    public  void setMessage(String des){
        tvMessage.setText(des);
    }
}

 

CustomRowMessageMe.java

public class CustomRowMessageMe extends FrameLayout {

    TextView tvName;
    TextView tvMessage;

    public CustomRowMessageMe(Context context) {
        super(context);
        initInflate();
        initInstance();
    }

    public CustomRowMessageMe(Context context, AttributeSet attrs) {
        super(context, attrs);
        initInflate();
        initInstance();
    }

    public CustomRowMessageMe(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initInflate();
        initInstance();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CustomRowMessageMe(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initInflate();
        initInstance();
    }

    public void initInflate(){
        inflate(getContext(), R.layout.layout_row_message_me,this);

    }

    public void initInstance(){
        tvName = (TextView) findViewById(R.id.tv_name);
        tvMessage = (TextView) findViewById(R.id.tv_message);

    }

    public void setName(String name){
        tvName.setText(name);
    }

    public  void setMessage(String des){
        tvMessage.setText(des);
    }
}

 

สร้าง class ของข้อมูล

ต่อมาสร้าง class สำหรับโครงสร้างข้อมูล ประกอบไปด้วย sticker ตัวอย่างนี้สมมุติใช้เป็นรูปใน res แล้วก็มี ชื่อคนส่ง ระบุว่าเป็นของเราหรือไม่ สุดท้ายก็ตัวเนื้อหาข้อความ

สังเกตว่าจะมี type ด้วยเพื่อระบุว่าเป็นประเภทไหน

Message.java

public class Message {

    public static final int MESSAGE_FRIEND_STICKER = 0;
    public static final int MESSAGE_FRIEND_TEXT = 1;
    public static final int MESSAGE_MY_TEXT = 2;
    
    private int sticker;
    private String message;
    private int type;
    private String senderName;

    public int getSticker() {
        return sticker;
    }

    public void setSticker(int sticker) {
        this.sticker = sticker;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getSenderName() {
        return senderName;
    }

    public void setSenderName(String senderName) {
        this.senderName = senderName;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
}

 

สร้าง Adapter

โดยทั่วไปก็เหมือนกับ adapter ปกติ แต่จะมี method ที่สำคัญคือ getItemViewType(position) ที่เรา ต้อง return ว่า position นี้เป็น type อะไร

แล้วที่ getView เราก็ทำการ เช็ค type ว่าเป็นประเภทไหน จากนั้นก็สร้าง CustomView พร้อมกำหนดค่าให้มัน
ก่อนที่จะ return View ออกไป

 

CustomMultipleLayoutAdapter.java

public class CustomMultipleLayoutAdapter extends BaseAdapter {

    ArrayList<Message> data;

    public void setData(ArrayList<Message> data){
        this.data = data;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public int getViewTypeCount() {
        return Message.COUNT_TYPE;
    }

    @Override
    public int getItemViewType(int position) {
        return data.get(position).getType();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        int type = getItemViewType(position);
        Message message = data.get(position);
        switch (type){
            case Message.MESSAGE_FRIEND_STICKER :
                CustomRowImageFriend rowImageFriend = new CustomRowImageFriend(parent.getContext());
                rowImageFriend.setImage(message.getSticker());
                return rowImageFriend;
            case Message.MESSAGE_FRIEND_TEXT :
                CustomRowMessageFriend rowMessageFriend = new CustomRowMessageFriend(parent.getContext());
                rowMessageFriend.setName(message.getSenderName());
                rowMessageFriend.setMessage(message.getMessage());
                return rowMessageFriend;
            case Message.MESSAGE_MY_TEXT :
                CustomRowMessageMe rowMessageMe = new CustomRowMessageMe(parent.getContext());
                rowMessageMe.setName(message.getSenderName());
                rowMessageMe.setMessage(message.getMessage());
                return rowMessageMe;
        }

        return null;
    }
}

 

ลองสมมุติข้อมูล

ต่อมาก็เอาข้อมูลมาใส่ใน adapter ผมจะใช้วิธีสุ่ม สมมุติขึ้นมานะ

MainActivity.java

public class MainActivity extends AppCompatActivity {

    ListView listView;

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

    private void initInstance(){
        listView = (ListView) findViewById(R.id.listView);
        CustomMultipleLayoutAdapter adapter = new CustomMultipleLayoutAdapter();
        adapter.setData(getSampleMessage());
        listView.setAdapter(adapter);
    }

    private ArrayList<Message> getSampleMessage(){
        ArrayList<Message> data = new ArrayList<>();

        for (int i = 0; i < 100; i++) {
            Message message = new Message();
            int random = (int )(Math.random() * 3 + 1);
            if(random == 1){ // set sticker friend message.
                message.setType(Message.MESSAGE_FRIEND_STICKER);
                message.setSticker(R.mipmap.ic_launcher);
            }else if(random == 2){ // set message friend message.
                message.setType(Message.MESSAGE_FRIEND_TEXT);
                message.setSenderName("Monkey D. luffy");
                message.setMessage("Hello guy!!");
            }else{
                message.setType(Message.MESSAGE_MY_TEXT);
                message.setSenderName("You");
                message.setMessage("I'am Iron Man!!");
            }

            data.add(message);
        }
        return data;
    }
}

 

ดูผลการทำงาน

a1

 

จบแล้ว

จากตัวอย่างสามารถทำได้หลายแบบจะไม่ทำ custom view class เลยก็ได้ ไป inflate ใน method ของ adapter ก็สามารถทำได้ครับ หรือถ้า layout คล้ายๆกัน จะทำแค่ layout เดียวแล้วเปิดปิด visibility ก็ได้เหมือนกัน แต่ผมคิดว่าทำแบบครอบคลุมไปเลยดีกว่า เพราะงานจริงคงซับซ้อน และจะได้เข้าใจการทำงาน ดังนั้นเลยพาทำ custom viewgroup สร้าง class ไปเลย อันนี้เป็นตัวอย่างคร่าวๆ เอาไปประยุกต์ใช้อีกทีครับ
หากมีอะไรสอบถาม หรือแนะนำคอมเม้นไว้ได้เลยนะ

(: