Android一种自定义下拉刷新和上拉加载更多(使用外部拦截法解决冲突)

 

首先上效果图

 

有2种刷新方式

1.list滚动时无法触发刷新,必须置顶或置底才能触发刷新。

Android一种自定义下拉刷新和上拉加载更多(使用外部拦截法解决冲突)_第1张图片

2.list滚动时也可以触发刷新,即刷新与滚动可以兼容(不包括fling)。

Android一种自定义下拉刷新和上拉加载更多(使用外部拦截法解决冲突)_第2张图片

开始之前,请在gradle加入RecyclerView组件

implementation 'com.android.support:design:28.0.0'

        因为之前参考过很多资料,在使用RecyclerView下拉刷新的时候,网上大多数方法都是在RecyclerView中添加首尾元素达到刷新的目的,但是这样需要写多个xml布局以及修改adapter,感觉使用起来很不方便,于是在研究如何脱离adapter来实现下拉刷新。

        有一个难点:需要解决滑动冲突的问题。

 解决:

    这里我使用外部拦截法来解决冲突,即在RecyclerView外层需要写一层viewgroup,然后重写layout的onInterceptTouchEvent,所有事件都会从这里经过。但是需要注意的是,DOWN事件不能拦截,因为拦截了就会致使RecyclerView接收不到任何事件。

    但是,需要注意的是,如果指定layout进行处理事件,那么这一次的事件就不会再经过layout的onInterceptTouchEvent,而是直接调用layout的onTouchEvent。

详细的我在代码中进行了注解,不明白的可以评论,我回及时回复您,也很欢迎和我讨论。

 

code:

layout代码,继承自LinearLayout


/**
 * author: siney
 * Date: 2019/3/3
 * description: 使用外部拦截法解决滑动冲突
 */
public class RefreshLoadLayout extends LinearLayout {

    private static final String TAG = "RefreshLoadLayout";
    private static final String REFRESH = "refreshing";
    private static final String LOAD = "loading";
    private static final String BOTH = "both";
    public static final int MOVING = 1, UP = 2;

    public interface OnChangeListener{
        void headerChange(RefreshLoadLayout layout, int nowH, int maxH, int action);
        void footerChange(RefreshLoadLayout layout, int nowH, int maxH, int action);
    }

    public RefreshLoadLayout(Context context) {
        super(context);
    }

