一种自定义的下拉刷新(RecyclerView也可以使用)

 

首先上效果图

一种自定义的下拉刷新(RecyclerView也可以使用)

 

一种自定义的下拉刷新(RecyclerView也可以使用) 

 

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

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

 解决:

    巧妙使用listener的方式,在RecyclerView中定义一个interface listener,然后在layout中获取recyclerview对象,然后实现该listener,同时在滑动时往listener中传入相关数据,然后再layout布局中修改header的LayoutParams就能实现下拉刷新的效果。

 

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

 

code:

layout代码,继承自LinearLayout


/**
 * author: siney
 * Date: 2019/1/17
 * description:
 */
public class SlideRefreshingLayout extends LinearLayout {

    public SlideRefreshingLayout(Context context) {
        super(context);
        setClickable(true);
    }

    public SlideRefreshingLayout(Context context,  AttributeSet attrs) {
        super(context, attrs);
        setClickable(true);
    }

    //提供给外面不适用Recycler的监听器
    interface OnScrollListener{
        void onScrollChanged(int height, int maxHeight);//此时显示的高度
    }

    private int yy;

    private int refreshingH, nowH;//refreshingH是获取的header的高度, nowH是现在已经滑动了多少高

    private View refreshingView;//获取header的View对象

    private boolean isSet;//判断布局是否已经设置过

    private OnScrollListener listener;//给使用者的回调监听,返回此时Header的高度以及最大高度

    private MyRecyclerView recyclerView;//如果子布局中有recyclerview,那么此不为空

    private MyRecyclerView.OnTouchScrollListener touchListener = new MyRecyclerView.OnTouchScrollListener() {
        //这里为下拉刷新核心代码
        //我在Recycler动了些手脚,因为滑动冲突,所以我就是以监听器的方式来传递所需要的滑动数据
        @Override
        public void touchScrolling(int dist, int action) {
            if(recyclerView.isCanScroll() && !recyclerView.canScrollVertically(-1)){
                recyclerView.setCanScroll(false);//设置为false说明现在处于refreshing状态,LoadingMore道理一样
            }
            if(action == MotionEvent.ACTION_UP && !recyclerView.isCanScroll()){
                //如果在手离开屏幕后,还是处于false状态,那么会自动关闭refreshing,并且设置为true
                bounceClose();
                recyclerView.setCanScroll(true);
            }else if(action == MotionEvent.ACTION_MOVE && !recyclerView.isCanScroll()){
                nowH += dist;
                if(nowH > refreshingH){
                    nowH = refreshingH;
                }else if(nowH < 0){
                    nowH = 0;
                    recyclerView.setCanScroll(true);
                }
                if(listener != null)
                    listener.onScrollChanged(nowH, refreshingH);
                resetRefreshingH(nowH);
            }
        }
    };

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(!isSet)
            initRefreshingWH();//初始化Layout布局中的所有控件,默认第一个为下拉刷新的布局
    }

    private void initRefreshingWH() {
        isSet = true;
        if(getChildCount() > 0){
            refreshingView = getChildAt(0);
            refreshingH = refreshingView.getMeasuredHeight();
            resetRefreshingH(0);//初始化Header为0
            for(int i = 0;i < getChildCount();i++){
                if(getChildAt(i) instanceof MyRecyclerView){
                    recyclerView = (MyRecyclerView) getChildAt(i);
                    recyclerView.setListener(touchListener);
                    break;
                }
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(recyclerView != null || getChildCount() == 0)
            return super.onTouchEvent(event);
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                yy = y;
                break;
            case MotionEvent.ACTION_MOVE:
                //下面进行测量移动的距离
                if(y - yy > 0){
                    //向下移动
                    nowH += (y - yy);
                    if(nowH > refreshingH){
                        nowH = refreshingH;
                    }
                }else if(y - yy < 0){
                    //向上移动
                    nowH -= (yy - y);
                    if(nowH < 0){
                        nowH = 0;
                    }
                }
                yy = y;
                resetRefreshingH(nowH);
                if(listener != null)
                    listener.onScrollChanged(nowH, refreshingH);
                break;
            case MotionEvent.ACTION_UP:
                bounceClose();
                break;
        }
        return super.onTouchEvent(event);
    }

    private void resetRefreshingH(int h){
        ViewGroup.LayoutParams lp = refreshingView.getLayoutParams();
        lp.height = h;
        refreshingView.setLayoutParams(lp);
    }

    private void bounceClose() {
        ValueAnimator animator = ValueAnimator.ofInt(nowH, 0);
        animator.setInterpolator(new BounceInterpolator());
        animator.setDuration(1000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int)animation.getAnimatedValue();
                resetRefreshingH(value);
            }
        });
        animator.start();
        nowH = 0;
    }

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

}

 recyclerview代码:

