本文主要介绍可同时实现下拉刷新及滑动到底部加载更多的ListView的使用。
该ListView优点包括:a. 可自定义下拉响应事件(如下拉刷新) b.可自定义滚动到底部响应的事件(如滑动到底部加载更多) c.可自定义丰富的样式 d.高效(若下拉样式关闭不会加载其布局,同listView效率一致) e. 丰富的设置。
更多下拉刷新开源项目可见 Android 下拉刷新。底部加载更多开源项目可见 Android 底部加载更多。
Demo APK 可以方便的查看效果,在各大应用商店搜索 trinea android 下载即可,如:Google Play。
可运行代码地址可见DropDownListViewDemo,效果图如下:
1、引入公共库
引入TrineaAndroidCommon@Github(欢迎star和fork^_^)作为你项目的library(如何拉取代码及添加公共库),或是自己抽取其中的DropDownListView@Github部分使用。
2、在layout中定义
将布局中的ListView标签换成cn.trinea.android.common.view.DropDownListView标签
并加上自定义属性的命名空间xmlns:listViewAttr=”http://schemas.android.com/apk/res/cn.trinea.android.demo”,其中cn.trinea.android.demo需要用自己的包名替换。如何自定义属性及其命名空间可见本文最后。xml代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:listViewAttr="http://schemas.android.com/apk/res/cn.trinea.android.demo" android:layout_width="match_parent" android:layout_height="match_parent" > <cn.trinea.android.common.view.DropDownListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:drawSelectorOnTop="false" android:paddingBottom="@dimen/dp_40" listViewAttr:isDropDownStyle="true" listViewAttr:isOnBottomStyle="true" listViewAttr:isAutoLoadOnBottom="true" /> </RelativeLayout> |
DropDownListView自定义了三个boolean属性
1 2 3 4 5 |
<declare-styleable name="drop_down_list_attr"> <attr name="isDropDownStyle" format="boolean" /> <attr name="isOnBottomStyle" format="boolean" /> <attr name="isAutoLoadOnBottom" format="boolean" /> </declare-styleable> |
isDropDownStyle表示是否允许下拉样式,java代码中可自定义下拉listener,表示需要完成的任务
isOnBottomStyle表示是否允许底部样式,java代码中可自定义滚动到底部的listener,表示需要完成的任务
isAutoLoadOnBottom表示是否允许滚动到底部时自动执行对应listener,仅在isOnBottomStyle为true时有效
PS:如果isDropDownStyle或isOnBottomStyle为false,并不会加载对应的布局,所以性能同ListView一样。
3、在Java类中调用
通过setOnDropDownListener设置下拉的事件,不过需要在事件结束时手动调用onDropDownComplete恢复状态(注意需要在adapter.notifyDataSetChanged();后面调用)
通过setOnBottomListener设置滚动到底部的事件,不过需要在事件结束时手动调用onBottomComplete恢复状态,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
/** * DropDownListViewDemo * * @author Trinea 2013-6-1 */ public class DropDownListViewDemo extends BaseActivity { private LinkedList<String> listItems = null; private DropDownListView listView = null; private ArrayAdapter<String> adapter; private String[] mStrings = { "Aaaaaa", "Bbbbbb", "Cccccc", "Dddddd", "Eeeeee", "Ffffff", "Gggggg", "Hhhhhh", "Iiiiii", "Jjjjjj", "Kkkkkk", "Llllll", "Mmmmmm", "Nnnnnn", }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState, R.layout.drop_down_listview_demo); listView = (DropDownListView)findViewById(R.id.list_view); // set drop down listener listView.setOnDropDownListener(new OnDropDownListener() { @Override public void onDropDown() { new GetDataTask(true).execute(); } }); // set on bottom listener listView.setOnBottomListener(new OnClickListener() { @Override public void onClick(View v) { new GetDataTask(false).execute(); } }); listItems = new LinkedList<String>(); listItems.addAll(Arrays.asList(mStrings)); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, listItems); listView.setAdapter(adapter); } private class GetDataTask extends AsyncTask<Void, Void, String[]> { private boolean isDropDown; public GetDataTask(boolean isDropDown){ this.isDropDown = isDropDown; } @Override protected String[] doInBackground(Void... params) { try { Thread.sleep(1000); } catch (InterruptedException e) { ; } return mStrings; } @Override protected void onPostExecute(String[] result) { if (isDropDown) { listItems.addFirst("Added after drop down"); adapter.notifyDataSetChanged(); // should call onDropDownComplete function of DropDownListView at end of drop down complete. SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss"); listView.onDropDownComplete(getString(R.string.update_at) + dateFormat.format(new Date())); } else { listItems.add("Added after on bottom"); adapter.notifyDataSetChanged(); // should call onBottomComplete function of DropDownListView at end of on bottom complete. listView.onBottomComplete(); } super.onPostExecute(result); } } } |
4、高级接口设置
public void setOnDropDownListener(OnDropDownListener onDropDownListener)设置下拉的事件
public void onDropDownComplete()和public void onDropDownComplete(CharSequence secondText)恢复下拉状态,注意onDropDownComplete需要在adapter.notifyDataSetChanged();后面调用
public void setOnBottomListener(OnClickListener onBottomListener)设置滚动到底部的事件
public void onBottomComplete()恢复底部状态
public void setSecondPositionVisible()
在isDropDownStyle为true情况下,drop down的header layout为ListView position为0的item,所以可能需要调用(如adapter.notifyDataSetChanged())setSecondPositionVisible()设置position为1(即第二个)的item可见。setSelection(0)滚动到的header layout的item。onDropDownComplete()默认已经调用setSecondPositionVisible()
public void setDropDownStyle(boolean isDropDownStyle)同xml中的isDropDownStyle属性,表示是否为下拉样式,下拉释放后执行onDropDownListener
public void setOnBottomStyle(boolean isOnBottomStyle)同xml中isOnBottomStyle属性,表示滚动到底部执行onBottomListener样式
public void setAutoLoadOnBottom(boolean isAutoLoadOnBottom)同xml中isAutoLoadOnBottom属性,表示滚动到底部是否自动执行onBottomListener
public void setHeaderPaddingTopRate(float headerPaddingTopRate)设置header padding top距离与实际下拉距离的比例
public void setHeaderReleaseMinDistance(int headerReleaseMinDistance)设置header可释放执行onDropDownListener的最小距离
public void setShowFooterProgressBar(boolean isShowFooterProgressBar)设置底部是否显示progressbar
public void setHasMore(boolean hasMore)set whether has more
public void setHeaderDefaultText(String headerDefaultText)设置header默认文字, default is R.string.drop_down_list_header_default_text
public void setHeaderPullText(String headerPullText)设置header下拉提示文字, default is R.string.drop_down_list_header_pull_text
public void setHeaderReleaseText(String headerReleaseText)设置header可释放提示文字, default is R.string.drop_down_list_header_release_text
public void setHeaderLoadingText(String headerLoadingText)设置header加载中提示文字, default is R.string.drop_down_list_header_loading_text
public void setFooterDefaultText(String footerDefaultText)设置footer默认文字, default is R.string.drop_down_list_footer_default_text
public void setFooterLoadingText(String footerLoadingText)设置footer加载中提示文字, default is R.string.drop_down_list_footer_loading_text
public void setFooterNoMoreText(String footerNoMoreText)设置footer没有更多提示文字, default is R.string.drop_down_list_footer_no_more_text
public void setHeaderSecondText(CharSequence secondText)设置header第二部分文字, default is null
5、样式设置(自定义header和footer信息)
将TrineaAndroidCommon作为lib引入之后,可以在自己工程内重定义某些资源,覆盖TrineaAndroidCommon中的设置。
自定义下拉的图片,在项目drawable资源下添加文件名为drop_down_list_arrow.png的图片即可
定义相关文字,strings.xml中定义下面属性:
1 2 3 4 5 6 7 |
<string name="drop_down_list_header_default_text">点击可以刷新</string> <string name="drop_down_list_header_pull_text">下拉可以刷新</string> <string name="drop_down_list_header_release_text">松开可以刷新</string> <string name="drop_down_list_header_loading_text">加载中…</string> <string name="drop_down_list_footer_default_text">更多</string> <string name="drop_down_list_footer_loading_text">加载中…</string> <string name="drop_down_list_footer_no_more_text">没有更多了</string> |
定义相关字体颜色,colors.mxl中定义下面属性:
1 2 3 4 5 6 |
<!-- drop down list header font color --> <color name="drop_down_list_header_font_color">#000000</color> <!-- drop down list header second font color --> <color name="drop_down_list_header_second_font_color">#000000</color> <!-- drop down list footer font color --> <color name="drop_down_list_footer_font_color">#000000</color> |
定义相关样式(会覆盖前面的string和color定义),styles.xml中定义下面属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<style name="drop_down_list_header_progress_bar_style"> <item name="android:minHeight">@dimen/drop_down_list_header_progress_bar_height</item> <item name="android:maxHeight">@dimen/drop_down_list_header_progress_bar_height</item> </style> <style name="drop_down_list_footer_progress_bar_style"> <item name="android:minHeight">@dimen/drop_down_list_footer_progress_bar_height</item> <item name="android:maxHeight">@dimen/drop_down_list_footer_progress_bar_height</item> </style> <style name="drop_down_list_header_font_style"> <item name="android:textColor">@color/drop_down_list_header_font_color</item> <item name="android:textAppearance">?android:attr/textAppearanceMedium</item> </style> <style name="drop_down_list_header_second_font_style"> <item name="android:textColor">@color/drop_down_list_header_second_font_color</item> <item name="android:textAppearance">?android:attr/textAppearanceSmall</item> </style> <style name="drop_down_list_footer_font_style"> <item name="android:textColor">@color/drop_down_list_footer_font_color</item> <item name="android:textAppearance">?android:attr/textAppearanceMedium</item> </style> |
定义相关dimen值,dimens.xml中定义下面属性:
1 2 3 4 |
<dimen name="drop_down_list_header_padding_top">12dp</dimen> <dimen name="drop_down_list_header_padding_bottom">15dp</dimen> <dimen name="drop_down_list_header_progress_bar_height">36dp</dimen> <dimen name="drop_down_list_footer_progress_bar_height">36dp</dimen> |
我发现个问题,假如当前显示的是第一行,然后轻轻的向下拉很小的距离,整个listview会弹一下,像屏幕闪了一下,感觉体验不是很好,但是向下拉的距离够大他就不会弹,请问这个应该怎么解决呢?
用这里关于 下拉刷新 ListView 的第一个 https://github.com/Trinea/android-open-project
按照DEMO的调用方式,只要执行过下拉刷新之后上拉加载就会出现重复加载停不下来!
demo 代码都写的这么清楚,还调不通!!
可以用,
报错:Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class cn.trinea.android.common.view.DropDownListView,我添加了trinea-android-common.jar包,我看到我拉下来的源码里面有cn.trinea.android.common.view.DropDownListView这个文件的,这是什么原因啊
存在资源,需要以工程依赖
具体要怎么做啊,而且由于导入开源的jar包,R.java文件的内容被合并了,如果我要删了jar包再重导要怎么做呢?我试了在lib文件夹下remove path然后删掉,再导入,我的R.java文件就不见了,求指导啊
http://www.trinea.cn/android/%e6%b7%bb%e5%8a%a0android-common-lib%e6%b7%bb%e5%8a%a0%e5%8f%8agoogle-code%e5%92%8cgithub%e5%a6%82%e4%bd%95%e6%8b%89%e5%8f%96%e4%bb%a3%e7%a0%81/
我是按照这个方法拉下来的,然后添加进去的,那个缓存图片的方法我调用没有问题,但是listview的就有问题
不是将jar加载进来,那篇文章第3部分介绍了如何添加
我想点击底部加载更多时出现加载进度框,如下,但是会出现增加两条数据的情况public void onClick(View v) { mDropDownListView.onBottom(); new GetDataTask(false).execute(); }
setShowFooterProgressBar
可以,怎么使用请自己看这篇文章
请问:如果使用baseadapter加载网络上数据,复杂布局显示,这个上滑分页可以使用吗,如何使用呢?
可以,怎么使用请自己看这篇文章
我写了一个分页,为什么每次上滑分页后只显示最后一页呢?上一页就不显示了,应该是追加的。代码如下public class DropDownListViewDemo extends Activity { private List story_entrys = null; private DropDownListView articles_list = null; public static final int MORE_DATA_MAX_COUNT = 30; public int moreDataCount = 0; private static final int success = 1; private static final int fail = 0; public int itemsize = 0; MyUtil myUtil = new MyUtil(); private Bitmap bitmap; private DataHandler dataHandler = new DataHandler(); MyAdapter adapter = new MyAdapter(story_entrys); ImageLoader imageLoader = new ImageLoader(this, false); public HashMap imagesCache = new HashMap();// 图片缓存 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.drop_down_listview_demo); articles_list = (DropDownListView) findViewById(R.id.list_view); // set drop down listener articles_list.setOnDropDownListener(new OnDropDownListener() { @Override public void onDropDown() { new GetDataTask(true).execute(); } }); new DataThread().start(); // set on bottom listener articles_list.setOnBottomListener(new OnClickListener() { @Override public void onClick(View v) { new GetDataTask(false).execute(); } }); articles_list.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ToastUtils.show(DropDownListViewDemo.this, story_entrys.get(position – 1).getId()); Intent intent = new Intent(); intent.setClass(DropDownListViewDemo.this, DropDownListViewDemo.class); intent.putExtra(“id”, story_entrys.get(position – 1).getId()); startActivity(intent); } }); articles_list.setShowFooterWhenNoMore(true); } private class GetDataTask extends AsyncTask> { private boolean isDropDown; public GetDataTask(boolean isDropDown) { this.isDropDown = isDropDown; } @Override protected List doInBackground(Void… params) { try { Thread.sleep(1000); } catch (InterruptedException e) { ; } return story_entrys; } @Override protected void onPostExecute(List result) { if (isDropDown) { moreDataCount = 0; new DataThread().start(); story_entrys.addAll(story_entrys); //adapter.notifyDataSetChanged(); // should call onDropDownComplete function of DropDownListView // at end of drop down complete. SimpleDateFormat dateFormat = new SimpleDateFormat( “yyyy-MM-dd HH:mm:ss”); articles_list.onDropDownComplete(getString(R.string.update_at) + dateFormat.format(new Date())); } else { moreDataCount++; new DataThread().start(); story_entrys.addAll(story_entrys); //adapter.addNewData(story_entrys); System.out.println(“页数:” + moreDataCount + “####” + story_entrys.size()); if (moreDataCount >= MORE_DATA_MAX_COUNT) { articles_list.setHasMore(false); } // should call onBottomComplete function of DropDownListView at // end of on bottom complete. articles_list.onBottomComplete(); } super.onPostExecute(result); } } private class DataHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case success: MyAdapter mAdapter =new MyAdapter(story_entrys); articles_list.setAdapter(mAdapter); mAdapter.addNewData(story_entrys); break; case fail: Toast.makeText(DropDownListViewDemo.this, “获取数据失败”, Toast.LENGTH_SHORT).show(); default: break; } } } // 定义自己的适配器 private class MyAdapter extends BaseAdapter { private List mData; public MyAdapter(List data){ mData = data; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { final Story_entryC sc = mData.get(position); View view = View.inflate(DropDownListViewDemo.this, R.layout.article_item, null); if (position % 2 == 0) { view.setBackgroundColor(Color.rgb(251, 248, 226)); // 颜色设置 } else { view.setBackgroundColor(Color.rgb(230, 246, 247)); // 颜色设置 } TextView title = (TextView) view.findViewById(R.id.article_title); title.setText(sc.getTitle()); TextView brief = (TextView) view.findViewById(R.id.article_brief); brief.setText(sc.getBrief()); TextView cname = (TextView) view.findViewById(R.id.cname); cname.setText(sc.getCname()); TextView view_num = (TextView) view.findViewById(R.id.view_num); view_num.setText(“阅读 ” + sc.getView_num()); TextView keep_num = (TextView) view.findViewById(R.id.keep_num); keep_num.setText(“收藏 ” + sc.getKeep_num()); ImageView iv = (ImageView) view.findViewById(R.id.article_pics); bitmap = imageLoader.getBitmap(sc.getThumb()); iv.setImageBitmap(bitmap); return view; } public void addNewData(List data){ mData.addAll(data); notifyDataSetChanged(); } } String id = “”; String name = “”; private class DataThread extends Thread { final int mypage = moreDataCount; @Override public void run() { String address = DropDownListViewDemo.this.getResources() .getString(R.string.article_entry_url); try { System.out.println(“需要使用的页数是:” + mypage); story_entrys = myUtil.getJsonStory_entry(address + “&cid=1″ + “&page=” + mypage); String urls = address + “&cid=1″ + “&page=” + mypage; System.out.println(urls); Message mes = new Message(); mes.what = success; dataHandler.sendMessage(mes); } catch (Exception e) { e.printStackTrace(); Message mes = new Message(); mes.what = fail; dataHandler.sendMessage(mes); } super.run(); } }}
细节自己调
BaseActivity是什么
https://github.com/Trinea/android-demo
请问 xmlns:listViewAttr=”http://schemas.android.com/apk/res/com.example.xiashang” 这里为什么改成我自己的包名他会报错啊 而且启动的时候也会报 android library projects cannot be launch
工程Clean一下重新编译,另外可以和示例Demo工程对比看看
额 解决了 我勾了个 is library
告诉我这里都可以遇到你,你这么叼。这个控件会玩了不。为什么数据不足一屏是,head 和 foot都出来了,这个还能用?
用这里关于 下拉刷新 ListView 的第一个 https://github.com/Trinea/android-open-project
同样需求。不过没明白你们怎么实现的,能说的详细些吗,3Q
请问第二个问题解决了嘛?我在github上获取的源工程,上拉加载的时候,会一直重复运行加载的问题;而且博主,我觉得如果没有数据的时候,同时显示header和footer,看起来样子确实挺怪的,只显示header倒是挺合理的
第二个可以看看https://github.com/Trinea/android-demo中的调用方法。没有数据时关于header和footer同时显示问题暂时没时间解决,这种情况下你那边比较好的解决方法就是整个ListView不显示
楼主您好 还得请教您一下 判定点击刷新还是下拉刷新 的逻辑是怎样的?具体代码在什么位置?
https://github.com/Trinea/android-common/blob/master/src/cn/trinea/android/common/view/DropDownListView.java#L381 这是释放下拉刷新。 https://github.com/Trinea/android-common/blob/master/src/cn/trinea/android/common/view/DropDownListView.java#L186 这是点击刷新onDropDown就是具体执行的函数