下拉刷新ScrollView
2015-10-07 14:42
211 查看
需求
昨天在网上找了好久都没有找到一个合适的可以下拉刷新的scrollView代码,不过github上有一个开源项目(Android-PullToRefresh)写得很不错,但好像必须是AbsListView的子类才能使用,当然自己写的这个和该项目的实现原理是一样的。想想自己学Android已经快一年了,总得努力努力自己搞搞,所以今天上午就查阅资料看博客开始写,下面就贴贴自己的思路和代码和下载地址。如果喜欢请star,如果觉得有纰漏或是有更好的点子请及时评论。实现
咋们还是按思路一步一步往下走..1.需要编写头部文件和布局,这个人们都知道,继承RelativeLayout,然后提供三个设置状态的方法,这里就不粘代码了,有需要的可以下载我的源码,下面开始干重要活…
2.extends ScrollView,将头部View,加到容器布局,设置头部的magrin值将头部headView隐藏
public class RefreshScrollView extends ScrollView { private Context mContext; // 容器布局,因为scroll只允许嵌套一个子布局 private LinearLayout mScrollContainer = null; // 头部刷新的View private ScrollViewHeader mRefreshHeaderView; // 头部的高度 private int mHeaderViewHeight; private final static float OFFSET_RADIO = 2.2f; // support iOS like pull public CopyOfRefreshScrollView(Context context) { this(context, null); } public CopyOfRefreshScrollView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CopyOfRefreshScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; initView(); } /** * 初始化view */ private void initView() { // 添加头部布局到容器 mRefreshHeaderView = new ScrollViewHeader(mContext); LinearLayout.LayoutParams headerViewParams = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); mScrollContainer = new LinearLayout(mContext); mScrollContainer.addView(mRefreshHeaderView, headerViewParams); mScrollContainer.setOrientation(LinearLayout.VERTICAL); addView(mScrollContainer); // 必须要测量一下头部view,要不然getMeasuredHeight()是0, // 当然mRefreshHeaderView.getViewTreeObserver().addOnGlobalLayoutListener也可以 measureView(mRefreshHeaderView); // 获取头部刷新headView的高度,设置margin让头部隐藏 mHeaderViewHeight = mRefreshHeaderView.getMeasuredHeight(); mRefreshHeaderView .updateMargin(-mRefreshHeaderView.getMeasuredHeight()); } /** * 通知父布局,占用的宽,高 * * @param view */ private void measureView(View view) { ViewGroup.LayoutParams p = view.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } int width = ViewGroup.getChildMeasureSpec(0, 0, p.width); int height; int tempHeight = p.height; if (tempHeight > 0) { height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); } else { height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } view.measure(width, height); }
3.这个时候我们可以新建一个activity试试看效果了,但一运行起来果断报异常了:java.lang.IllegalStateException:ScrollView can host only one direct child,这个没办法了总得解决一下吧。那么就新建一个方法setContainerView(View child) 吧?那如果非要在布局文件里面添加呢怎么办呢?所以只能够阅读源码了,发现scrollView在加载仅有的一个childView的时候会调用addView(View child, android.view.ViewGroup.LayoutParams params)方法,这下好了只要override这个方法就可以了,再次运行爽了。
/** * 其他的addView 的方法也要override,暂且放一边 */ @Override public void addView(View child, android.view.ViewGroup.LayoutParams params) { // 2.重载addView(View child, android.view.ViewGroup.LayoutParams params)方法 // 解决 java.lang.IllegalStateException // 因为scrollView只许添加一个子布局,如果在xml中添加子布局,那么肯定会throw // java.lang.IllegalStateException:ScrollView can host only one direct child this.removeAllViews(); mScrollContainer.addView(child, params); super.addView(mScrollContainer, mScrollContainer.getLayoutParams()); }
4.处理最重要的方法onTouchEvent的触摸事件
// 4.处理触摸事件,override onTouchEvent @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(deltaY < 0 && mRefreshing){ // 如果往上滑并且是刷新的状态就不除阻力,要不然当头部出来的时候向上滑动怪怪的 updateHeader(deltaY); }else if (getScrollY() == 0 && (deltaY > 0 || mRefreshHeaderView.getTopMargin() > -mHeaderViewHeight)) { // 更新headerView的高度,同时更改状态 updateHeader(deltaY/OFFSET_RADIO); return true; } break; default: //这里没有使用action_up的原因是,可能会受到viewpager的影响接收到action_cacel事件 if (getScrollY() == 0) { if (mRefreshHeaderView.getTopMargin() > 0 && mEnableRefresh && !mRefreshing) { mRefreshing = true; mRefreshHeaderView.setState(ScrollViewHeader.STATE_REFRESHING); // 刷新加载中,给调用者监听 if(mListener!=null){ mListener.onRefresh(); } } //重置RefreshHeaderView的高度 resetHeaderView(); } break; } return super.onTouchEvent(ev); } /** * 更新headerview的高度,同时更改状态 * @param deltY */ public void updateHeader(float deltY) { int currentMargin = (int) (mRefreshHeaderView.getTopMargin() + deltY); mRefreshHeaderView.updateMargin(currentMargin); if(mEnableRefresh && !mRefreshing) { if (currentMargin > mHeaderViewHeight/5) { // 头部全部出来了,就显示松开刷新 mRefreshHeaderView.setState(ScrollViewHeader.STATE_READY); } else { // 否则显示下拉加载更多 mRefreshHeaderView.setState(ScrollViewHeader.STATE_NORMAL); } } } /** * 重置RefreshHeaderView的高度 */ public void resetHeaderView() { int margin = mRefreshHeaderView.getTopMargin(); if(margin == -mHeaderViewHeight) { return ; } if(margin < 0 && mRefreshing) { // 当前已经在刷新,又重新进行拖动,但未拖满,不进行操作 return ; } int finalMargin = 0; if(margin <= 0 && !mRefreshing) { finalMargin = mHeaderViewHeight; } // 松开刷新,或者下拉刷新,又松手,没有触发刷新 // mAssistScroller辅助滚动 ,得要Override computeScroll() // 如果头部的状态已经是隐藏的就没必要再去滚动了 if(this.getScrollY()<mHeaderViewHeight){ mAssistScroller.startScroll(0, -margin, 0, finalMargin + margin, SCROLL_DURATION); invalidate(); } } @Override public void computeScroll() { if(mAssistScroller.computeScrollOffset()) { mRefreshHeaderView.updateMargin(-mAssistScroller.getCurrY()); //继续重绘 postInvalidate(); } super.computeScroll(); }
5.到第四步为止就基本大功告成了,最后就差几个提供给调用者的公共方法和刷新的监听。
/** * 设置刷新的监听 * @param listener */ public void setOnRefreshScrollViewListener(OnRefreshScrollViewListener listener){ this.mListener = listener; } public interface OnRefreshScrollViewListener { public void onRefresh(); } /** * 加载完成 */ public void onLoadComplete(){ if(mRefreshing) { mRefreshing = false; resetHeaderView(); } } /** * 设置scroll是否可以刷新 * * @param enableRefresh */ public void setEnableRefresh(boolean enableRefresh) { this.mEnableRefresh = enableRefresh; }
源码地址:http://pan.baidu.com/s/1bnhqHI3
相关文章推荐
- JSON--List集合转换成JSON对象
- 有上下界的网络流问题
- zigbee学习之DHT11
- adb Logcat用法
- Eclipse 中的多线程编译——如何成倍提升Gcc编译器的编译速度
- 一步一步创建一个Servlet站点
- CF_#324(Div.2)
- 边缘检测之Canny算子
- 简单工厂+单例模式
- 关于ActionBar
- HTML5 canvas画图
- zigbee学习之OLED
- linux之Makefile(上)
- 计算圆点坐标
- 《Effective C++》读书摘要
- 质量值体系 Phred33 和 Phred 64 的由来 及其在质量控制中的实际影响
- 第四章:javascript: 栈
- Android实战技巧:深入解析AsyncTask
- django-pagination分页
- 条款21:必须返回对象的时候,不要妄想使其返回reference