24 สิงหาคม 2555

[Android Code] การทำให้ List View แสดงค่าแบบวนลูป [Endless Scrolling List View]


** ปรับปรุงแก้ไขให้ใช้งานคำสั่งได้ง่ายขึ้นแล้ว สามารถอ่านได้ที่  **


บทความในคราวนี้กลับมาที่เรื่อง List View กันบ้าง
สำหรับการใช้งาน List View ไม่อธิบายนะ ไปหาอ่านเอง
โดยเจ้าของบล็อกจะให้ข้อมูลที่แสดงใน List View
วนลูปเรื่อยๆ คือสามารถเลื่อนขึ้นลงไปมาได้เรื่อยๆไม่มีสุดขอบ

ในวีดีโอตัวอย่างข้างล่างนี้จะเป็นข้อมูลจำนวน 132 ตัว
โดยจะให้ดึงข้อมูลมาแสดงครั้งละ 20 ตัว
ซึ่งจะเห็นบน List View จริงๆครั้งละประมาณ 5-6 ตัว
ดังนั้นจึงสามารถเลื่อนขึ้นและลงได้อีก 7 ตัว
แล้วทำการดึงข้อมูลตัวถัดไปมาแสดงเรื่อยๆ



เหมาะสำหรับผู้ที่มีข้อมูลจำนวนมากเป็นหลักพันหลักหมื่นแถว
จะให้ ArrayList เก็บข้อมูลทั้งหมดในที่เดียว ก็สิ้นเปลืองทรัพยากรเปล่าๆ
เพราะเวลาแสดงบน List View ผู้ใช้ก็จะเห็นแค่ครั้งละ 5-6 ตัว
(แล้วแต่ผู้เขียนโปรแกรมจะกำหนดขนาดของ List View)
ดังนั้นถ้าให้โปรแกรมทำการดึงข้อมูลซักทีละ 30 ตัวมาแสดง
เวลาผู้ใช้เลื่อนไปสุดขอบ ก็ค่อยให้ดึงข้อมูลตัวถัดไปมาแสดง
แบบนี้จะประหยัดการใช้ทรัพยากรได้มากกว่าเยอะเลย
แต่ก็เอาโค๊ดของเจ้าของบล็อกไปแก้ไขเองนะ 
หรือไม่เจ้าของบล็อกว่างๆก็อาจจะทำให้ (มั้ง แต่อย่างหวังเลย)

สำหรับโค๊ดในรอบนี้เจ้าของบล็อกไม่ขออธิบายอะไรมากมายนะ
เพราะอธิบายไปก็งงกว่าเก่า เลยให้ผู้ที่หลงเข้ามาอ่าน
ดาวน์โหลดไฟล์ตัวอย่างไปดูเองเลยดีกว่า

สำหรับตัวคำสั่งก็จะมีดังนี้ 
(ตัดข้อมูลที่จะแสดงให้เหลือ 40 ตัวพอ เดี๋ยวยาว)


Main.java
package app.akexorcist.listviewscrolllistener;  
 
import java.util.ArrayList;  
import android.os.Bundle;  
import android.app.Activity;  
import android.util.Log;  
import android.view.Menu;  
import android.widget.AbsListView;  
import android.widget.AbsListView.OnScrollListener;  
import android.widget.ArrayAdapter;  
import android.widget.ListView;  

public class Main extends Activity {  
    ArrayList<String> arr_list;  
    int first_row, last_row, total_row = 20;  
    ListView lv;  
    String[] lv_arr = { "Row 0", "Row 1", "Row 2", "Row 3"  
            , "Row 4", "Row 5", "Row 6", "Row 7"  
            , "Row 8", "Row 9", "Row 10", "Row 11"  
            , "Row 12", "Row 13", "Row 14", "Row 15"  
            , "Row 16", "Row 17", "Row 18", "Row 19"  
            , "Row 20", "Row 21", "Row 22", "Row 23"  
            , "Row 24", "Row 25", "Row 26", "Row 27"  
            , "Row 28", "Row 29", "Row 30", "Row 31"  
            , "Row 32", "Row 33", "Row 34", "Row 35"  
            , "Row 36", "Row 37", "Row 38", "Row 39" };  
   
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  

        arr_list = new ArrayList<String>();  
        first_row = lv_arr.length - 1;  

        while(first_row < 0) {  
            first_row += lv_arr.length;  
        }  

        for(int i = 0 ; i < total_row ; i++) {  
            arr_list.add(lv_arr[first_row]);  
            first_row++;  
            if(first_row >= lv_arr.length)  
            first_row = 0;  
        }  

        first_row -= total_row;  

