Android 自定義 ListView 上下拉動“刷新最新”和“加載更多”歌曲列表


本文內容

  • 環境
  • 測試數據
  • 項目結構
  • 演示
  • 參考資料

本文演示,上拉刷新最新的歌曲列表,和下拉加載更多的歌曲列表。所謂“刷新最新”和“加載更多”是指日期。演示代碼太多,點擊此處下載,自己調試一下。

下載 Demo

環境


  • Windows 2008 R2 64 位
  • Eclipse ADT V22.6.2,Android 4.4.3
  • SAMSUNG GT-I9008L,Android OS 2.2.2

 

測試數據


本演示的歌曲信息,共有 20 條,包括歌手名、歌曲名、時長、縮略圖,為了簡單起見都存在本地,你當然也可以通過網絡獲得。同時,當下拉刷新最新或上拉加載更多時,為了簡單,20 條歌曲信息是取模運算循環使用的。

package com.my.android.app.data;
 
import com.my.android.app.R;
 
public class TestData {
    public static String[] title = new String[] { "Someone Like You",
            "Space Bound", "Stranger In Moscow", "Love The Way You Lie",
            "Khwaja Mere Khwaja", "All My Days", "Life For Rent",
            "Love To See You Cry", "The Good, The Bad And The Ugly",
            "Show me the meaning", "Someone Like You", "Space Bound",
            "Stranger In Moscow", "Love The Way You Lie", "Khwaja Mere Khwaja",
            "All My Days", "Life For Rent", "Love To See You Cry",
            "The Good, The Bad And The Ugly", "Show me the meaning" };
    public static String[] artist = new String[] { "Adele", "Eminem",
            "Michael Jackson", "Rihanna", "A R Rehman", "Alexi Murdoch",
            "Dido", "Enrique Iglesias", "Ennio Morricone", "Backstreet Boys",
            "Adele", "Eminem", "Michael Jackson", "Rihanna", "A R Rehman",
            "Alexi Murdoch", "Dido", "Enrique Iglesias", "Ennio Morricone",
            "Backstreet Boys" };
    public static int[] thumb = new int[] { R.drawable.adele,
            R.drawable.eminem, R.drawable.mj, R.drawable.rihanna,
            R.drawable.arrehman, R.drawable.alexi_murdoch, R.drawable.dido,
            R.drawable.enrique, R.drawable.ennio, R.drawable.backstreet_boys,
            R.drawable.adele, R.drawable.eminem, R.drawable.mj,
            R.drawable.rihanna, R.drawable.arrehman, R.drawable.alexi_murdoch,
            R.drawable.dido, R.drawable.enrique, R.drawable.ennio,
            R.drawable.backstreet_boys };
    public static String[] duration = new String[] { "4:47", "4:38", "5:44",
            "4:23", "6:58", "4:47", "3:41", "4:07", "2:42", "3:56", "4:47",
            "4:38", "5:44", "4:23", "6:58", "4:47", "3:41", "4:07", "2:42",
            "3:56" };
}

 

項目結構


2014-07-09_174417_副本

圖 1 項目結構

  • com.my.android.app.activit 包,是主程序和三個演示的界面后台;
  • com.my.android.app.activit.data 包,是測試數據;
  • com.my.android.app.activit.view 包,是實現下拉上拉“刷新最新”和“加載更多”。

 

演示


本程序主界面,如圖 2 所示。包含三個演示,“example 1:Simple Push and Pull”是上拉下拉刷新最新和加載更多的演示,刷新或加載的內容只是當前時間而已;“example 2:Custom Music List”是顯示歌曲列表。

當點擊第三個按鈕“Combine example 1 and 2”時,就是本文想要達到的效果,如圖 3 所示。

  • 初始狀態為圖 3 左邊,加載 20 個音樂;
  • 當用手指向下拉動時,獲得最新的 10 個,如圖 3 中間所示;
  • 當向上拉動時,加載之前的 10 個,如圖 3 右邊所示。

1

圖 2 主程序

4-1_副本

