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" };
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
项目结构

图 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 右边所示。

图 2 主程序

图 3 本文想要达到的效果:下拉或上拉,刷新最新或加载更多的歌曲列表
刚开始,若想实现这个功能,着实不易,主要是没思路。疑问在于,如何实现下拉和上拉?如何显示歌曲列表?又如何刷新最近、加载更多的歌曲列表?可你要是研究一下本程序前面的两个演示:example 1 和 example 2,这两个示例在网上很容易找到,那么,以上所有的问题,都迎刃而解了。

图 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);
}
};
}
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
图 4 主要是自定义了一个 Listview,并继承 android.widget.AbsListView.OnScrollListener 接口,至于如何定义 PushPullListListener 和 PushPullList,请自行下载源代码,这样就能实现上拉或下拉功能。通过上面代码,你就能了解基本的运行情况。
onCreate 方法,创建一个自定义 ListView——PushPullList,并添加到页面,然后创建一个 Adapter,用 list.setAdapter 设置该 Adaper; onRefreshOrMore 方法,是分别利用两个线程,下拉或上拉后,刷新最新或加载更多。

图 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 外,其他代码都没有变化。
参考资料
- Android 自定义 ListView 上下拉动刷新最新和加载更多
- Android ListView 和 ***Adapter 从本地/网络获取歌曲列表
- Android 自定义 ListView 显示网络上 JSON 格式歌曲列表
Android 自定义 ListView 上下拉动“刷新最新”和“加载更多”歌曲列表的更多相关文章
- 自定义ListView下拉刷新上拉加载更多
自定义ListView下拉刷新上拉加载更多 自定义RecyclerView下拉刷新上拉加载更多 Listview现在用的很少了,基本都是使用Recycleview,但是不得不说Listview具有划时 ...
- Android 自定义 ListView 上下拉动刷新最新和加载更多
本文内容 开发环境 演示上下拉动刷新最新和加载更多 ListView 参考资料 本文演示上下拉动,刷新最新和加载更多,这个效果很常见,比如,新闻资讯类 APP,当向下拉动时,加载最新的资讯:向上拉动时 ...
- ListView下拉刷新上拉加载更多实现
这篇文章将带大家了解listview下拉刷新和上拉加载更多的实现过程,先看效果(注:图片中listview中的阴影可以加上属性android:fadingEdge="none"去掉 ...
- react-native-page-listview使用方法(自定义FlatList/ListView下拉刷新,上拉加载更多,方便的实现分页)
react-native-page-listview 对ListView/FlatList的封装,可以很方便的分页加载网络数据,还支持自定义下拉刷新View和上拉加载更多的View.兼容高版本Flat ...
- android ListView下拉刷新 上拉加载更多
背景 最近在公司的项目中要使用到ListView的下拉刷新和上拉加载更多(貌似现在是个项目就有这个功能!哈哈),其实这个东西GitHub上很多,但是我感觉那些框架太大,而且我这个项目只用到了ListV ...
- listview下拉刷新和上拉加载更多的多种实现方案
listview经常结合下来刷新和上拉加载更多使用,本文总结了三种常用到的方案分别作出说明. 方案一:添加头布局和脚布局 android系统为listview提供了addfootview ...
- Android项目:使用pulltorefresh开源项目扩展为下拉刷新上拉加载更多的处理方法,监听listview滚动方向
很多android应用的下拉刷新都是使用的pulltorefresh这个开源项目,但是它的扩展性在下拉刷新同时又上拉加载更多时会有一定的局限性.查了很多地方,发现这个开源项目并不能很好的同时支持下拉刷 ...
- listview下拉刷新上拉加载扩展(二)-仿美团外卖
经过前几篇的listview下拉刷新上拉加载讲解,相信你对其实现机制有了一个深刻的认识了吧,那么这篇文章我们来实现一个高级的listview下拉刷新上拉加载-仿新版美团外卖的袋鼠动画: 项目结构: 是 ...
- Android 实现下拉刷新和上拉加载更多的RECYCLERVIEW和SCROLLVIEW
PullRefreshRecyclerView.java /** * 类说明:下拉刷新上拉加载更多的RecyclerView * Author: gaobaiq * Date: 2016/5/9 18 ...
随机推荐
- swift笔记(二) —— 运算符
基本运算符 Swift支持大部分的标准C语言的操作符,而且做了一些改进,以帮助开发人员少犯低级错误,比方: 本该使用==的时候,少写了个=, if x == y {-} 写成了 if x = y {- ...
- 通过NTP协议进行时间同步
最近发现手机的时间不是很准了,便到网上下了一个同步时间的小程序,简单了看了一下它的原理,是通过NTP协议来实现校时的,就顺便学习了一下NTP协议,用C#写了个简单的实现. NTP(Network Ti ...
- Spring整合Disruptor
原文:https://segmentfault.com/a/1190000014469173 什么是Disruptor 从功能上来看,Disruptor 是实现了“队列”的功能,而且是一个有界队列.那 ...
- Running Jenkins behind Nginx
original : https://wiki.jenkins-ci.org/display/JENKINS/Running+Jenkins+behind+Nginx In situations wh ...
- android 玩转ContentProvider之二--实现多个ContentProvider对多张表进行操作
SQLite数据库直接操作类: DatabaseHelper.java package com.jacp.database; import android.content.Context; impor ...
- Android 自动编译、打包生成apk文件 2 - 使用原生Ant方式
from://http://blog.csdn.net/androiddevelop/article/details/11100109 相关文章列表: <Android 自动编译.打包生成apk ...
- 架构:Eventually Consistent - Revisited
原文地址:http://i.cnblogs.com/EditPosts.aspx?opt=1. I wrote a first version of this posting on consisten ...
- [LNU.Machine Learning.Question.1]梯度下降方法的一些理解
曾经学习machine learning,在regression这一节,对求解最优化问题的梯度下降方法,理解总是处于字面意义上的生吞活剥. 对梯度的概念感觉费解?到底是标量还是矢量?为什么沿着负梯度方 ...
- 同志亦凡人第一季/全集BQueer As Folk 1迅雷下载
同志亦凡人 第一季 Queer as Folk Season 1 (2000) 本季看点:本剧叙述一群同志男女在美国匹兹堡的生活,剧情重心由原来三位男主角Brian,Michael,Justin之间的 ...
- Gradle 简介
一.简介 Gradle 是 Android 现在主流的编译工具,虽然在Gradle 出现之前和之后都有对应更快的编译工具出现,但是 Gradle 的优势就在于它是亲儿子,Gradle 确实比较慢,这和 ...