本文内容
- 环境
- 测试数据
- 项目结构
- 演示
- 参考资料
本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表。所谓“刷新最新”和“加载更多”是指日期。演示代码太多,点击此处下载,自己调试一下。
下载 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" };
}
项目结构
图 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);
}
};
}
图 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 格式歌曲列表