本文主要介绍可同时实现下拉刷新及滑动到底部加载更多的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> |
刚看了 更新后的代码,下拉刷新,好像很费劲了。就是用户往下拉的时候,需要使劲往下拉,我用了两只手,才拉下来,正常拉下来还是 没有切换到 刷新那,还提示向下拉,,,麻烦大大 看下,,是不是代码的问题,,还是我自己出问题了
之前的代码没有问题,但是下拉的体验 感觉 不是很好,嘿嘿,不好意思啊,有的时候卡,有的时候拉的时候 突然蹦下来了。。。。
修复了,没适配好。不过在hdpi上你说的偶尔卡的问题还是存在,最近有时间会修复掉
嗯,好的,非常感谢啊。哈哈。。支持你
看完DropDownListView 的代码,试验了下发现: 1:修改 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totleItemCount) 中代码:if (headerLayout.getPaddingTop() >= headerOriginalHeight + headerReleaseMinDistance){ setHeaderStatusReleaseToLoad();}else if(headerLayout.getPaddingTop() < headerOriginalHeight + headerReleaseMinDistance){ setHeaderStatusDropDownToLoad();}2:修改 private void adjustHeaderPadding(MotionEvent event) 中代码:if (currentHeaderState == HEADER_STATUS_DROP_DOWN_TO_LOAD)这样headerReleaseMinDistance 参数才有意义。(之前代码里面这个参数是无效的)不然的话 headerLayout 由 HEADER_STATUS_DROP_DOWN_TO_LOAD 状态 变为HEADER_STATUS_RELEASE_TO_LOAD 状态太早,没有向下拖动一段距离后释放刷新的效果。(原代码的效果是:只要稍微拖动就出现了 释放刷新,原因在于onScroll( ) 中判断条件和padding设置时机有问题)
可能改为if (currentHeaderState == HEADER_STATUS_DROP_DOWN_TO_LOAD || currentHeaderState == HEADER_STATUS_RELEASE_TO_LOAD) 效果更好一点。
非常感谢你的反馈,我今天会看一下
之前的改发发现在下拉之始 有些问题 headrLayout会突然跳出来,我又改了下代码:1:public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totleItemCount) { if (headerLayout.getPaddingTop() >= headerReleaseMinDistance){ setHeaderStatusReleaseToLoad(); }else if(headerLayout.getPaddingTop() < headerReleaseMinDistance){ setHeaderStatusDropDownToLoad(); }2:private void adjustHeaderPadding(MotionEvent event){ int pointerCount = event.getHistorySize(); for (int i=0; i
2:private void adjustHeaderPadding(MotionEvent event){ if (currentHeaderState == HEADER_STATUS_RELEASE_TO_LOAD || (currentHeaderState == HEADER_STATUS_DROP_DOWN_TO_LOAD && headerLayout.getTop() == 0))
嗯,已经修复了这个问题并提交了代码。主要修改(1) onScroll函数中 if (headerLayout.getBottom() >= headerOriginalHeight + headerReleaseMinDistance || headerLayout.getTop() >= 0) {改为为if (headerLayout.getBottom() >= headerOriginalHeight + headerReleaseMinDistance) {这里最好用getBottom比较而不是getPaddingTop,否则下拉一定距离后上拉无法出现”下拉刷新提示”(2) adjustHeaderPadding函数中if (currentHeaderStatus == HEADER_STATUS_RELEASE_TO_LOAD) {改为if (currentHeaderStatus == HEADER_STATUS_DROP_DOWN_TO_LOAD || currentHeaderStatus == HEADER_STATUS_RELEASE_TO_LOAD) {你说的”下拉之始headrLayout会突然跳出来的问题”我这边没碰到
“下拉之 始headerLayout 会突然跳出来的问题” 意思是说 : 开始慢慢下拉的时候, headerLayout 不是平滑的一点点随着下拉出现,而是整个layout全部出现。 我这边有这个问题。将 adjustHeaderPadding函数中if (currentHeaderStatus == HEADER_STATUS_DROP_DOWN_TO_LOAD|| currentHeaderStatus == HEADER_STATUS_RELEASE_TO_LOAD) {改为:if (currentHeaderState == HEADER_STATUS_RELEASE_TO_LOAD || (currentHeaderState == HEADER_STATUS_DROP_DOWN_TO_LOAD && headerLayout.getTop() == 0)){就没有这个问题了
我想问下什么时候才是需要的时候?
listView.setDropDownStyle(false);listView.setOnBottomStyle(false);纠正下,可以通过这两个函数决定是否显示header和footer,不过要是很智能的话,需要确定listView item少于多少个时不显示header和footer,比如少于8个时候不显示可以listView.setDropDownStyle(adapter.getCount() > 8);listView.setOnBottomStyle(adapter.getCount() > 8);
Trinea你好,listview在加载条数少的情况下,加载完成后header和footer都显示出来了,有什么办法避免的?
你在DropDownListView.java中添加如下代码
就可以了,目前是这样,这个问题我现在也在想更好的解决方式放到DropDownListView中
listView.setDropDownStyle(false);listView.setOnBottomStyle(false);可以通过这两个函数决定是否显示header和footer,不过要是很智能的话,需要确定listView item少于多少个时不显示header和footer,比如少于8个时候不显示可以listView.setDropDownStyle(adapter.getCount() > 8);listView.setOnBottomStyle(adapter.getCount() > 8);
listview item的高度不固定的,如果是设置为8的话,不通用!即使<8但是已经超出了一屏的高度了,header 和 footer 就不会出现了,这时候下拉就不能用了
嗯,这个数字确实很麻烦,而且存在Cannot add header view to list — setAdapter has already been called.这个bug(setAdapter后不能addHeaderView),所以这种方式也行不通,暂时我也没想到比较好的方式。目前我这边的使用是不管item多少都显示,因为这时可以点击刷新和点击加载更多也是满足产品需求的
Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class cn.trinea.android.common.view.DropDownListView在eclipse调试运行后,报了一个这样的错,这是什么原因
公共库没添加成功啊,添加Android Common Lib
trineaandroidcommon.jar指的是这个么,我导入了啊
求明示啊
你的jar是哪儿下载的,可以直接把项目拉下来做为依赖库 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/
svn checkout http://trinea-android-demo.googlecode.com/svn/trunk/ trinea-android-demo-read-only我用的这个地址获取的代码
这个是示例工程的代码,我之前测试过,把libs下的包依赖添加进去应该没问题啊
我通过Build Path—>Add to BUild Path引入的libs下的jar包,这样没问题吧,难道是jar包有问题么?
不是jar有问题,是google不支持将含有resource的common lib打包成jar的方式。你需要从TrineaAndroidCommon@Github(可点击)或TrineaAndroidCommon@Google Code(可点击)拉下TrineaAndroidCommon工程做为android lib引入,不明白的可以参考如何添加TrineaAndroidCommon(可点击)
公共库的源码可以下载么?
可以的,上面提供了github和google code的地址
我的意思是jar包里面的源码有么,例如DropDownListView.class这个类的源码有么,还有可以不做成公共库的方式调用么,我看了LZ的blog试了半天也不行,直接将java源文件打包成jar文件导入工程不行么,真的弄的很郁闷啊,求解!
不行的,因为DropDownListView有resource资源修改,打包成jar资源并不会包含进去(这也是为什么报你遇到的xml错误的原因),如果没有资源修改的话是可以打包成jar的,这点google在开发文档中有介绍,后续google会升级adt解决这个问题
不用公共库的话你需要将DropDownListView源码及其中关联到的res下面的资源文件也拷贝进去。公共库有好处的可以方便的重用
已经找到源码了,多谢lz分享的好东西
客气啦
LZ可以通过这个地址用SVN拉下代码到本地,然后直接import到eclipse,调试运行,然后那个DropDownListView就报错了,实在不知道哪里有问题,求帮助啊!
稍等,我在看
就是加载完成时removeHeaderView时的动画效果如何弄
我想让head布局设置可见性为gone的时候,让它慢慢的收回去而不是变回去,这样看起来更流畅,改如何处理啊
DropDownListView.java的onDropDownComplete函数的setSecondPositionVisible();后面添加动画
你好,在加载更多数据时假如已经没有数据了,他会把上次加载的数据加载进去,我这边可以判断下数据小于0时怎么屏蔽掉加载更多的事件,又该怎么恢复
setHasMore(false)就可以了
发现两个问题:
1.当item选项不满一屏时下拉刷新的head会出来,很不好看。
2.上拉加载会加载多页,自动加载多次。
第一个问题跟实现有关,用的是ListView.addHeaderView实现的,所以不可避免,同时不满一屏时head出现,可点击,我觉得合理啊
第二个问题今晚会发布解决。
有办法让item不满一屏时header隐藏么?
明白了,3Q。
两种方法:
一、简单方法:adapter大小等于0时直接隐藏listView,大于0时显示listVIew。
二、通过addHeaderView和removeHeaderView实现,比较麻烦
判断listView的adapter大小,当等于0时,removeHeaderView(v)删除header,不过这里参数需要header,你改下DropDownListView源码,添加
public RelativeLayout getHeaderLayout() {
return headerLayout;
}
得到header。在adapter大小大于0时addHeaderView
第一和第二个方法都是判断Adapter是否为0,如果为0就隐藏header,Adapter为0是ListView没有一条数据时,我的意思是如果ListView的item不为0但也不满一屏时怎么隐藏header。
getFirstVisiblePosition(),getLastVisiblePosition()分别得到第一个和最后一个可见的position,用他们和adapter的大小比较,可参考DropDownListView的onScroll函数
上拉加载会加载多页,自动加载多次的bug已提交,可以再拉下代码,google cod和github都已经同步
划到底部加载数据,真心不知道咋用嗯 呵呵 ,,
示例中介绍的啊,通过isOnBottomStyle开启样式,再通过setOnBottomListener设置滚动到底部的事件即可,需要在事件结束时手动调用onBottomComplete恢复状态,
貌似有bug啊哥们,设置AutoLoadOnBottom后会不断的调onBottom(),这个是不是应该放到onScrollStateChanged里?
是有这个bug
放到onScrollStateChanged里面也不行,灵敏度不够,快速滑动到底部不会调用。
已经有解决方法,明晚会提交解决方法。解决方法可见用状态标记解决onBottom多次被调用问题