App中下拉刷新和上拉加載是很常見(jiàn)的功能组去,網(wǎng)上也有很多第三方庫(kù)可以用,為了盡快完成項(xiàng)目,一般都是集成別人的庫(kù)快速完成功能霉祸, 但有時(shí)候產(chǎn)品腦洞比較大 ,用別人的庫(kù)又沒(méi)那么靈活改起來(lái)也麻煩袱蜡, 就只能自己來(lái)了脉执。。戒劫。半夷。。
下面看一下效果:
自定義還是挺麻煩的迅细,不過(guò)也比較有意思巫橄,完成這個(gè)需要先了解幾個(gè)知識(shí)點(diǎn):
1.自定義ViewGroup
2.Scroller
3.事件分發(fā)體系。
下面開(kāi)始簡(jiǎn)單的實(shí)現(xiàn)一下:
1.自定義ReboundLinearLayout 繼承自 RelativeLayout 這個(gè)ViewGroup內(nèi)部放需要上拉加載的Recyclerview
public class ReboundLinearLayout extends RelativeLayout {
2.由于事件傳遞的時(shí)候RecyclerView會(huì)把事件全部消耗完茵典,所以要在ReboundLinearLayout的onInterceptTouchEvent中判斷需要攔截的事件湘换, 這里的思路是:如果RecyclerView已經(jīng)滑動(dòng)到最底部,就可以進(jìn)行上拉加載了,這時(shí)候就可以攔截手指的向上滑動(dòng)事件:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(isSlideToBottom(reboundRecyclerView)){
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
endY = ev.getY();
if(endY - startY >= 0){
return false;
}else if(endY - startY < 0){
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
}else{
return false;
}
return false;
}
3.在攔截完事件后彩倚,我們?cè)趏nTouchEvent中進(jìn)行事件的處理筹我,當(dāng)然就是使用scroller來(lái)將RecyclerView進(jìn)行移動(dòng)了:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
scrollEndY = event.getY();
if(startY - scrollEndY <= SizeUtils.dp2px(scrollHeight)){
scrollTo(0, (int) (startY - scrollEndY));
}
break;
case MotionEvent.ACTION_UP:
mScroller.startScroll(0, getScrollY(),0, -getScrollY(),300);
invalidate();
if(onRefreshListenner != null){
onRefreshListenner.onRefresh();
}
break;
default:
break;
}
return true;
}
這樣就可以實(shí)現(xiàn)簡(jiǎn)單的上拉加載了,加個(gè)刷新監(jiān)聽(tīng) 帆离,在刷新的時(shí)候請(qǐng)求數(shù)據(jù)蔬蕊,就完成啦。哥谷。岸夯。
下面貼一下代碼
public class ReboundLinearLayout extends RelativeLayout {
private RecyclerView reboundRecyclerView;
//判斷滑動(dòng)方向
private float startY;
private float endY;
private float scrollEndY;
private Scroller mScroller;
//最大拉動(dòng)距離
private int scrollHeight = 80;
private OnRefreshListenner onRefreshListenner;
public ReboundLinearLayout(Context context) {
super(context);
}
public ReboundLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public ReboundLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(isSlideToBottom(reboundRecyclerView)){
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
endY = ev.getY();
if(endY - startY >= 0){
return false;
}else if(endY - startY < 0){
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
}else{
return false;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
scrollEndY = event.getY();
if(startY - scrollEndY <= SizeUtils.dp2px(scrollHeight)){
scrollTo(0, (int) (startY - scrollEndY));
}
break;
case MotionEvent.ACTION_UP:
mScroller.startScroll(0, getScrollY(),0, -getScrollY(),300);
invalidate();
if(onRefreshListenner != null){
onRefreshListenner.onRefresh();
}
break;
default:
break;
}
return true;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
reboundRecyclerView = (RecyclerView) getChildAt(0);
}
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) return false;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange())
return true;
return false;
}
@Override
public void computeScroll() {
super.computeScroll();
//第二步
if(mScroller.computeScrollOffset()){
//第三步
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
invalidate();
}
}
public void setOnRefreshListenner(OnRefreshListenner onRefreshListenner) {
this.onRefreshListenner = onRefreshListenner;
}
public interface OnRefreshListenner{
void onRefresh();
}
}
結(jié)束。