码迷,mamicode.com
首页 > 其他好文 > 详细

三角函数之美-水波纹加载LoadingView

时间:2016-04-02 22:59:22      阅读:988      评论:0      收藏:0      [点我收藏+]

标签:

一、前言

       学习是要总结的,最近几天学习了绘图相关的,但是使用的机会较少,现在又快要遗忘了,这次看了水波纹的绘制,觉得十分有意思,还是 把实现的方法记录下来。技术无他,为手熟尔,还是要多练习,空淡误国,实干兴邦,让我们看看今天的三角函数之美吧。

二、概述

       肯定大家对中学学习的三角函数都不陌生吧,不过学习的sin、cos是超越函数一类函数,是初等函数的一种,借用维基百科的一张图:

       技术分享
        一个完整的正弦函数应该是这样的:>y=Asin(ωx+φ)+h,A决定峰值,ω决定周期,φ表示初相位,h表示y轴的位置。在Android我们使用一般是Math.sin(30*Math.PI/180)这种形式,我原来搞不懂这样写的含义,后来才明白Math.PI就是π,π即180度,所以上述式子相当于sin(π/6)=1/2,在中学我们一般是sin(30°)=1/2,把sin(x)中的x赋值为角度,而在计算机语言x通常是弧度。

技术分享
这个流量界面就是通过sin函数曲线不断改变x位置绘制而来,实现的方式肯定也还有其它的方法,通过绘制贝塞尔曲线也有实现的案例,我们将通过例子逐渐对水波纹loadingview绘制,好了不多说了,手把手带你一步一步实现漂亮炫酷的loading水波纹效果。

三、波浪控件的实现

首先我们先实现波浪控件的绘制:
先计算坐标点:

 //这里我们以view的总宽度为周期,y = a * sin(2π) + b
        for (int i = 0; i < mTotalWidth; i++){
            mPointY[i] = (float) (20 * (Math.sin( 2 * Math.PI * i / w)));
        }

绘制曲线

for (int i = 0;i < mTotalWidth; i++){
            canvas.drawLine(i,mTotalHeight - mDaymicPointY[i] - 300,i,mTotalHeight,mPaint);
        }

这里减去300只是为了控制在Y轴上的位置,我们可以动态设置一个数值,在view上下移动,从而达到流量界面,我们先一步一步来。
控制移动

 //改变两条波纹的移动点
        mXoffset += X_SPEED;
        //如果已经移动到末尾处,则到头重新移动
        if(mXoffset > mTotalWidth){
            mXoffset = 0;
        }
 // 超出屏幕的挪到前面,mXoffset表示第一条水波纹要移动的距离
        int yIntelrval = mPointY.length - mXoffset;
        //使用System.arraycopy方式重新填充第一条波纹的数据
        System.arraycopy(mPointY, 0, mDaymicPointY, mXoffset, yIntelrval);
        System.arraycopy(mPointY, yIntelrval, mDaymicPointY, 0, mXoffset);

现在你会发现一个简单的水波纹效果出来了,如下图所示:
技术分享
你会发现这个水波纹跟我们的效果还是有些差距,别急,我们还有这个类PorterDuffXfermode,可以实现与所绘制的图像按一定规则进行混合,形成新的像素值,那么我们就可以先画一个水波纹效果,然后在画一个圆,使用下面这幅神图进行混合,你会发现奇迹的,PorterDuffXfermode的相关使用可以参考这边博客:

Android中Canvas绘图之PorterDuffXfermode使用及工作原理详解

技术分享
使用哪种规则呢,自己找找呗,都是进行某种规则进行混合的,我找到了,使用的是SrcIn,就可以进行混合实现效果了
现在就是这种效果,我们在加一个seekbar,拖动显示效果,基本上就可以出现这个波浪loading效果了

        int canvasWidth = canvas.getWidth();
        int canvasHeight = canvas.getHeight();
        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
            canvas.drawCircle(mTotalWidth / 2, mTotalHeight / 2, mTotalWidth / 2, mCriclePaint);
            //设置颜色混合模式
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            //高减去宽除以2使水波纹底部在圆底部,动态改变percent值,在Y轴上变化
            for (int i = 0; i < mTotalWidth; i++) {
                canvas.drawLine(i, mTotalHeight - mDaymicPointY[i] - (mTotalHeight - mTotalWidth) / 2 - percent * mTotalWidth / 100, i, mTotalHeight - (mTotalHeight - mTotalWidth) / 2, mPaint);
            }
            //最后将画笔去除Xfermode
            mPaint.setXfermode(null);
            //改变两条波纹的移动点
            mXoffset += X_SPEED;
            //如果已经移动到末尾处,则到头重新移动
            if (mXoffset > mTotalWidth) {
                mXoffset = 0;
            }
        canvas.restoreToCount(layerId);