圖 3 本文想要達到的效果:下拉或上拉,刷新最新或加載更多的歌曲列表

剛開始,若想實現這個功能,着實不易,主要是沒思路。疑問在於,如何實現下拉和上拉?如何顯示歌曲列表?又如何刷新最近、加載更多的歌曲列表?可你要是研究一下本程序前面的兩個演示:example 1 和 example 2,這兩個示例在網上很容易找到,那么,以上所有的問題,都迎刃而解了。

2-1_副本 

圖 4 簡單的下拉或上拉,刷新最新或加載更多

圖 4 演示的核心代碼如下所示:

package com.my.android.app.activity;
 
import java.util.ArrayList;
import java.util.Date;
 
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
//import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.RelativeLayout;
import android.widget.Toast;
 
import com.my.android.app.R;
import com.my.android.app.view.PushPullList;
import com.my.android.app.view.PushPullListListener;
 
@SuppressLint("HandlerLeak")
public class PushPullListTest extends Activity implements PushPullListListener {
 
    PushPullList list;
    ArrayList<String> data;
    ArrayAdapter<String> adapter;
 
    // 刷新控件狀態
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0) { // 刷新最新
                adapter.notifyDataSetChanged();
                list.doneRefresh();
                Toast.makeText(PushPullListTest.this,
                        "新加載" + msg.arg1 + "條數據!", Toast.LENGTH_LONG).show();
            } else if (msg.what == 1) { // 加載更多
                adapter.notifyDataSetChanged();
                list.doneMore();
            } else {
                super.handleMessage(msg);
            }
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pushpulllist);
 
        // 添加自定義控件
        list = new PushPullList(this);
        RelativeLayout root = (RelativeLayout) findViewById(R.id.root_a);
        root.addView(list);
 
        data = new ArrayList<String>();
        for (int i = 1; i < 10; ++i) {
            data.add(new Date().toLocaleString());
        }
 
        adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_expandable_list_item_1, data);
        list.setAdapter(adapter);
        list.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) {
                Toast.makeText(PushPullListTest.this, data.get(arg2 - 1),
                        Toast.LENGTH_LONG).show();
                // Log.i("", data.get(arg2 - 1));
            }
        });
 
        list.setDoMoreWhenBottom(false); // 滾動到低端的時候不自己加載更多
        list.setOnRefreshListener(this); // 刷新最新 的監聽
        list.setOnMoreListener(this); // 加載更多 的監聽
    }
 
    @Override
    public boolean onRefreshOrMore(PushPullList dynamicListView,
            boolean isRefresh) {
        if (isRefresh) {
            new Thread(r_refresh).start();
        } else {
            new Thread(r_more).start();
        }
        return false;
    }
 
    // 刷新最新,插入前邊
    Runnable r_refresh = new Runnable() {
        @Override
        public void run() {
            // 下拉,刷新最新
            ArrayList<String> temp = new ArrayList<String>();
            for (int i = 0; i < 3; ++i) {
                temp.add(0, new Date().toLocaleString());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (data) {
                data.addAll(0, temp);
            }
 
            Message message = new Message();
            message.what = 0;
            message.arg1 = temp.size();
            handler.sendMessage(message);
        }
    };
    // 加載更多,插入末尾
    Runnable r_more = new Runnable() {
        @Override
        public void run() {
            // 上拉,加載更多
            ArrayList<String> temp = new ArrayList<String>();
            for (int i = 0; i < 3; ++i) {
                temp.add(new Date().toLocaleString());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (data) {
                data.addAll(temp);
            }
            handler.sendEmptyMessage(1);
        }
    };
}

圖 4 主要是自定義了一個 Listview,並繼承 android.widget.AbsListView.OnScrollListener 接口,至於如何定義 PushPullListListenerPushPullList,請自行下載源代碼,這樣就能實現上拉或下拉功能。通過上面代碼,你就能了解基本的運行情況。

  • onCreate 方法,創建一個自定義 ListView——PushPullList,並添加到頁面,然后創建一個 Adapter,用 list.setAdapter 設置該 Adaper;
  • onRefreshOrMore 方法,是分別利用兩個線程,下拉或上拉后,刷新最新或加載更多。

3

圖 5 歌曲列表

圖 5 核心代碼如下所示:

package com.my.android.app.activity;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import android.app.Activity;
import android.os.Bundle;
 
import android.view.View;
 
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
 
import android.widget.ListView;
 
import android.widget.SimpleAdapter;
import android.widget.Toast;
 
import com.my.android.app.data.TestData;
 
import com.my.android.app.R;
 
public class CustomListTest extends Activity {
 
    ListView list;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_customlist);
 
        // 創建一個List集合,List集合的元素是Map
        List<Map<String, Object>> listItems = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < TestData.artist.length; i++) {
            Map<String, Object> listItem = new HashMap<String, Object>();
            listItem.put("thumb", TestData.thumb[i]);
            listItem.put("artist", TestData.artist[i]);
            listItem.put("title", TestData.title[i]);
            listItem.put("duration", TestData.duration[i]);
            listItems.add(listItem);
        }
        // 創建一個SimpleAdapter
        SimpleAdapter simpleAdapter = new SimpleAdapter(this, listItems,
                R.layout.list_row, new String[] { "artist", "title", "thumb",
                        "duration" }, new int[] { R.id.artist, R.id.title,
                        R.id.imagethumb, R.id.duration });
        list = (ListView) findViewById(R.id.mylist_a);
        // 為ListView設置Adapter
        list.setAdapter(simpleAdapter);
        // 為ListView的列表項單擊事件綁定事件監聽器
        list.setOnItemClickListener(new OnItemClickListener() {
            // 第position項被單擊時激發該方法。
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                Toast.makeText(getApplicationContext(),
                        TestData.artist[position] + "被單擊了", Toast.LENGTH_SHORT)
                        .show();
            }
        });
        list.setOnItemSelectedListener(new OnItemSelectedListener() {
            // 第position項被選中時激發該方法。
            @Override
            public void onItemSelected(AdapterView<?> parent, View view,
                    int position, long id) {
                Toast.makeText(getApplicationContext(),
                        TestData.artist[position] + "被選中了", Toast.LENGTH_SHORT)
                        .show();
            }
 
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
        /*
         * list = (ListView) findViewById(R.id.mylist); adapter = new
         * ComplexAdapter(this, TestData.thumb_url); list.setAdapter(adapter);
         */
 
        /*
         * Button b = (Button) findViewById(R.id.btn_clear_a);
         * b.setOnClickListener(new OnClickListener() {
         * 
         * @Override public void onClick(View arg0) {
         * adapter.imageLoader.clearCache(); adapter.notifyDataSetChanged(); }
         * });
         */
    }
 
    @Override
    public void onDestroy() {
        list.setAdapter(null);
        super.onDestroy();
    }
}