    public RefreshLoadLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RefreshLoadLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initParameters(attrs);
    }

    private void initParameters(AttributeSet attrs) {
        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RefreshLoadLayout);
        headerId = a.getResourceId(R.styleable.RefreshLoadLayout_header, -1);
        footerId = a.getResourceId(R.styleable.RefreshLoadLayout_footer, -1);
        mode = a.getString(R.styleable.RefreshLoadLayout_mode);
        duration = a.getInt(R.styleable.RefreshLoadLayout_duration, 500);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(!isFirst){
            isFirst = true;
            initChildView();
        }
    }

    private void initChildView() {
        if(headerId == -1 && footerId == -1){
            Log.e(TAG, "Your have to set at least one resource Id");
            return;
        }
        for(int i = 0;i < getChildCount();i++){
            View child = getChildAt(i);
            if(child.getId() == headerId){
                header = child;
                headerMaxH = child.getMeasuredHeight();
                headerNowH = 0;
                setViewHeight(header, 0);
            }else if(child.getId() == footerId){
                footer = child;
                footerMaxH = child.getMeasuredHeight();
                setViewHeight(footer, 0);
            }else if(child instanceof RecyclerView){
                recyclerView = (RecyclerView) child;
            }
        }
    }

    private void setViewHeight(View v, int height) {
        ViewGroup.LayoutParams lp = v.getLayoutParams();
        if(v == header){
            if(height > headerMaxH)headerNowH = headerMaxH;
            else if(height < 0)headerNowH = 0;
            else headerNowH = height;
            lp.height = headerNowH;
        }else if(v == footer){
            if(height > footerMaxH)footerNowH = footerMaxH;
            else if(height < 0)footerNowH = 0;
            else footerNowH = height;
            lp.height = footerNowH;
        }
        v.setLayoutParams(lp);

    }

    private void animClose(final View v, int nowH) {
        ValueAnimator animator = ValueAnimator.ofInt(nowH, 0);
        if(interpolator != null)
            animator.setInterpolator(interpolator);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int vv = (int) animation.getAnimatedValue();
                setViewHeight(v, vv);
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimFinish = true;
            }
        });
        animator.setDuration(duration);
        animator.start();
    }

    private int headerId, footerId;

    private String mode;

    private boolean isFirst, isAnimFinish = true;

    private int headerNowH, headerMaxH, footerNowH, footerMaxH;

    private View header, footer;

    private RecyclerView recyclerView;

    private int lastY, duration;

    private OnChangeListener listener;

    private Interpolator interpolator;

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if(!isAnimFinish)
            return true;
        int y = (int) e.getRawY();
        switch (e.getAction()){
            case MotionEvent.ACTION_MOVE:
                handle(e);
                break;
            case MotionEvent.ACTION_UP:
                if(listener != null){
                    if(headerNowH > 0){
                        isAnimFinish = false;
                        listener.headerChange(this, headerNowH, headerMaxH, UP);
                    }else if(footerNowH > 0){
                        isAnimFinish = false;
                        listener.footerChange(this, footerNowH, footerMaxH, UP);
                    }
                }
                break;
        }
        lastY = y;
        return true;//如果本layout拦截了,那么处理结束就已经消费结束
    }

    private void handle(MotionEvent e) {
        int y = (int) e.getRawY();
        if(REFRESH.equals(mode)){
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if(manager instanceof LinearLayoutManager)
                ((LinearLayoutManager)manager).setStackFromEnd(false);
            if(headerNowH == 0 && (y - lastY) < 0 || (y - lastY) > 0 && recyclerView.canScrollVertically(-1))
                recyclerView.onTouchEvent(e);
            else if(!recyclerView.canScrollVertically(-1)){
                if(listener != null)
                    listener.headerChange(this, headerNowH, headerMaxH, MOVING);
                setViewHeight(header, headerNowH + y - lastY);
            }
        }else if(LOAD.equals(mode)){
            if(footerNowH == 0 && (y - lastY) > 0 || (y - lastY) < 0 && recyclerView.canScrollVertically(1))
                recyclerView.onTouchEvent(e);
            else if(recyclerView.canScrollVertically(-1) && !recyclerView.canScrollVertically(1)){
                if(listener != null)
                    listener.headerChange(this, footerNowH, footerMaxH, MOVING);
                RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
                if(manager instanceof LinearLayoutManager)
                    ((LinearLayoutManager)manager).setStackFromEnd(true);
                setViewHeight(footer, footerNowH + lastY - y);
            }

        }else if(BOTH.equals(mode)){
            if((y - lastY) > 0 && !recyclerView.canScrollVertically(-1)){//如果下拉,并且不能下滑了,说明到头
                ((LinearLayoutManager)recyclerView.getLayoutManager()).setStackFromEnd(false);
                if(listener != null)
                    listener.headerChange(this, headerNowH, headerMaxH, MOVING);
                setViewHeight(header, headerNowH + y - lastY);
            }else if((y - lastY) < 0 && !recyclerView.canScrollVertically(1)
                    && recyclerView.canScrollVertically(-1) ){//如果上拉,并且不能上滑了,说明到底
                ((LinearLayoutManager)recyclerView.getLayoutManager()).setStackFromEnd(true);
                if(listener != null)
                    listener.headerChange(this, footerNowH, footerMaxH, MOVING);
                setViewHeight(footer, footerNowH + lastY - y);
            }else{//其余情况在这里处理
                if(headerNowH > 0){
                    if(listener != null)
                        listener.headerChange(this, headerNowH, headerMaxH, MOVING);
                    setViewHeight(header, headerNowH + y - lastY);
                }else if(footerNowH > 0){
                    if(listener != null)
                        listener.headerChange(this, footerNowH, footerMaxH, MOVING);
                    setViewHeight(footer, footerNowH + lastY - y);
                }else{
                    recyclerView.onTouchEvent(e);
                }
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        boolean intercepted = false;
        int y = (int) e.getRawY();
        switch (e.getAction()){
            case MotionEvent.ACTION_DOWN:
                intercepted = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if(!isAnimFinish){
                    intercepted = true;
                }else if(REFRESH.equals(mode)){
                    if(!recyclerView.canScrollVertically(-1) && (y - lastY) > 0)
                        intercepted = true;
                }else if(LOAD.equals(mode)){
                    if(!recyclerView.canScrollVertically(1) && (lastY - y) > 0 && recyclerView.canScrollVertically(-1))
                        intercepted = true;
                }else if(BOTH.equals(mode)){
                    if(!recyclerView.canScrollVertically(-1) && (y - lastY) > 0 ||
                            (recyclerView.canScrollVertically(-1) && !recyclerView.canScrollVertically(1) && (lastY - y) > 0))
                        intercepted = true;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        lastY = y;
        return intercepted;
    }

    //完成加载后finish操作
    public void finish(){
        if(headerNowH >0){
            animClose(header, headerNowH);
        }else if(footerNowH > 0){
            animClose(footer, footerNowH);
        }
    }

    public OnChangeListener getListener() {
        return listener;
    }

    public void setListener(OnChangeListener listener) {
        this.listener = listener;
    }

    public Interpolator getInterpolator() {
        return interpolator;
    }

    public void setInterpolator(Interpolator interpolator) {
        this.interpolator = interpolator;
    }
}

 

 

xml布局文件

mode:表示滑动模式,有refreshing、loading、both,3种模式,第一种只有刷新,第二种只有加载更多,第三种两者都有。header和footer分别表示下拉刷新和上拉加载更多的资源布局id。



    

    


    

Activity

public class MainActivity extends AppCompatActivity {

    private RecyclerView my;

    private RefreshLoadLayout layout;

    private TextView header, footer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        my = findViewById(R.id.my);
        layout = findViewById(R.id.layout);
        header = findViewById(R.id.header);
        footer = findViewById(R.id.footer);
        final LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        MyAdapter adapter = new MyAdapter();
        my.setLayoutManager(manager);
        my.setAdapter(adapter);
        initListener();
    }

    private void initListener() {
        layout.setListener(new RefreshLoadLayout.OnChangeListener() {
            @Override
            public void headerChange(final RefreshLoadLayout layout, int nowH, int maxH, int action) {
                if(action == RefreshLoadLayout.UP){
                    header.setText("刷新中.......");
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            header.setText("加载完成");
                            layout.finish();
                        }
                    }, 2000);
                }
            }
            @Override
            public void footerChange(final RefreshLoadLayout layout, int nowH, int maxH, int action) {
//                Log.e("TAG", "footer "+nowH+" "+" "+action);
                if(action == RefreshLoadLayout.UP){
                    footer.setText("加载中.......");
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            footer.setText("加载完成");
                            layout.finish();
                        }
                    }, 2000);
                }
            }
        });
    }
}

对于 list滚动时也可以触发刷新,即刷新与滚动可以兼容(不包括fling)这种情况,只需要继承RecycleView,或者设置OnTouchListener。

代码如下:

 @Override
    public boolean onTouchEvent(MotionEvent e) {
        int y = (int) e.getRawY();
        switch (e.getAction()){
            case MotionEvent.ACTION_MOVE:
                if(!canScrollVertically(-1) && (y - lastY) > 0 || !canScrollVertically(1) && (lastY - y) > 0){
                    Log.e("TAG", "MyRecyclerView onTouchEvent 【MOVE】 给上一级");
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
                break;
        }
        return super.onTouchEvent(e);
    }

demo可以看我的github

你可能感兴趣的:(android)