android里面quadTo的实例(不是很酷炫)

好久没有写点小东西了,昨天去逛了一些dribbble,看了一个下拉效果貌似还不错

这让我联想到之前很久就出来一个类似的效果,github上面已经有人写出来了https://github.com/tuesda/CircleRefreshLayout.

言归正传,标题尽然是quadTo,此时你一定会吐槽不就是贝塞尔的二阶函数么,下拉的时候的有点像椭圆的形状不就是被他搞出来的么。没错其实原理还是很简单,但是如果数学不好的人去写的话,还是很吃力的譬如本菜鸟,那我们今天主要实现的是下拉的时候出现的类似椭圆的形状。

一眼看懂原理的人请无视下面一段话。说下菜鸟的思路:考虑用一个FrameLayout去包裹一个RecylerView(为什么不用线性的呢,万一人家要用覆盖式的,请自行脑补),在onInterceptTouchEvent里面去判断是否是下拉并且RecylerView是否能够被下拉(描述不太准确),在onTouchEvent里面去根据下拉距离不断改变HeaderView的高度以及mChildViewtranslationY,释放的时候用一个ValueAnimator同理去操作,罗里吧嗦把原理说完了,下面具体我只想静静滴上代码。

先看下FrameLayout的代码:

/**
 * Created by Tangxb on 2016/4/27.
 */
public class ComplexLayout extends FrameLayout {
    private View mChildView;
    private float mPullHeight;
    private float mHeaderHeight;
    private float mTouchStartY;
    private float mTouchCurY;
    private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(10);
    private AnimationView mHeader;

    public ComplexLayout(Context context) {
        this(context, null);
    }

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

    public ComplexLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPullHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 600, context.getResources().getDisplayMetrics());
        mHeaderHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mChildView = getChildAt(0);
        addHeaderView();
    }

    private void addHeaderView() {
        mHeader = new AnimationView(getContext());
        LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
        params.gravity = Gravity.TOP;
        mHeader.setLayoutParams(params);
        addView(mHeader);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchStartY = ev.getY();
                mTouchCurY = mTouchStartY;
                break;
            case MotionEvent.ACTION_MOVE:
                float curY = ev.getY();
                float dy = curY - mTouchStartY;
                if (dy > 0 && !canChildScrollUp()) {
                    return true;
                }
        }
        return super.onInterceptTouchEvent(ev);
    }

    private boolean canChildScrollUp() {
        if (mChildView == null) {
            return false;
        }

        return ViewCompat.canScrollVertically(mChildView, -1);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mTouchCurY = event.getY();
                float dy = mTouchCurY - mTouchStartY;
                dy = Math.min(mPullHeight * 2, dy);
                dy = Math.max(0, dy);


                if (mChildView != null) {
                    float offsetY = decelerateInterpolator.getInterpolation(dy / 2 / mPullHeight) * dy / 2;
                    mChildView.setTranslationY(offsetY);

                    mHeader.getLayoutParams().height = (int) offsetY;
                    mHeader.requestLayout();
                }


                return true;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mHeaderHeight = mHeader.getHeight();
                ValueAnimator va = ValueAnimator.ofFloat(mHeaderHeight, 0);
                va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float val = (float) animation.getAnimatedValue();
                        val = decelerateInterpolator.getInterpolation(val / mHeaderHeight) * val;
                        if (mChildView != null) {
                            mChildView.setTranslationY(val);
                        }
                        mHeader.getLayoutParams().height = (int) val;
                        mHeader.requestLayout();
                    }
                });
                va.setDuration(500);
                va.start();
                return true;
            default:
                return super.onTouchEvent(event);
        }
    }
}

在看下里面的AnimationView的代码:

public class AnimationView extends View {
    private int PULL_HEIGHT;
    private int PULL_DELTA;
    private float mWidthOffset;
    private Path mPath;
    private Paint mBackPaint;
    private int mWidth;
    private int mHeight;

    public AnimationView(Context context) {
        this(context, null);
    }

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

    public AnimationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        PULL_HEIGHT = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, context.getResources().getDisplayMetrics());
        PULL_DELTA = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 500, context.getResources().getDisplayMetrics());
        mWidthOffset = 0.5f;
        mPath = new Path();
        mBackPaint = new Paint();
        mBackPaint.setAntiAlias(true);
        mBackPaint.setStyle(Paint.Style.FILL);
        mBackPaint.setColor(0xff8b90af);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (height > PULL_DELTA + PULL_HEIGHT) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(PULL_DELTA + PULL_HEIGHT, MeasureSpec.getMode(heightMeasureSpec));
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawRect(0, 0, mWidth, PULL_HEIGHT, mBackPaint);

        mPath.reset();
        mPath.moveTo(0, PULL_HEIGHT);
        mPath.quadTo(mWidthOffset * mWidth, PULL_HEIGHT + (mHeight - PULL_HEIGHT) * 2, mWidth, PULL_HEIGHT);
        canvas.drawPath(mPath, mBackPaint);
    }
}

其实难点的计算部分,里面用了DecelerateInterpolator

https://github.com/tuesda/CircleRefreshLayout
https://dribbble.com/shots/1209105-first-shot
https://dribbble.com/shots/1797373-Pull-Down-To-Refresh

你可能感兴趣的:(Android)