下拉刷新及相关框架
来源:互联网 发布:没有好奇心 知乎 编辑:程序博客网 时间:2024/05/18 03:40
android-Ultra-Pull-to-Refresh 深入理解及使用
下拉刷新,几乎是每个 Android 应用都会需要的功能。 android-Ultra-Pull-To-Refresh (以下简称 UltraPTR )便是一个强大的 Andriod 下拉刷新框架。
主要特点:
(1).继承于 ViewGroup, Content 可以包含任何 View。
(2).简洁完善的 Header 抽象,方便进行拓展,构建符合需求的头部。
项目地址:
https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh
竞品:
https://github.com/chrisbanes/Android-PullToRefresh
https://github.com/johannilsson/android-pulltorefresh
https://github.com/Demievil/SwipeRefreshLayout
参考文章:
android-Ultra-Pull-To-Refresh源码解析:http://a.codekk.com/detail/Android/Grumoon/android-Ultra-Pull-To-Refresh%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90
公共技术点之 View 绘制流程:http://codekk.com/open-source-project-analysis/detail/Android/lightSky/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20View%20%E7%BB%98%E5%88%B6%E6%B5%81%E7%A8%8B
公共技术点之 View 事件传递:http://codekk.com/open-source-project-analysis/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20View%20%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92
Android事件分发机制完全解析,带你从源码的角度彻底理解(上、下):http://blog.csdn.net/guolin_blog/article/details/9097463
1.添加依赖
compile 'in.srain.cube:ultra-ptr:1.0.10'
2.XML界面配置
详细官方中文文档:https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/README-cn.md
<?xml version="1.0" encoding="utf-8"?><in.srain.cube.views.ptr.PtrClassicFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cube_ptr="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#f1f1f1" cube_ptr:ptr_duration_to_close="200" cube_ptr:ptr_duration_to_close_header="1000" cube_ptr:ptr_keep_header_when_refresh="true" cube_ptr:ptr_pull_to_fresh="false" cube_ptr:ptr_ratio_of_header_height_to_refresh="1.2" cube_ptr:ptr_resistance="1.7"> <WebView android:layout_width="match_parent" android:layout_height="match_parent" /></in.srain.cube.views.ptr.PtrClassicFrameLayout>
自定义属性实现
//1.在attr里定义属性<declare-styleable name="PtrFrameLayout"?//2.在构造方法里获取TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.PtrFrameLayout);arr.recycle();
如何获取header及content
1.重写view的onFinishInflate方法
2.判断childCount
childCount > 2 throw excetpion
childCount = 2 header+content
childCount = 1 content
childCount = 0 errorView
3.mHeaderView.bringToFront(),不懂什么意思
3.Java代码配置
// the following are default settings//1.设置阻尼系数mPtrFrame.setResistance(1.7f);//2.下拉刷新头部的比率mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f);//3.设置从松手的位置到头部所要的时间mPtrFrame.setDurationToClose(200);//4.设置从头部到顶部所要的时间mPtrFrame.setDurationToCloseHeader(1000);//5.default is false,false下拉就进行刷新,true松手到超过指定高度才进行刷新mPtrFrame.setPullToRefresh(false);//6.default is true,true刷新时有消息头,flase刷新时没有消息头mPtrFrame.setKeepHeaderWhenRefresh(true);
1.mPtrFrame.setResistance(1.7f);
//setp 1.在PtrIndicator.class里面设置resistancemPtrIndicator.setResistance(arr.getFloat(R.styleable.PtrFrameLayout_ptr_resistance, mPtrIndicator.getResistance()));private float mResistance = 1.7f;//setp 2.在MotionEvent.ACTION_MOVE中计算,新的offsetY。(即offsetY / mResistance)mPtrIndicator.onMove(e.getX(), e.getY());public final void onMove(float x, float y) { float offsetX = x - mPtLastMove.x; float offsetY = (y - mPtLastMove.y); processOnMove(x, y, offsetX, offsetY); mPtLastMove.set(x, y);}protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) { setOffset(offsetX, offsetY / mResistance);}protected void setOffset(float x, float y) { mOffsetX = x; mOffsetY = y;}private PointF mPtLastMove = new PointF();//setp 3.在MotionEvent.ACTION_MOVE的时候使用新的offsetYmovePos(offsetY);mContent.offsetTopAndBottom(change);
2.mPtrFrame.setRatioOfHeaderHeightToRefresh(1.2f);
//setp 1.在attr里设置header高度的比率float ratio = mPtrIndicator.getRatioOfHeaderToHeightRefresh();ratio = arr.getFloat(R.styleable.PtrFrameLayout_ptr_ratio_of_header_height_to_refresh, ratio);mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio);//setp 1'.在java代码里设置header高度的比率 public void setRatioOfHeaderHeightToRefresh(float ratio) { mPtrIndicator.setRatioOfHeaderHeightToRefresh(ratio);}//以上两种方式都会调用PtrIndicator的方法public void setRatioOfHeaderHeightToRefresh(float ratio) { mRatioOfHeaderHeightToRefresh = ratio; mOffsetToRefresh = (int) (mHeaderHeight * ratio);}//setp 2.在onMeasure的时候设置header的高度及栈值mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;mPtrIndicator.setHeaderHeight(mHeaderHeight);public void setHeaderHeight(int height) { mHeaderHeight = height; updateHeight();}protected void updateHeight() { mOffsetToRefresh = (int) (mRatioOfHeaderHeightToRefresh * mHeaderHeight);}public int getOffsetToRefresh() { return mOffsetToRefresh;}//setp 3.在MotionEvent.ACTION_UP,使用这个栈值//3.1onRelease(false);//3.2tryToPerformRefresh();//3.3mPtrIndicator.isOverOffsetToRefresh()public boolean isOverOffsetToRefresh() { return mCurrentPos >= getOffsetToRefresh();}public int getOffsetToRefresh() { return mOffsetToRefresh;}//疑问:setRatioOfHeaderHeightToRefresh与onMeasure的调用顺序
3.mPtrFrame.setDurationToClose(200);
//step 1.设置方式省略//step 2.在MotionEvent.ACTION_UP中使用这个值private void onRelease(boolean stayForLoading)//step 3.要满足正在加载中&当刷新的时候保持头部&当前的pos要大于offsetToKeepHeader.//这三个条件都要满足,才会执行回滚头部。(也就是从当前位置回滚到offsetToKeepHeader)if (mStatus == PTR_STATUS_LOADING) { // keep header for fresh if (mKeepHeaderWhenRefresh) { // scroll header back if (mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && !stayForLoading) { mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToKeepHeaderWhileLoading(), mDurationToClose); } }}//step 4.那什么是offsetToKeepHeader?可以看到它的默认值为mHeaderHeight,也有公开的方法可以设置,///但是,不知道应该在什么时候设置private int mOffsetToKeepHeaderWhileLoading = -1;public void setOffsetToKeepHeaderWhileLoading(int offset) { mOffsetToKeepHeaderWhileLoading = offset;}public int getOffsetToKeepHeaderWhileLoading() { return mOffsetToKeepHeaderWhileLoading >= 0 ? mOffsetToKeepHeaderWhileLoading : mHeaderHeight;}
4.mPtrFrame.setDurationToCloseHeader(1000);
//step 1.当主动调用刷新完成的private void performRefreshComplete() { mStatus = PTR_STATUS_COMPLETE; notifyUIRefreshComplete(false);}//step 2.通知UI刷新完成private void notifyUIRefreshComplete(boolean ignoreHook) { tryScrollBackToTopAfterComplete(); tryToNotifyReset();}private void tryScrollBackToTopAfterComplete() { tryScrollBackToTop();}//step 3.回滚到起点(即顶部)private void tryScrollBackToTop() { if (!mPtrIndicator.isUnderTouch()) { mScrollChecker.tryToScrollTo(PtrIndicator.POS_START, mDurationToCloseHeader); }}//问题:mDurationToClose和mDurationToCloseHeader可能会有冲突?//当网络请求的响应的时候.即小于mDurationToClose的时间。在UI就会有卡顿的问题//根本原因:当执行mDurationToClose一半的时候,在调用refreshComplete()的,就会有一个回退的效果,然//后在执行mDurationToCloseHeader
5.mPtrFrame.setPullToRefresh(false);
//step 1.设置方法略//step 2.在下拉的时候,调用movePos(即MotionEvent.ACTION_MOVE)private void movePos(float deltaY) { updatePos(change);}private void updatePos(int change) {// Pull to Refresh if (mStatus == PTR_STATUS_PREPARE) { // reach fresh height while moving from top to bottom if (isUnderTouch && !isAutoRefresh() && mPullToRefresh && mPtrIndicator.crossRefreshLineFromTopToBottom()) { tryToPerformRefresh(); } // reach header height while auto refresh if (performAutoRefreshButLater() && mPtrIndicator.hasJustReachedHeaderHeightFromTopToBottom()) { tryToPerformRefresh(); } }}//step 3.执行下拉刷新(就是没有滑动超过指定高度的时候,就执行下拉刷新。相当于一个提前执行网络请求的一个效果) private boolean tryToPerformRefresh() { performRefresh();} private void performRefresh() { if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIRefreshBegin(this); } if (mPtrHandler != null) { mPtrHandler.onRefreshBegin(this); }}
6.mPtrFrame.setKeepHeaderWhenRefresh(true);
//step 1.设置省略(xml,java两种方式)//step 2.在MotionEvent.ACTION_UP时调用onRelease(false)private void onRelease(boolean stayForLoading) { tryToPerformRefresh(); if (mStatus == PTR_STATUS_LOADING) { // keep header for fresh if (mKeepHeaderWhenRefresh) { // scroll header back if (mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && !stayForLoading) { mScrollChecker.tryToScrollTo(mPtrIndicator.getOffsetToKeepHeaderWhileLoading(), mDurationToClose); } else { // do nothing } } else { tryScrollBackToTopWhileLoading(); } } else { if (mStatus == PTR_STATUS_COMPLETE) { notifyUIRefreshComplete(false); } else { tryScrollBackToTopAbortRefresh(); } }}//step 3.与mDurationToClose相比,不再是回滚到mHeaderHeight(默认值),而是直接回滚到顶点(0)。//这样做达到了。回滚的时候隐藏header的目的
4.设置下拉刷新监听
注意:如果有下拉问题,可以选择性重写checkCanDoRefresh
mPtrFrame.setPtrHandler(new PtrHandler() { //检查能否下刷新 @Override public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { return PtrDefaultHandler.checkContentCanBePulledDown(frame, mWebView, header); } //开始下拉刷新 @Override public void onRefreshBegin(PtrFrameLayout frame) { updateData(); }});
需要自己实现checkContentCanBePulledDown
public static boolean canChildScrollUp(View view) { if (android.os.Build.VERSION.SDK_INT < 14) { if (view instanceof AbsListView) { final AbsListView absListView = (AbsListView) view; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { return view.getScrollY() > 0; } } else { return view.canScrollVertically(-1); }}
5.手动回调完成下拉刷新
mPtrFrame.refreshComplete();
6.自定义下拉刷新的头部
5.1 自定义view,并实现PtrUIHandler接口
public interface PtrUIHandler { /** * When the content view has reached top and refresh has been completed, view will be reset. * * @param frame */ public void onUIReset(PtrFrameLayout frame); /** * prepare for loading * * @param frame */ public void onUIRefreshPrepare(PtrFrameLayout frame); /** * perform refreshing UI */ public void onUIRefreshBegin(PtrFrameLayout frame); /** * perform UI after refresh */ public void onUIRefreshComplete(PtrFrameLayout frame); public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator);}
5.2 用过xml布局或者setHeaderView,设置头部
5.3 设置PtrUIHandler
ptrHome.addPtrUIHandler(ptrUIHandler);
7.自动下拉刷新
ptrFrame.postDelayed(new Runnable() { @Override public void run() { ptrFrame.autoRefresh(true); }}, 150);
8.其他设置
1. setPinContent(false)
解释:设置ContentView是否顶住.(设置为true即content不动,只有header随着移动)
//step 1.在MotionEvent.ACTION_MOVE中调用movePos(offsetY)private void movePos(float deltaY) { updatePos(change);}private void updatePos(int change) { mHeaderView.offsetTopAndBottom(change); if (!isPinContent()) { mContent.offsetTopAndBottom(change); }}
2.setLoadingMinTime(500)
解释:设置网络加载时间最少为500毫秒
//step 1.在MotionEvent.ACTION_UP的时候调用onRelease(false)//step 2.执行下拉刷新,并保存mLoadingStartTime private void onRelease(boolean stayForLoading) { tryToPerformRefresh();}private boolean tryToPerformRefresh() { if (mStatus != PTR_STATUS_PREPARE) { return false; } // if ((mPtrIndicator.isOverOffsetToKeepHeaderWhileLoading() && isAutoRefresh()) || mPtrIndicator.isOverOffsetToRefresh()) { mStatus = PTR_STATUS_LOADING; performRefresh(); } return false;}private void performRefresh() { mLoadingStartTime = System.currentTimeMillis(); if (mPtrUIHandlerHolder.hasHandler()) { mPtrUIHandlerHolder.onUIRefreshBegin(this); if (DEBUG) { PtrCLog.i(LOG_TAG, "PtrUIHandler: onUIRefreshBegin"); } } if (mPtrHandler != null) { mPtrHandler.onRefreshBegin(this); }}//step 3.在调用refreshComplete()时计算delay。并且postDelayedfinal public void refreshComplete() { int delay = (int) (mLoadingMinTime - (System.currentTimeMillis() - mLoadingStartTime)); if (delay <= 0) { performRefreshComplete(); } else { postDelayed(new Runnable() { @Override public void run() { performRefreshComplete(); } }, delay); }}//可处理mDurationToClose和mDurationToCloseHeader的冲突的问题
3.disableWhenHorizontalMove(false)
解释:为了解决与viewpager的手势冲突问题(即在x轴滑动的距离大于在y轴滑动的距离,则直接返回,UltraPtr不做处理)
//step 1.在MotionEvent.ACTION_MOVEif (mDisableWhenHorizontalMove && !mPreventForHorizontal && (Math.abs(offsetX) > mPagingTouchSlop && Math.abs(offsetX) > Math.abs(offsetY))) { if (mPtrIndicator.isInStartPosition()) { mPreventForHorizontal = true; }}if (mPreventForHorizontal) { return dispatchTouchEventSupper(e);}
9.其他问题
1.UltraPtr如何实现滑动动画
解答:通过Scroller与ScrollChecker。
1.Scroller为滚动的封装类,通过偏移来描述从一个点到移动另一个点,坐标的变化情况。(mScroller.startScroll(startX, startY, dx, dy, duration)
2.ScrollChecker实现Runnable接口的线程类,但是其run放在主线程执行。根据其变更的坐标,去设置header
和content的offsetTopAndBottom(int offset),最后在刷新界面
3.使用view.post(action),不断执行ScrollChecker的run方法。这样到header和content位置不断变化,然后产生动画
4.最后mScroller.computeScrollOffset()或者mScroller.isFinished()来判断是否到了指定的间隔
2.如何测量header和content?
3.为什么UltraPtr没有实现加载更多
对比 Android-PullToRefresh 项目,UltraPTR 没有实现 加载更多 的功能,但我认为 下拉刷新 和 加载更多 不是同一层次的功能, 下拉刷新 有更广泛的需求,可以适用于任何页面。而 加载更多 的功能应该交由具体的 Content 自己去实现。这应该是和 Google 官方推出 SwipeRefreshLayout 是相同的设计思路,但对比 SwipeRefreshLayout, UltraPTR 更灵活,更容易拓展。
4.事件处理流程是怎么样
5.类的关系图是怎样的
9.PtrIndicator
字段
- mIsUnderTouch-在ACTION_DOWN为true,ACTION_UP为false
- mPtLastMove-在ACTION_DOWN和ACTION_MOVE都要更新这个坐标
- mOffsetX,mOffsetY-偏移量,当前坐标减去mPtLastMove
- mOffsetToRefresh-(int) (mHeaderHeight * ratio)
- mCurrentPos-ACTION_MOVE的时候更新这个坐标
- mLastPos-ACTION_MOVE保存上一个mCurrentPos坐标
- mPressedPos-ACTION_DOWN保存mCurrentPos坐标
- mRefreshCompleteY-刷新完成的时候保存mCurrentPos坐标
方法
//滑动的位置与Header的高度的百分比public float getCurrentPercent()//在顶部上面public boolean willOverTop(int to)//在顶部public boolean isAlreadyHere(int to)//顶部有空余public boolean hasLeftStartPosition()//在开始点public boolean isInStartPosition()//‘刚刚’离开开始点public boolean hasJustLeftStartPosition() //‘刚刚’回到开始点public boolean hasJustBackToStartPosition()//超过设定的刷新高度public boolean hasMovedAfterPressedDown()public boolean goDownCrossFinishPosition()public boolean crossRefreshLineFromTopToBottom()public boolean hasJustReachedHeaderHeightFromTopToBottom()
可选
public void setOffsetToKeepHeaderWhileLoading(int offset) { mOffsetToKeepHeaderWhileLoading = offset;}public int getOffsetToKeepHeaderWhileLoading() { return mOffsetToKeepHeaderWhileLoading >= 0 ? mOffsetToKeepHeaderWhileLoading : mHeaderHeight;}public boolean isOverOffsetToKeepHeaderWhileLoading() { return mCurrentPos > getOffsetToKeepHeaderWhileLoading();}
- 下拉刷新及相关框架
- android下拉刷新框架
- 下拉刷新框架
- PullToRefresh下拉刷新框架
- 下拉刷新框架
- 下拉刷新及注意事项
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- droid 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- Android 下拉刷新框架实现
- kepware 发生 Setup was unable to initialize your PC错误解决办法
- Science上聚类算法论文——Clustering by fast search and find of density peaks翻译稿
- Win7 图标异常
- 验证码的实现
- Java大数类用法
- 下拉刷新及相关框架
- 《leetCode》:ZigZag Conversion
- Cocos适配带虚拟导航栏手机(HUAWEI 荣耀6 Plus)
- hdu 4280 Island Transport 最大流sap
- PHP面向对象1.2对象类型在内存中的分配
- 设计模式之建造者模式
- matlab使用Matlab进行特征选择
- kvm随笔系列四:AMD SVM
- sql--索引创建