        while(first_row < 0) {  
            first_row += lv_arr.length;  
        }  

        last_row = first_row + total_row - 1;  

        while(last_row >= lv_arr.length) {  
            last_row -= lv_arr.length;  
        }  

        lv = (ListView)findViewById(R.id.listView1);  
        lv.setAdapter(new ArrayAdapter<String>(getApplicationContext(),  
                android.R.layout.simple_list_item_1, arr_list));  
        lv.setSelection((total_row / 2));  
        lv.setOnScrollListener(new OnScrollListener() {  
            public void onScroll(AbsListView view, int firstVisibleItem,  
                    int visibleItemCount, int totalItemCount) {  
                if(firstVisibleItem + visibleItemCount >= totalItemCount) {  
                    arr_list = new ArrayList<String>();  
                    last_row -= total_row / 2;  

                    while(last_row < 0) {  
                        last_row += lv_arr.length;  
                    }  

                    for(int i = 0 ; i < total_row ; i++) {  
                        arr_list.add(lv_arr[last_row]);  
                        last_row++;  
                        if(last_row >= lv_arr.length)  
                            last_row = 0;  
                    }  

                    first_row = last_row - total_row;  
               
                    while(first_row < 0) {  
                        first_row += lv_arr.length;  
                    }  

                    last_row--;  

                    while(last_row < 0) {  
                        last_row += lv_arr.length;  
                    }  

                    lv.setAdapter(new ArrayAdapter<String>
                            (getApplicationContext()
                            , android.R.layout.simple_list_item_1
                            , arr_list));  

                    if(total_row % 2 == 0) {  
                        lv.setSelection(
                                (firstVisibleItem + 2) - (total_row / 2) );  
                    } else {  
                        lv.setSelection(
                                (firstVisibleItem + 1) - (total_row / 2) );  
                    }  
                }  

                if(firstVisibleItem == 0) {  
                    arr_list = new ArrayList<String>();  
                    first_row -= total_row / 2;  

                    while(first_row < 0) {  
                        first_row += lv_arr.length;  
                    }  

                    for(int i = 0 ; i < total_row ; i++) {  
                        arr_list.add(lv_arr[first_row]);  
                        first_row++;  
    
                        if(first_row >= lv_arr.length)  
                            first_row = 0;  
                    }  

                    first_row -= total_row;  

                    while(first_row < 0) {  
                        first_row += lv_arr.length;  
                    }  

                    last_row = first_row + total_row - 1;  

                    while(last_row >= lv_arr.length) {  
                        last_row -= lv_arr.length;  
                    }  

                    lv.setAdapter(new ArrayAdapter<String>
                            (getApplicationContext(),  
                            android.R.layout.simple_list_item_1
                            , arr_list));  
                    lv.setSelection((total_row / 2) + 1);  
                }  
            }  

            public void onScrollStateChanged(AbsListView view
                    , int scrollState) {  }  
        });  
    }
  
    public boolean onCreateOptionsMenu(Menu menu) {  
        getMenuInflater().inflate(R.menu.main, menu);  
        return true;  
    }  
}  

String[] lv_arr ก็คือข้อความที่ต้องการแสดงบน List View
ซึ่งเจ้าของบล็อกจะเอามาเก็บไว้ใน ArrayList<String> อีกทีหนึ่ง

สำหรับตัวแปร first_row และ last_row จะใช้เก็บว่า
ข้อมูลแรกสุดและท้ายสุดที่แสดงใน List View ณ ตอนนั้น
เป็นข้อมูลตัวที่เท่าไรในอาเรย์ของตัวแปร lv_arr 
เพื่อให้รู้ว่แสดงข้อมูลช่วงไหนอยู่ จะได้ดึงค่าถัดไปมาแสดง
สำหรับ total_row เอาไว้กำหนดว่าจะให้ดึงข้อมูลมาทีละเท่าไร
โดยในตัวอย่างจะให้ดึงข้อมูลมาแสดงทีละ 20 แถว

** โค๊ดของเจ้าของบล็อกไม่รองรับ total_row ที่ต่ำกว่า 20 
เพราะไม่ได้เขียนมาดีพอ จึงทำให้เกิดบั๊กได้ แต่มากกว่านั้นไม่เป็นไร
จะมีข้อมูลทั้งหมดกี่ตัวก็ได้ ได้ตั้งแต่สองสามตัวจนถึงหมื่นตัว **

ทีนี้ขอข้ามมาที่ onScrollListener เลย เพราะคำสั่งก่อนหน้า
เป็นคำสั่งสำหรับแสดงค่าเริ่มต้นขึ้น List View ขี้เกียจอธิบาย

