项目链接:点击打开链接
前段时间和同事一起写了一个下拉刷新和上拉加载的控件。该控件实现的功能的功能还是挺多的。支持自定义头部和底布局,同时还处理了NestScrolling机制的嵌套滑动的一些东西。我们先来看下效果图吧。
1.主页面布局展示
2.支持RecycleView的刷新和加载效果。
3.支持listView的刷新和加载效果。
4.支持ScrollView的刷新和加载效果
5.支持NestScrolling机制的刷新和加载效果。
6.内容固定模式
1.控件的使用方式
从代码中可以看到,使用pullLayout的时候,我们一般要设置三个view,第一个view是内容布局,这里我们可以设置recycycleview、listview、scrollview甚至LinearLayout等其他布局。第二个view和第三个view分别代表头布局和底布局,这是我们自定义的布局,头布局和底部局的具体事件操作需要我们自行在里面定义。
当然也不是必须设置三个view,但是至少设置一个view来代表内容布局,否则这个空间将没有什么意义了,啥都没有还能用来干嘛呢?第二和第三个view是我们可以自行设置的。
2.pullLayout的代码
import android.content.Context;
import android.graphics.Point;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.NestedScrollingChild;
import android.support.v4.view.NestedScrollingChildHelper;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import java.util.ArrayList;
/**
* @description 可能具有顶部刷新和底部加载功能的布局
* @note 视图的添加顺序为内容、头部(非必要)、底部(非必要)
**/
public class PullLayout extends ViewGroup implements NestedScrollingParent,NestedScrollingChild {
//内容视图
private View mContentView;
//顶部刷新的时候会显示的视图
private View mHeaderView;
//底部加载的时候会显示的视图
private View mFooterView;
//当前是否在触摸状态下
private boolean isOnTouch;
private PullLayoutOption mOption;
//头部视图的高度
private int mHeaderHeight;
//底部视图的高度
private int mFooterHeight;
//上次的触摸事件坐标
private Point mLastPoint;
//当前偏移量
private int mCurrentOffset;
//上次的偏移量
private int mPrevOffset;
private int mTouchSlop;
//刷新和加载更多的回调
private ArrayList
mRefreshListeners;
private ArrayList
mLoadMoreListeners;
//当前是否在刷新中
private boolean isRefreshing;
//当前是否在加载中
private boolean isLoading;
//缓慢滑动工作者
private ScrollerWorker mScroller;
//主要用于标记当前事件的意义
private boolean canUpIntercept;
private boolean canDownIntercept;
//一次拦截事件的时候当前是否可以顶部或底部刷新
private boolean canUp;
private boolean canDown;
//当前是否处于嵌套滑动中
private boolean isNestedScrolling;
private NestedScrollingParentHelper mParentHelper;
private NestedScrollingChildHelper mChildHelper;
public PullLayout(Context context) {
this(context, null);
}
public PullLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData();
}
private void initData() {
mOption = new PullLayoutOption();
mLastPoint = new Point();
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mRefreshListeners = new ArrayList<>();
mLoadMoreListeners = new ArrayList<>();
mScroller = new ScrollerWorker(getContext());
mParentHelper = new NestedScrollingParentHelper(this);
mChildHelper = new NestedScrollingChildHelper(this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
int childCount = getChildCount();
switch (childCount) {
case 1://这种时候默认只有一个内容视图
mContentView = getChildAt(0);
break;
case 2://默认优先支持顶部刷新
mContentView = getChildAt(0);
mHeaderView = getChildAt(1);
break;
case 3:
mContentView = getChildAt(0);
mHeaderView = getChildAt(1);
mFooterView = getChildAt(2);
break;
default:
throw new IllegalArgumentException("必须包括1到3个子视图");
}
checkHeaderAndFooterAndAddListener();
}
/**
* 检查头部和底部是否为监听,是的话添加到监听回调列表中
*/
private void checkHeaderAndFooterAndAddListener() {
if (mHeaderView instanceof IRefreshListener) {
mRefreshListeners.add((IRefreshListener) mHeaderView);
}
if (mFooterView instanceof ILoadMoreListener) {
mLoadMoreListeners.add((ILoadMoreListener) mFooterView);
}
}
@Override
protected