仿饿了么下拉刷新

使用RecyclerView仿饿了么下拉刷新

经常用饿了么点餐,看到这个下拉刷新,模仿一波。

效果图

Step1

自定义EleView.这个view就是根据RecyclerView的onTouch移动的距离,显示处理篮子打开和冒出水果的动画。这里有两个偏移,坐标(篮子位移)与角度(篮子盖打开)偏移,所以需要一个onMove方法统一传入偏移的百分百percent,再计算和重绘。

 public void onMove(float percent) {
        if (percent >= 0.6) {
            currentDegree = (float) (150 * (percent - 0.6) * 2.5);
        }
        if (percent <= 1) {
            offsetY = percent * maxOffsetY;
            invalidate();
        }
    }

在onDraw方法里面可以先绘制静态的图,在根据偏移统一调整坐标和角度。这里篮子有个迷の带感的震动效果,实现也是用属性动画:

    cycleAnimator = ValueAnimator.ofFloat(-1, 1);
        cycleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentOffset = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        ......

通过currentOffset变化,影响绘制的canvas.drawBitmap传入的Rect dst的坐标,造成影像的缩放。

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mWidth / 2, offsetY);
        for (int i = 0; i < 5; i++) {
            dstFruit.left = (int) (mPosition0[i][0] - 0.5 * fW);
            dstFruit.top = (int) (mPosition0[i][1] - 0.5 * fH);
            dstFruit.right = (int) (mPosition0[i][0] + 0.5 * fW);
            dstFruit.bottom = (int) (mPosition0[i][1] + 0.5 * fH);
            if (foods.get(i).showing) {
                canvas.drawBitmap(foods.get(i).bitmap, null, dstFruit, paint);
            }
        }
        canvas.save();
        int offsetC = (int) (currentOffset * 5);
        canvas.rotate(currentDegree, (float) (dstE.right - iWidth * scale / 4), (float) (dstE.top + scale * iWidth));
        canvas.drawBitmap(iR, null, new Rect(dstI2.left, dstI2.top - offsetC, dstI2.right, dstI2.bottom + offsetC), paint);
        canvas.restore();

        canvas.save();
        canvas.rotate(-currentDegree, (float) (dstE.left + iWidth * scale / 4), (float) (dstE.top + scale * iWidth));
        canvas.drawBitmap(iL, null, new Rect(dstI.left, dstI.top - offsetC, dstI.right, dstI.bottom + offsetC), paint);
        canvas.restore();
        canvas.drawBitmap(ele, null, new Rect(dstE.left, dstE.top - offsetC, dstE.right, dstE.bottom + offsetC), paint);
    }

水果移动需要得到水果移动的路径,在根据路径算出坐标。路径很好得到,大概估计一下就行。获取到路径在获取每个点的坐标,这里用到了PathMeasure的getPosTan(float distance, float pos[], float tan[]),简单的说就是把这个路径拉直成一条直线,然后根据当前位移获取当前的坐标,封装到数组中去。由于现在有5个水果需要移动,新建二位数组mPosition0 = new float[5][2];

 for (int i = 0; i < 5; i++) {
            final int finalI = i;
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, mPathMeasure.get(finalI).getLength());
            valueAnimator.setDuration(duration);
            valueAnimator.setInterpolator(new DecelerateInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    // 获取当前点坐标封装到mCurrentPosition
                    mPathMeasure.get(finalI).getPosTan(value, mPosition0[finalI], null);
                    postInvalidate();
                }
            });
    }

Step2

新建一个MyHeaderView 继承LinearLayout。这里为了方便以后的拓展,抽取一个接口:之后只要实现了这个接口,就可以传入到ERecyclerView充当刷新的HeadView。

public interface Header {
    int STATE_NORMAL = 0;
    int STATE_READY = 1;
    int STATE_REFRESHING = 2;
    int STATE_DONE = 3;


    void setVisibleHeight(int height);

    int getVisibleHeight();

    void setState(int state);

    void onMove(float delta);

    void refreshComplete();

    boolean releaseAction();
}

Step3

自定义RecyclerView主要是处理当onTouchEvent。其实还有其他的地方但不是本文的重点。

.....
    private Header headerView;
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (mLastY == -1) {
            mLastY = e.getRawY();
        }
        if (isOnTop() && !isloading) {
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mLastY = e.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float delta = e.getRawY() - mLastY;
                    mLastY = e.getRawY();
                    headerView.onMove(delta / 1.5f);
                    break;
                default:
                    if (headerView.releaseAction() && freshListener != null && !isloading) {
                        noMore = false;
                        isloading = true;
                        freshListener.onRefresh();
                    }
                    mLastY = -1;
                    break;
            }
        }
        return super.onTouchEvent(e);
    }

差不多就这些,没错就是这么短小精悍!还有其他的具体代码细节请看项目源码,如果觉得不错顺手点个star也是极好的O(∩_∩)O哈哈~:
https://github.com/Ulez/ERecyclerView

发布了29 篇原创文章 · 获赞 4 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/s122ktyt/article/details/74433986