สำหรับ onScrollingListener เป็น Event เมื่อผู้ใช้เลื่อน List View ขึ้นลง
โดยมี Callback Method อยู่สองตัวคือ onScroll กับ onScrollStateChanged
สำหรับ onScrollStateChanged จะทำงานเมื่อสถานะของการ Scroll 
มีการเปลี่ยนแปลง โดยมีจะมีค่าส่งเข้ามาเก็บไว้ใน scrollState
ซึ่งค่าจะมีดังนี้

  • ค่าเป็น 0 เมื่อไม่มีการเลื่อนแถบ List View ไปมา
  • ค่าเป็น 1 เมื่อมีการสัมผัสหน้าจอแล้วเลื่อนแถบ List View ไปมา
  • ค่าเป็น 2 เมื่อหยุดสัมผัสหน้าจอแล้ว แต่แถบ List View ยังเลื่อนอยู่ จะเป็นตอนที่เวลาผู้ใช้เลื่อนแถบ List View แรงๆแล้วมันจะไหลไปเรื่อยๆจนกว่าจะหยุดเอง

สำหรับ onScroll จะทำงานเมื่อมีการเลื่อนแถบ List View ไปมา
โดยจะมีค่าส่งเข้ามาดังนี้ firstVisibleItem, visibleItemCount และ totalItemCount

  • firstVisibleItem คือลำดับแถวแรกสุดที่ถูกแสดงให้เห็นบน List View
  • visibleItemCount คือจำนวนแถวทั้งหมดที่ถูกแสดงให้เห็นบน List View
  • totalItemCount คือจำนวนแถวทั้งหมดที่มีอยู่



เมื่อรู้ว่าแถวไหนถูกแสดงอยู่ ก็กำหนดให้ดึงข้อมูลมาแสดงต่อได้
สำหรับการตรวจสอบว่าเลื่อน List View ถึงขอบบนสุดและขอบล่างสุด
จะใช้เงื่อนไข If ตรวจสอบด้วยคำสั่งดังนี้

  • เลื่อนสุดขอบล่าง - if(firstVisibleItem + visibleItemCount >= totalItemCount)
  • เลื่อนสุดขอบบน - if(firstVisibleItem == 0)
ในกรณีที่สุดขอบบน ก็จะให้ดึงข้อมูลลำดับก่อนหน้าจำนวน 10 แถวเข้ามา
แล้วเคลียร์ 10 แถวหลังสุดออกไป และกรณีที่สุดขอบล่างก็จะดึงข้อมูล
ลำดับถัดไปจำนวน 10 แถว แล้วเคลียร์ 10 แถวแรกสุดออกไป

ถ้าข้อมูลที่แสดงถึงตัวสุดท้ายของอาเรย์ lv_arr แล้ว 
ก็ให้ไปดึงตัวแรกสุดมาใหม่ โดยใช้คำสั่งดังนี้
if(last_row >= lv_arr.length) last_row = 0;

จากนั้นก็นำมาแสดงใน List View โดยใช้คำสั่ง setSelection 
เพื่อเลื่อนไปยังแถวล่าสุด เพราะเวลาที่แสดงค่าขึ้น List View ใหม่
แถบ List View จะเลื่อนไปยังตำแหน่งบนสุด จึงต้องใช้คำสั่งเลื่อนกลับมา

main.xml
<RelativeLayout 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"  
    android:background="#454545" >  
    <ListView  
        android:id="@+id/listView1"  
        android:layout_width="300dp"  
        android:layout_height="300dp"  
        android:layout_centerHorizontal="true"  
        android:layout_centerVertical="true"  
        android:cacheColorHint="#00000000"  
        android:scrollbars="none" >  
    </ListView>  
</RelativeLayout> 

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="app.akexorcist.listviewscrolllistener"  
    android:versionCode="1"  
    android:versionName="1.0" >  

    <uses-sdk  
        android:minSdkVersion="8"  
        android:targetSdkVersion="8" />  

    <application  
        android:icon="@drawable/ic_launcher"  
        android:label="@string/app_name"  
        android:theme="@style/AppTheme" >  
        <activity  
            android:name=".Main"  
            android:label="@string/title_activity_main"  
            android:screenOrientation="portrait">  
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />  
                <category android:name="android.intent.category.LAUNCHER" />  
            </intent-filter>  
        </activity>  
    </application>  
 </manifest> 




เพียงเท่านี้ก็สามารถเลื่อนข้อมูลบน List View ขึ้นลงได้โดยไม่มีสุดขอบแล้ว

เสร็จเรียบร้อย!!!




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium