【Android】下拉刷新实现
关于这方面的文章百度下有很多,我就只写写我自己实现过程。
我觉得学习一门语言不是做了几个项目就可以认为自己会了,这只是暂时的,若没有笔记,时间长了,你是怎么解决某些问题,估计连你自己都忘了,又得费时费力的重新研究~ 这就是我为什么写这篇文章的原因。
现在公司做Android是用AndroidStudio来做的,AndroidStudio开发是需要搞gradle 的,有点麻烦,我就直接上Eclipse上做些测试例子~
进入正文:
下拉的刷新的实现主要是基于ListView这个控件扩展来的。ListView的数据需要由“适配器”来提供,这个跟之前的web开发有些差异。以前做集合控件开发时,将一些有规律的数据直接与控件绑定就可以显示了。但Android却不行,为什么呢?因为ListView是个多功能控件,它能将不同数据表现实现都在一个集合中展示。那不同的形式要在一个容器中展示就涉及到“适配”的问题。
(什么的适配?我的理解就是匹配,插口转换之类的)
原来ListView要呈现不同子视图,所以就需要一个适配器来做“兼容”~
下拉ListView时它上面会有“刷新”等图标及文字,这是怎么做到的? ListView类中有个addHeaderView(View v) 方法,我们将 【放好“刷新”等图标及文字】的一个子视图,通过这个方法将这个子视图放到ListView中,就可以从“逻辑上”了解了,上面那块“刷新”的东东是怎么来的。
| 新增的Head视图(Layout) |
| 原ListView |
有了这个组合,剩下的就是滑动相关了~
由于我们是要实现“下拉刷新”效果的,且这个效果是基于ListView控件的,所以我们需要自定义一个ListView控件,我们叫它mListView吧~
在继续下面文章之前,我提一个代码使用技巧,高手略过~
Begin---------------------------------------------------------------------------------------------------------------------------
如图:

我们把相关事件定义在辅类时,实现却在主类中来实现,怎么来实现呢?
在辅类中定义一个接口如IInterface1
辅类部分代码Code-begin -------------------------------------
IInterface1 iobj;
public void setIInterface1(IInterface1 l)
{
iobj=l;
}
然后,在相应事件中直接使用iobj.doSomeThing() 把位置占住~
辅类部分代码Code-end -------------------------------------
主类Activity implements IInterface1 (主类Activity实现 辅类中定义的接口及方法)
public override doSomeThing(){......}
KEY:主类中调用辅类的 setIInterface1 方法把自己放进去 如:setIInterface1(this);
小节下,辅类创建一个接口定义些抽象方法,在相关事件把位置占好,并提供外部set接口实例的方法,主类实现接口把自己做为接口的实现者传入辅类中,成功替换之前的占位抽象方法~
End---------------------------------------------------------------------------------------------------------------------------
以上逻辑将用于下拉触点时,调用相关刷新数据(加载数据)
自定义的mListView 需要 实现(implements)OnScrollListener 接口,
申明一个Scroller 实例,如下:
mScroller=new Scroller(context,new DecelerateInterpolator());
重写 onTouchEvent 方法
根据不同事件保存与调用滑动方法
mScroller.startScroll(0, height, 0, finalHeight-height,SCROLL_DURATION);
然后就是调用方法 invalidate() 重绘界面
仅仅调用Scroller的startScroll方法是不能实现滚动的~ 还有个关键方法 computeScroll ,你可能需要重写这个方法
@Override
public void computeScroll() { if(mScroller.computeScrollOffset())
{
mHeaderView.setVisiableHeight(mScroller.getCurrY()); postInvalidate();
invokeOnScrolling();
}
super.computeScroll();
}
每次滑动时都会调用这个computerScroll()方法,
mScroller.computeScrollOffset() ///// 是否有偏移
postInvalidate() ///// 刷新界面与 Invalidate 有点细微差别~
以上就是我的小节~
直接贴代码吧~ 如下:
package com.example.androidtest1.widget; import android.content.Context;
import android.text.format.Time;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.Scroller; import com.example.androidtest1.R; public class XListView extends ListView implements OnScrollListener { private float mLastY = -1; // save event y
private Scroller mScroller;
private OnScrollListener mScrollListener; ///// 滚动侦听 private IXListViewListener mListViewListener; ///// 触发刷新接口 private Context m_context;
private XListViewHeader mHeaderView;
private RelativeLayout mHeaderViewContent;
private int mHeaderViewHeight;
private boolean mEnablePullRefresh=true; //// 是否允许下拉刷新
private boolean mPullRefreshing=false; //// 下拉刷新中? ////// list 中的数量
private int mTotalItemCount; private int mScrollBack;
private final static int SCROLLBACK_HEADER=0;
private final static int SCROLLBACK_FOOTER=1; private final static int SCROLL_DURATION=400;
private final static int PULL_LOAD_MORE_DELTA=50; private final static float OFFSET_RADIO=1.8f; public XListView(Context context) {
super(context);
initWidthContext(context);
} public XListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initWidthContext(context);
} public XListView(Context context, AttributeSet attrs) {
super(context, attrs);
initWidthContext(context);
} private void initWidthContext(Context context)
{
mScroller=new Scroller(context,new DecelerateInterpolator());
super.setOnScrollListener(this); m_context=context;
mHeaderView=new XListViewHeader(context);
mHeaderViewContent=(RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);
addHeaderView(mHeaderView); mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener(){
@Override
public void onGlobalLayout() {
mHeaderViewHeight=mHeaderViewContent.getHeight();
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
} /**
* 准备刷新中~
*/
public void pullRefreshing(){
if(!mEnablePullRefresh){
return;
}
mHeaderView.setVisiableHeight(mHeaderViewHeight);
mPullRefreshing=true;
mHeaderView.setState(XListViewHeader.STATE_READY);
} /*
* 设置能否下拉刷新
*/
public void setPullRefreshEnable(boolean enable){
mEnablePullRefresh=enable;
if(!mEnablePullRefresh){
mHeaderViewContent.setVisibility(View.INVISIBLE);
}else{
mHeaderViewContent.setVisibility(View.VISIBLE);
}
} /**
* 停止刷新
*/
public void stopRefresh(){
Time time=new Time();
time.setToNow();
mHeaderView.setRefreshTime(time.format("%Y-%m-%d %T"));
if(mPullRefreshing==true){
mPullRefreshing=false;
resetHeaderHeight();
}
} /**
* 未处于刷新状态,更新箭头
* @param delta
*/
private void updateHeaderHeight(float delta)
{
mHeaderView.setVisiableHeight((int)delta+mHeaderView.getVisiableHeight());
if(mEnablePullRefresh && !mPullRefreshing){
if(mHeaderView.getVisiableHeight()>mHeaderViewHeight){
mHeaderView.setState(XListViewHeader.STATE_READY);
}else{
mHeaderView.setState(XListViewHeader.STATE_NORMAL);
}
}
setSelection(0);
} /**
* 重置自定义头部高度
*/
private void resetHeaderHeight()
{
int height=mHeaderView.getVisiableHeight();
if(height==0)
return; /*
* 下拉刷新的高度不够~
* */
if(mPullRefreshing && height<=mHeaderViewHeight)
{
mListViewListener.reShowMoreView();
}
int finalHeight=0;
if(mPullRefreshing && height>mHeaderViewHeight){
finalHeight=mHeaderViewHeight;
} mScrollBack=SCROLLBACK_HEADER; ////// 设置滚动
mScroller.startScroll(0, height, 0, finalHeight-height,SCROLL_DURATION); ////// 触发computeScroll
invalidate();
} private void invokeOnScrolling(){
if(mScrollListener instanceof OnXScrollListener){
OnXScrollListener l=(OnXScrollListener)mScrollListener;
l.onXScrolling(this);
}
} @Override
public boolean onTouchEvent(MotionEvent ev) { if(mLastY==-1)
mLastY=ev.getRawY(); switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:
mLastY=ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
final float deltaY=ev.getRawY()-mLastY;
mLastY=ev.getRawY();
if(getFirstVisiblePosition()==0
&& (mHeaderView.getVisiableHeight()>0 || deltaY>0))
{
updateHeaderHeight(deltaY/OFFSET_RADIO);
invokeOnScrolling();
}
break;
default:
mLastY=-1;
if(getFirstVisiblePosition()==0){
if(mHeaderView.getVisiableHeight()>0)
mPullRefreshing=true; if(mEnablePullRefresh && mHeaderView.getVisiableHeight()>mHeaderViewHeight){
mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
if(mListViewListener!=null){
mListViewListener.onRefresh();
}
}
resetHeaderHeight();
}
break;
}
return super.onTouchEvent(ev);
} @Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
mTotalItemCount=totalItemCount;
if(mScrollListener!=null)
{
mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
} @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(mScrollListener!=null)
{
mScrollListener.onScrollStateChanged(view, scrollState);
}
} @Override
public void computeScroll() { if(mScroller.computeScrollOffset())
{
mHeaderView.setVisiableHeight(mScroller.getCurrY()); postInvalidate();
invokeOnScrolling();
}
super.computeScroll();
} @Override
public void setOnScrollListener(OnScrollListener l) {
mScrollListener = l;
} /**
*
* @param l
*/
public void setXListViewListener(IXListViewListener l){
mListViewListener=l;
} public interface OnXScrollListener extends OnScrollListener{
public void onXScrolling(View view);
} public interface IXListViewListener{
public void onRefresh();
public void onLoadMore();
public void hiddenShowMoreView();
public void reShowMoreView();
}
}
【Android】下拉刷新实现的更多相关文章
- 【原创】窥视懒人的秘密---android下拉刷新开启手势的新纪元
小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...
- Android 下拉刷新上拉载入 多种应用场景 超级大放送(上)
转载请标明原文地址:http://blog.csdn.net/yalinfendou/article/details/47707017 关于Android下拉刷新上拉载入,网上的Demo太多太多了,这 ...
- [Android]下拉刷新控件RefreshableView的实现
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4172483.html 需求:自定义一个ViewGroup,实现 ...
- [转]Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能
版权声明:本文出自郭霖的博客,转载必须注明出处. 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最近项目中需要用到L ...
- Android下拉刷新效果实现
本文主要包括以下内容 自定义实现pulltorefreshView 使用google官方SwipeRefreshLayout 下拉刷新大致原理 判断当前是否在最上面而且是向下滑的,如果是的话,则加载数 ...
- Android 下拉刷新
以前旧版用的是开源的PullToRefresh第三方库,该库现在已经不再维护了: chrisbanes/Android-PullToRefreshhttps://github.com/chrisban ...
- Android 下拉刷新框架实现
原文地址:http://blog.csdn.net/leehong2005/article/details/12567757 前段时间项目中用到了下拉刷新功能,之前在网上也找到过类似的demo,但这些 ...
- Android下拉刷新底部操作栏的隐藏问题
最近自己编写下拉刷新的时候,发现了一个问题,就是有一个需求是这样的:要求页面中是一个Tab切换界面,一个界面有底部操作栏,不可下拉刷新,另一个界面没有底部操作栏,但可以下拉刷新. 按照平常的做法,我在 ...
- Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能 (转)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9255575 最 近项目中需要用到ListView下拉刷新的功能,一开始想图省事,在 ...
- Android下拉刷新-SwipeRefreshLayout,RecyclerView完全解析之下拉刷新与上拉加载SwipeRefreshLayout)
SwipeRefrshLayout是Google官方更新的一个Widget,可以实现下拉刷新的效果.该控件集成自ViewGroup在support-v4兼容包下,不过我们需要升级supportlibr ...
随机推荐
- March 20 2017 Week 12 Monday
A goal is a dream with a deadline. 目标就是给梦想一个期限. Dream without dealine is just daydream, because you ...
- 作为软件技术人员建立自己的git账户并保存资料的重要性
日常生活中,当修改并保存了一个文件,所得到的就是此文件的最新版本,假若今后因某一问题需要用到原来文件,可是很多情况下,这种修改是不可逆的.你修改完之后,无法回到你修改前的样子.为了避免这种情况,有的人 ...
- 从产品展示页面谈谈Hybris系列之二: DTO, Converter和Populator
文章作者:张健(Zhang Jonathan) 上一篇文章 从产品展示页面谈谈Hybris的特有概念和设计结构 我们讲解了Hybris一些特有的概念以及大体架构,并且介绍了Facade层里是如何定义D ...
- Arcgis Javascript中geometryEngine报错’hq‘of undefined的解决方法
这个问题困扰了我一个星期,原因是使用geomagicbuffer时候,有的线可正常使用,有的就直接报错,一直没有解决,后来发现是api自己的bug导致的 干脆直接读代码,在geometryEngine ...
- ArcGIS10.1之crossdomain文件
大家都知道在10.1之前的版本在开发的时候需要使用跨域部署文件crossdomain.xml文件,在10.1中该文件不需要单独拷贝到IIS根目录或者是java版本的weboutput目录,在serve ...
- bootstrap table 分页只显示分页不显示总页数等数据
搜了下没找到解决方案,就用CSS来解决了. 把paginationDetailHAlign:"right",使pagination-detail的class为.pull-right ...
- 【[CTSC2000]冰原探险】
noip前练一下码力还是非常有前途的 这道题本来就是想写个大暴力弃疗的,所以直接强上暴力浑身舒爽 结果发现要不是判重的时候脑残了,就能\(A\)了 没什么好说的呀,就是每一次都暴力\(O(n)\)往上 ...
- 2018.11.29 Struts2中拦截器的学习&项目的实际运用
struts2官方架构 拦截器的创建 第一种方式 第二种方式 看源码AbstractInterceptor 底层已经帮我们写过这些方法了 第三种方式(推荐) 拦截器API学习 放行 前后处理 不放行, ...
- maven学习记录二——依赖管理
5 依赖管理 Jar包的管理 需求:整合struts2 页面上传一个客户id 跳转页面 5.1 添加依赖: 打开maven仓库的视图: 5.2 重建索引 1. 创建m ...
- 【洛谷P4342】[IOI1998]Polygon
Polygon 比较裸的环形DP(也可以说是区间DP) 将环拆成链,复制到后面,做区间DP即可 #include<iostream> #include<cstdio> usin ...