效果如下图所示
技术分享

我贴下全部实现过程的代码:

public class WaveView extends View {
    private Paint mPaint, mCriclePaint,mTextPaint;
    // 倾斜或旋转、快速变化,当在屏幕上画一条直线时, 横竖不会出现锯齿,
    // 但是当斜着画时, 就会出现锯齿的效果,所以需要设置抗锯齿
    private DrawFilter mDrawFilter;
    private int mTotalHeight, mTotalWidth;
    private int mXoffset = 0;
    private float[] mPointY;
    private float[] mDaymicPointY;
    //波浪线移动速度
    private static final int X_SPEED = 20;
    private int percent;

    public void setPercent(int percent) {
        this.percent = percent;
    }

    public WaveView(Context context) {
        super(context);
        init();
    }

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

    public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        //图片线条(通用)的抗锯齿需要另外设置
        mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        //实例化一个画笔
        mPaint = new Paint();
        //去除画笔锯齿
        mPaint.setAntiAlias(true);
        //设置画笔风格为实线
        mPaint.setStyle(Paint.Style.FILL);
        //设置画笔颜色
        mPaint.setColor(Color.GREEN);
        //实例化圆的画笔
        mCriclePaint = new Paint(mPaint);
        mCriclePaint.setColor(Color.parseColor("#88dddddd"));
        mCriclePaint.setAlpha(255);
        //实例化文字画笔
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //去除锯齿
        canvas.setDrawFilter(mDrawFilter);
        runWave();
        int canvasWidth = canvas.getWidth();
        int canvasHeight = canvas.getHeight();
        int layerId = canvas.saveLayer(0, 0, canvasWidth, canvasHeight, null, Canvas.ALL_SAVE_FLAG);
            canvas.drawCircle(mTotalWidth / 2, mTotalHeight / 2, mTotalWidth / 2, mCriclePaint);
            //设置颜色混合模式
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            //高减去宽除以2使水波纹底部在圆底部,动态改变percent值,在Y轴上变化
            for (int i = 0; i < mTotalWidth; i++) {
                canvas.drawLine(i, mTotalHeight - mDaymicPointY[i] - (mTotalHeight - mTotalWidth) / 2 - percent * mTotalWidth / 100, i, mTotalHeight - (mTotalHeight - mTotalWidth) / 2, mPaint);
            }
            //最后将画笔去除Xfermode
            mPaint.setXfermode(null);
            //改变两条波纹的移动点
            mXoffset += X_SPEED;
            //如果已经移动到末尾处,则到头重新移动
            if (mXoffset > mTotalWidth) {
                mXoffset = 0;
            }
        canvas.restoreToCount(layerId);
        String text = percent + "%";
        mTextPaint.setTextSize(80);
        float textLength = mTextPaint.measureText(text);
        canvas.drawText(text,(mTotalWidth - textLength) / 2,mTotalHeight / 2 - 20,mTextPaint);
        //引起view重绘
        postInvalidateDelayed(300);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTotalHeight = h;
        mTotalWidth = w;
        //数组的长度为view的宽度
        mPointY = new float[w];
        mDaymicPointY = new float[w];
        //这里我们以view的总宽度为周期,y = a * sin(2π) + b
        for (int i = 0; i < mTotalWidth; i++) {
            mPointY[i] = (float) (20 * (Math.sin(2 * Math.PI * i / w)));
        }
    }

    private void runWave() {
        // 超出屏幕的挪到前面,mXoffset表示第一条水波纹要移动的距离
        int yIntelrval = mPointY.length - mXoffset;
        //使用System.arraycopy方式重新填充第一条波纹的数据
        System.arraycopy(mPointY, 0, mDaymicPointY, mXoffset, yIntelrval);
        System.arraycopy(mPointY, yIntelrval, mDaymicPointY, 0, mXoffset);
    }
}

上面就是实现的全部代码了,都有注释,相信大家能看懂,其实实现这些效果还是很简单的,但是我们还有许多扩展的地方,做成一个网络加载的等待动画,或者做成一个下载的的进度,等待大家自己去实现。

源码稍后上传

三角函数之美-水波纹加载LoadingView

标签:

原文地址:http://blog.csdn.net/qq_30806949/article/details/51010254

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!