注意上面代碼段中 Adapter。圖 5 的目的是如何利用 android.widget.SimpleAdapter android.widget.ListView 顯示歌曲列表。

因此,若將圖 5 的 android.widget.ListView 改為圖 4 的自定義 ListView,就能實現想要的功能,而且修改起來相當容易,只改一個地方。如果想實現更強大的功能,還可以繼承 android.widget.BaseAdapter 自定義 Adpater,本文只是使用 SimpleAdapter 。核心代碼如下所示:

package com.my.android.app.activity;
 
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import com.my.android.app.R;
import com.my.android.app.data.TestData;
import com.my.android.app.view.PushPullList;
import com.my.android.app.view.PushPullListListener;
 
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.RelativeLayout;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
 
public class ComplexListTest extends Activity implements PushPullListListener {
 
    PushPullList list;
    List<Map<String, Object>> listItems = null;
    SimpleAdapter simpleAdapter = null;
    // ArrayAdapter<String> adapter;
    int len = TestData.artist.length;
    int step = 10;
 
    // 刷新控件狀態
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0) { // 刷新最新
                simpleAdapter.notifyDataSetChanged();
                list.doneRefresh();
                Toast.makeText(ComplexListTest.this, "新加載" + msg.arg1 + "條數據!",
                        Toast.LENGTH_LONG).show();
            } else if (msg.what == 1) { // 加載更多
                simpleAdapter.notifyDataSetChanged();
                list.doneMore();
            } else {
                super.handleMessage(msg);
            }
        }
    };
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_complexlist);
 
        // 添加自定義控件
        list = new PushPullList(this);
        RelativeLayout root = (RelativeLayout) findViewById(R.id.root_b);
        root.addView(list);
 
        // 創建一個List集合,List集合的元素是Map
        listItems = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < TestData.artist.length; i++) {
            Map<String, Object> listItem = new HashMap<String, Object>();
            listItem.put("thumb", TestData.thumb[i]);
            listItem.put("artist", TestData.artist[i]);
            listItem.put("title", TestData.title[i]);
            listItem.put("duration", TestData.duration[i]);
            listItems.add(listItem);
        }
        // 創建一個SimpleAdapter
        simpleAdapter = new SimpleAdapter(this, listItems, R.layout.list_row,
                new String[] { "artist", "title", "thumb", "duration" },
                new int[] { R.id.artist, R.id.title, R.id.imagethumb,
                        R.id.duration });
        // 為ListView設置Adapter
        list.setAdapter(simpleAdapter);
 
        list.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3) {
                Toast.makeText(ComplexListTest.this,
                        listItems.get(arg2 - 1).toString(), Toast.LENGTH_LONG)
                        .show();
                // Log.i("", data.get(arg2 - 1));
            }
        });
 
        list.setDoMoreWhenBottom(false); // 滾動到低端的時候不自己加載更多
        list.setOnRefreshListener(this); // 刷新最新 的監聽
        list.setOnMoreListener(this); // 加載更多 的監聽
    }
 
    @Override
    public void onDestroy() {
        list.setAdapter(null);
        super.onDestroy();
    }
 
    @Override
    public boolean onRefreshOrMore(PushPullList dynamicListView,
            boolean isRefresh) {
        if (isRefresh) {
            new Thread(r_refresh).start();
        } else {
            new Thread(r_more).start();
        }
        return false;
    }
 
    // 刷新最新,插入前邊
    Runnable r_refresh = new Runnable() {
        @Override
        public void run() {
            // 下拉,刷新最新
            int pos = 0;
            List<Map<String, Object>> temp = new ArrayList<Map<String, Object>>();
            for (int i = len + 1; i <= len + step; ++i) {
                pos = i % TestData.artist.length;
                Map<String, Object> item = new HashMap<String, Object>();
                item.put("thumb", TestData.thumb[pos]);
                item.put("artist", TestData.artist[pos]);
                item.put("title", TestData.title[pos]);
                item.put("duration", TestData.duration[pos]);
                temp.add(item);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (listItems) {
                listItems.addAll(0, temp);
            }
            len = len + step;
            Message message = new Message();
            message.what = 0;
            message.arg1 = temp.size();
            handler.sendMessage(message);
        }
    };
    // 加載更多,插入末尾
    Runnable r_more = new Runnable() {
        @Override
        public void run() {
            // 上拉,加載更多
            int pos = 0;
            List<Map<String, Object>> temp = new ArrayList<Map<String, Object>>();
            for (int i = len + 1; i <= len + step; ++i) {
                pos = i % TestData.artist.length;
                Map<String, Object> item = new HashMap<String, Object>();
                item.put("thumb", TestData.thumb[pos]);
                item.put("artist", TestData.artist[pos]);
                item.put("title", TestData.title[pos]);
                item.put("duration", TestData.duration[pos]);
                temp.add(item);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (listItems) {
                listItems.addAll(0, temp);
            }
 
            len = len + step;
            handler.sendEmptyMessage(1);
        }
    };
}

除了改變了 Adapter 外,其他代碼都沒有變化。

 

參考資料


 

下載 Demo

下載 Demo v.1.1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM