1

在Android的一些app中,经常有一些水波纹的加载效果,非常好看,比如下面的为知笔记的加载效果就是非常的好看~~

为知笔记

还有一些加速球等等中的波浪效果也是类似。那么应该怎么实现这种效果呢?我们从效果上看,这个效果大概可以分为两个难点。

  • 绘制波浪图形

  • 让波浪随时间荡漾起来

1.分析

先说第一个,如何绘制波浪图形。绘制波浪图形的方法有很多,这里介绍一种最简单的方法,就是使用canvas的drawLine方法绘制直线。我们可以将view的宽度转化成弧度,通过sin或者cos方法获取每个像素点的坐标,通过drawline绘制一条从顶点到底部的直线,这样都可以达到效果。

再说第二个,这个是一个难点,但是实现起来却很简单。我们获取的坐标是从0-360度的坐标,也就是说,最后一个的坐标接下来的坐标应该是第一的坐标,它们是一个连续的没有断开的坐标系列,那么我们只需要将坐标按照一定的规则交换一下位置就可以实现波浪的荡漾效果了。

2.关键代码

计算波浪上所有点的坐标可以使用如下公式:y = A×sin(x × Φ + offset) + H,A代表了波浪的振幅,x代表了当前view的宽度的位置,Φ代表了2 Math.PI / width,H代表了Y轴上的基本高度。

    //计算Y轴的坐标
    private float getYPosition(int x, int swing, int offset, int baseHeight) {
        float cycle = (float) (2 * Math.PI) / mWidth;
        return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
    }

偏移坐标的方法也很简单,根据响应的步长设置每次的偏移起始点

    private void changeRestorePosition() {
        if (mWidth != 0) {
            mPosition1 = (mPosition1 + STEP1) % mWidth;
            System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
            System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);

            mPosition2 = (mPosition2 + STEP2) % mWidth;
            System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
            System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
        }
    }

完整代码

package com.app.motion.wavemotion.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by joe.wang on 2016/10/27.
 */

public class WaveView extends View {

    private final int INIT_BASE_HEIGHT1 = 300;
    private final int INIT_BASE_HEIGHT2 = 300;
    private int mHeight;
    private int mWidth;
    private float[] mContentOneYs = null;
    private float[] mContentTwoys = null;
    private float[] mRestoreOnes = null;
    private float[] mRestoreTwos = null;
    private static final int SWINGONE = 40;
    private static final int SWINGTWO = 80;
    private static final int OFFSETONE = 0;
    private static final int OFFSETTWO = 40;
    private int mPosition1 = 0;
    private int mPosition2 = 0;
    private static final int STEP1 = 5;
    private static final int STEP2 = 8;
    private Paint mPaint1;
    private Paint mPaint2;


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

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

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

    private void init() {
        mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint1.setColor(Color.parseColor("#AB9DCF"));
        mPaint1.setStrokeWidth(4);
        mPaint1.setAlpha(155);

        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setColor(Color.parseColor("#A2D1F3"));
        mPaint2.setStrokeWidth(4);
        mPaint2.setAlpha(155);
    }


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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (w != 0 || h != 0 || w != oldw || h != oldh) {
            mWidth = w;
            mHeight = h;
            calculatePoints();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.save();
        changeRestorePosition();
        for (int i = 0; i < mWidth; i++) {
            final int x = i;
            final float y1 = mRestoreOnes[i];
            final float y2 = mRestoreTwos[i];
            canvas.drawLine(x, y2, x, mHeight, mPaint2);
            canvas.drawLine(x, y1, x, mHeight, mPaint1);
        }
        invalidate();
    }

    private void calculatePoints() {
        mContentOneYs = new float[mWidth];
        mContentTwoys = new float[mWidth];
        mRestoreOnes = new float[mWidth];
        mRestoreTwos = new float[mWidth];
        for (int i = 0; i < mWidth; i++) {
            mContentOneYs[i] = getYPosition(i, SWINGONE, OFFSETONE, INIT_BASE_HEIGHT1);
            mContentTwoys[i] = getYPosition(i, SWINGTWO, OFFSETTWO, INIT_BASE_HEIGHT2);
        }
    }


    private void changeRestorePosition() {
        if (mWidth != 0) {
            mPosition1 = (mPosition1 + STEP1) % mWidth;
            System.arraycopy(mContentOneYs, mPosition1, mRestoreOnes, 0, mWidth - mPosition1);
            System.arraycopy(mContentOneYs, 0, mRestoreOnes, mWidth - mPosition1, mPosition1);

            mPosition2 = (mPosition2 + STEP2) % mWidth;
            System.arraycopy(mContentTwoys, mPosition2, mRestoreTwos, 0, mWidth - mPosition2);
            System.arraycopy(mContentTwoys, 0, mRestoreTwos, mWidth - mPosition2, mPosition2);
        }
    }

    private float getYPosition(int x, int swing, int offset, int baseHeight) {
        float cycle = (float) (2 * Math.PI) / mWidth;
        return (float) Math.sin(cycle * x + offset) * swing + baseHeight;
    }

}

实现效果

最终效果


summerpxy
44 声望1 粉丝

引用和评论

0 条评论