仿IOS版QQ的下拉刷新头实现原理

一直很好奇苹果版QQ的下拉刷新头,那种水滴状的感觉,特别有弹性的感觉,于是趁着项目比较松的时候也来实现一下,这是实现后的图


最主要的要知道这个图形的画法,我使用的是Path路径来做的,然后使用填充画笔,把他全部填充

主要使用两个半圆和两条二次曲线构成


仿IOS版QQ的下拉刷新头实现原理_第1张图片

于是引入关键代码

补充一下,path中绘制圆弧用的是arcTo方法,不仅可以绘制圆弧也可以绘制椭圆圆弧,传入矩形区域和角度变化即可,值得注意的是圆弧的方向,用法不当会导致曲线无法闭合。至于二次曲线就不说了

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

         //路径重置
        mPath.reset();

        //绘制大圆圆弧
        mPath.arcTo(new RectF(viewwdith / 2 - GreatCircleRadius, GreatCircleY - GreatCircleRadius,
                viewwdith / 2 + GreatCircleRadius, GreatCircleY + GreatCircleRadius), 0, -180);
        //绘制左边的二次曲线
        mPath.quadTo(viewwdith / 2 - SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 - SmallCircleRadius, SmallCircleY);
        //把点移动到大半圆的右边
        mPath.moveTo(viewwdith / 2 + GreatCircleRadius, GreatCircleY);
        //绘制右边的二次曲线
        mPath.quadTo(viewwdith / 2 + SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 + SmallCircleRadius, SmallCircleY);
        //绘制小圆圆弧
        mPath.arcTo(new RectF(viewwdith / 2 - SmallCircleRadius, SmallCircleY - SmallCircleRadius,
                viewwdith / 2 + SmallCircleRadius, SmallCircleY + SmallCircleRadius), 0, 180);

        canvas.drawPath(mPath, mPaint);
    }


这就是主要的绘制方法,剩下的就好办啦,重写触摸事件,使他下拉时,移动小圆的圆心位置,并且根据两个圆的圆心距,改变两个圆的半径,初始时,两个园大小是一致的,随着距离的增大,小圆半径缩小的更快。当手抬起后,开启线程重置刷新头。


下面是所有的代码:

package com.example.kaifa.myapplication;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * TODO: document your custom view class.
 */
public class MyView extends View {


    private Paint mPaint;
    /**
     * 下拉进度
     */
    private float progress = 0;
    /**
     * view的宽高
     */
    private int viewheight, viewwdith;

    /**
     * 大圆半径
     */
    private float GreatCircleRadius = 50;
    /**
     * 小圆半径
     */
    private float SmallCircleRadius = 50;
    /**
     * 大圆和小圆分别Y轴的坐标
     */
    private float GreatCircleY = 60, SmallCircleY = 60;
    /**
     * 绘制路径
     */
    private Path mPath;
    /**
     * 第一次按下的Y轴坐标
     */
    float firstY = 0;

    public MyView(Context context) {
        super(context);
        init(null, 0);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setColor(0xff0000ff);
        mPaint.setStyle(Paint.Style.FILL);
        mPath = new Path();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewwdith = w;
        viewheight = h;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

         //路径重置
        mPath.reset();

        //绘制大圆圆弧
        mPath.arcTo(new RectF(viewwdith / 2 - GreatCircleRadius, GreatCircleY - GreatCircleRadius,
                viewwdith / 2 + GreatCircleRadius, GreatCircleY + GreatCircleRadius), 0, -180);
        //绘制左边的二次曲线
        mPath.quadTo(viewwdith / 2 - SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 - SmallCircleRadius, SmallCircleY);
        //把点移动到大半圆的右边
        mPath.moveTo(viewwdith / 2 + GreatCircleRadius, GreatCircleY);
        //绘制右边的二次曲线
        mPath.quadTo(viewwdith / 2 + SmallCircleRadius, (GreatCircleY + SmallCircleY) / 2, viewwdith / 2 + SmallCircleRadius, SmallCircleY);
        //绘制小圆圆弧
        mPath.arcTo(new RectF(viewwdith / 2 - SmallCircleRadius, SmallCircleY - SmallCircleRadius,
                viewwdith / 2 + SmallCircleRadius, SmallCircleY + SmallCircleRadius), 0, 180);

        canvas.drawPath(mPath, mPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                firstY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = event.getY() - firstY;
                if (Math.abs(dy) > 2 &&(SmallCircleY-GreatCircleY)<viewheight) {
                    if (dy<0&&SmallCircleY<GreatCircleY){

                    }else{

                        SmallCircleY = SmallCircleY + dy;
                        if (SmallCircleY<GreatCircleY)
                            SmallCircleY=GreatCircleY;
                    }
                    jisuanR();
                }
                firstY = event.getY();
                Log.v("xingyun", "dy=" + dy);
                invalidate();
                break;

            case MotionEvent.ACTION_UP:
                //开启线程匀速返回
               new MyTread().start();
                break;

        }
        return true;
    }

    /**
     * 根据下拉的距离来计算两个圆的半径
     */
    private void jisuanR(){
        float dy=SmallCircleY-GreatCircleY;

        progress=dy/(viewheight);
        SmallCircleRadius=(float)(50*(1-0.9*progress));
        GreatCircleRadius= (float) (50*(1-0.5*progress));

    }

    /**
     * 回弹的线程
     */
    class MyTread extends Thread{
        @Override
        public void run() {
            while (SmallCircleY-GreatCircleY>0){
                SmallCircleY=SmallCircleY-10;
                if (SmallCircleY<GreatCircleY){
                    SmallCircleY=GreatCircleY;
                }
                jisuanR();
                postInvalidate();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }


}

如想用用在下拉刷新处也是非常简单的,监听到下拉的距离,用此来设置圆心距即可、~

如有问题,或者更好的实现方法也可以分享一下,谢谢~







你可能感兴趣的:(android,自定义组件)