/**
 * author: siney
 * Date: 2019/1/17
 * description:
 */
public class MyRecyclerView extends RecyclerView {

    interface OnTouchScrollListener{
        void touchScrolling(int dist, int action);
    }

    public MyRecyclerView(@NonNull Context context) {
        super(context);
    }

    public MyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    private OnTouchScrollListener listener;//监听moving状态

    private int yy;

    private boolean canScroll = true;//当前RecyclerView能否滑动

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        int y = (int) e.getRawY();//获取相对屏幕的坐标
        switch (e.getAction()){
            case MotionEvent.ACTION_DOWN:
                yy = y;
            case MotionEvent.ACTION_MOVE:
                listener.touchScrolling(y - yy, MotionEvent.ACTION_MOVE);
                yy = y;
                if(!canScroll){
                    return true;//设置为已经消费,这样就不会进行事件传播
                }
                break;
            case MotionEvent.ACTION_UP:
                listener.touchScrolling(yy, MotionEvent.ACTION_UP);//松开后如果canScroll是false,那么就会关闭上refresh的页面
                break;
        }
        return super.onTouchEvent(e);
    }

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

    public boolean isCanScroll() {
        return canScroll;
    }

    public void setCanScroll(boolean canScroll) {
        this.canScroll = canScroll;
    }

}

xml布局文件

<com.frz.slidefreshlayout.SlideRefreshingLayout
    android:id="@+id/layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp">
        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="24sp"
            android:layout_centerInParent="true"/>
    </RelativeLayout>

    <!--<com.frz.slidefreshlayout.MyRecyclerView-->
        <!--android:id="@+id/recycler"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent"/>-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="asdfasdfsad"
            android:background="#BCBCBC"/>
    </RelativeLayout>

</com.frz.slidefreshlayout.SlideRefreshingLayout>

Activity

public class MainActivity extends AppCompatActivity {

    private TextView text;

    private SlideRefreshingLayout layout;

//    private RecyclerView recyclerView;

    private MyAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = findViewById(R.id.text);
        layout = findViewById(R.id.layout);
//        recyclerView = findViewById(R.id.recycler);
        initListener();
        dataBind();
    }

    private void dataBind() {
//        List<String> list = new ArrayList<>();
//        for(int i = 0;i < 20;i++){
//            list.add(UUID.randomUUID().toString());
//        }
//        adapter = new MyAdapter(list);
//        LinearLayoutManager manager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
//        recyclerView.setLayoutManager(manager);
//        recyclerView.setAdapter(adapter);
    }

    private void initListener() {
        layout.setListener(new SlideRefreshingLayout.OnScrollListener() {
            @Override
            public void onScrollChanged(int height, int maxHeight) {
                if(height > maxHeight / 2){
                    text.setText("松手刷新");
                }else{
                    text.setText("继续下拉刷新");
                }
            }
        });
    }

}

class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{

    private List<String> list;

    public MyAdapter(List<String> list) {
        this.list = list;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
        viewHolder.text.setText(list.get(i));
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder{

        private TextView text;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            text = itemView.findViewById(R.id.text);
        }

    }

}

总结:

就是根据layout获取子控件,子控件中存在和不存在recyclerview,都可以判断出来,然后根据需求调整,从而实现下拉刷新。