以前一直想写一个自己用的下拉插件,最近自己也看了很多的下拉插件。所以总结了一下,自己写了一个下拉刷新插件。实现的这个下拉刷新的框架,并不是自己的原创,在完成过程中是参考了很多开源的框架,并把自己认为比较好的东西借鉴了过来,从而形成我的东西。
实现原理
1、LoadingLayout是实现刷新头部和尾部布局的主要文件,而CustomLayout则继承自LoadingLayout的抽象类,它定义了一些自定义方法。在LoadingLayout中声明了刷新文字、时间、图片等设置和状态的改变方法。
Headr布局文件主要是实现下拉动画实现,据下拉的距离来改变它的状态,从而显示不同的样式。
Footer布局文件是自动加载更多等状态显示已经上拉刷新状态改变,从而显示不停的样式。
【1】OnRefreshListener刷新接口监听。
/**
* 下拉松手后调用
*
* @param refreshView 刷新的view
*/
void onPullDownRefresh(final PullRefreshBase<V> refreshView);
/**
* 加载更多或上拉时调用
*
* @param refreshView 刷新的view
*/
void onPullUpToRefresh(final PullRefreshBase<V> refreshView);
【2】通过对onInterceptTouchEvent方法重写,来对Touch进行拦截来判断滑动手势的操作。同时再根据是否拦截判断是否要传递给子视图。而onTouchEvent方法则根据拦截操作的标识来判断是否消费事件,不然就传递给父视图操作。
/**
* Touch事件拦截器
*
* @param event
* @return
*/
@Override
public final boolean onInterceptTouchEvent(MotionEvent event) {
if (!isInterceptTouchEventEnabled()) {
return false;
}
if (!isPullLoadEnabled() && !isPullRefreshEnabled()) {
return false;
}
final int action = event.getAction();
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsHandledTouchEvent = false;
return false;
}
if (action != MotionEvent.ACTION_DOWN && mIsHandledTouchEvent) {
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastMotionY = event.getY();
mIsHandledTouchEvent = false;
break;
case MotionEvent.ACTION_MOVE:
final float deltaY = event.getY() - mLastMotionY;
final float absDiff = Math.abs(deltaY);
// 这里有三个条件:
// 1,位移差大于mTouchSlop,这是为了防止快速拖动引发刷新
// 2,isPullRefreshing(),如果当前正在下拉刷新的话,是允许向上滑动,并把刷新的HeaderView挤上去
// 3,isPullLoading(),理由与第2条相同
if (absDiff > mTouchSlop || isPullRefreshing() || isPullLoading()) {
mLastMotionY = event.getY();
// 第一个显示出来,Header已经显示或拉下
if (isPullRefreshEnabled() && isReadyForPullDown()) {
// 1,Math.abs(getScrollY()) > 0:表示当前滑动的偏移量的绝对值大于0,表示当前HeaderView滑出来了或完全
// 不可见,存在这样一种case,当正在刷新时并且RefreshableView已经滑到顶部,向上滑动,那么我们期望的结果是
// 依然能向上滑动,直到HeaderView完全不可见
// 2,deltaY > 0.5f:表示下拉的值大于0.5f
mIsHandledTouchEvent = (Math.abs(getScrollYValue()) > 0 || deltaY > 0.5f);
// 如果截断事件,我们则仍然把这个事件交给刷新View去处理,典型的情况是让ListView/GridView将按下
// Child的Selector隐藏
if (mIsHandledTouchEvent) {
mRefreshableView.onTouchEvent(event);
}
} else if (isPullLoadEnabled() && isReadyForPullUp()) {
// 原理如上
mIsHandledTouchEvent = (Math.abs(getScrollYValue()) > 0 || deltaY < -0.5f);
}
}
break;
default:
break;
}
return mIsHandledTouchEvent;
}
3、总体布局视图
![这里写图片描述](https://img-blog.csdn.net/20170213122717650?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
基本组件
1、框架内置提供PullRefreshRecyclerview,PullRefreshListView,PullRefreshWebView这三种刷新组件。PullRefreshRecyclerview最强大,它可以实现LinearView和GridView。而之所以提供PullRefreshListView,是主要供自定义的ListView和adapter来实现速度更快和多种样式的ListView。
【1】PullRefreshRecyclerview是一个自定义的下拉刷新RecyclerView扩展。我们知道RecylerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能。可以支持GridView与ListView,它的功能更加强大。
这里主要参考了HeaderAndFooterRecyclerView和LRecyclerView。
/**
* 点击事件接口
*/
public interface OnItemClickLitener {
void onItemClick(View view, int position);
boolean onItemLongClick(View view, int position);
}
void addHeaderView(View header)
void addFooterView(View footer)
int getCount()
/**
*数据处理逻辑
*/
void add(D object)
void addAll(Collection<? extends D> collection)
void addAll(D[] items)
void insert(D object, int index)
void insertAll(D[] object, int index)
void insertAll(Collection<? extends D> object, int index)
void remove(D object)
void remove(int position)
void clear()
void sort(Comparator<? super D> comparator)
/**
* 创建视图,交给子类实现方法
*
* @param parent
* @param viewType
* @return
*/
public abstract BaseViewHolder OnCreateViewHolder(ViewGroup parent, int viewType);
BaseViewHolder:这个ViewHolder是继承RecyclerView.ViewHolder的抽象类,主要是将视图处理与adapter的逻辑处理分离开来了。因此更加便于处理和操作。在建立adapter时就会要求生成BaseViewHolder。
以上RecyclerAdapter和BaseViewHolder主要参考了EasyRecyclerView
【2】PullRefreshListView是一个自定义的下拉刷新ListView扩展。之所以保留是为了更加方便的自定义和使用自己定义的adapter。
【3】PullRefreshWebView是一个自定义的下拉刷新WebView扩展。只支持下拉刷新。
【2】样式截图:
a、hader样式:
DefaultHeaderLoadingLayout:
![这里写图片描述](https://img-blog.csdn.net/20170213113927213?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
IndicatorViewHeaderLoadingLayout:
![这里写图片描述](https://img-blog.csdn.net/20170213114041230?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
ProgressBarHeaderLoadingLayout:
![这里写图片描述](https://img-blog.csdn.net/20170213114141418?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
RotateHeaderLoadingLayout:
![这里写图片描述](https://img-blog.csdn.net/20170213114310562?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
DefaultFooterLoadingLayout:
![这里写图片描述](https://img-blog.csdn.net/20170213114435436?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
IndicatorViewFooterLoadingLayout:
![这里写图片描述](https://img-blog.csdn.net/20170213114522487?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQW51bWJyZWxsYQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)