天天看点

(十四)水波纹动画一、效果二、分析三、自定义属性四、onMeasure五、onDraw六、动画七、WaveView八、附

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、效果

(十四)水波纹动画一、效果二、分析三、自定义属性四、onMeasure五、onDraw六、动画七、WaveView八、附

二、分析

这个水波纹动画主要分为两大块,水波纹跟运动的小船。

(十四)水波纹动画一、效果二、分析三、自定义属性四、onMeasure五、onDraw六、动画七、WaveView八、附

1.水波纹

1.水波纹采用正余弦函数的图像,当进行绘制的时候,图中水平红线部分的一个波长分为两个贝塞尔曲线进行绘制。重复直至充满手机屏幕。

2.往屏幕最左边超出多画一个波长。动画执行的时候整个绘制图形不停的往右运动,当左边超出屏幕的波长整个进入屏幕时候,重新执行。

2.小船

小船随水波纹上下移动,我们可以在竖直方向上去一个很小的矩形区域(竖直红线),取这个矩形区域与水波纹的相交区域,把小船绘制的这个区域的最高点即可。(小船中心跟水波纹会有偏差,但是很小,基本看不出来)

三、自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="WaveView">
        <!-- 波长(即一个波峰的占x的长度) -->
        <attr name="waveLength"  format="dimension"/>
        <!-- 波峰的高度 -->
        <attr name="waveHeight" format="dimension" />
        <!-- 水位的初始位置 -->
        <attr name="originY" format="dimension" />
        <!-- 水涨的快慢 -->
        <attr name="duration" format="dimension" />
        <!-- 小船的图片 -->
        <attr name="boatBitmap" format="reference" />
    </declare-styleable>
</resources>
           
private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
        waveLength = (int) typeArray.getDimension(R.styleable.WaveView_waveLength, );
        waveHeight = (int) typeArray.getDimension(R.styleable.WaveView_waveHeight, );
        originY = (int) typeArray.getDimension(R.styleable.WaveView_originY, );
        duration = (int) typeArray.getDimension(R.styleable.WaveView_duration, );
        waveView_boatBitmap = (int) typeArray.getDimension(R.styleable.WaveView_boatBitmap, );
        typeArray.recycle();

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = ;       // 缩放图片
        if(waveView_boatBitmap>){
            mBitmap = BitmapFactory.decodeResource(getResources(), waveView_boatBitmap, options);
        }else{
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg, options);
        }
    }
           

图片的缩放这边是随意采用了2,严谨的话因根据比例进行计算,然后进行缩放。

四、onMeasure

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(width, height);

        //没有设置水位初始位置的时候,从最底部开始
        if(originY == ){
            originY = height;
        }
    }
           

onMeasure 方法主要是进行宽高的测量与记录。另外是对水位的初始位置 originY 进行赋值,当 xml 中没有对水位位置属性进行设置时候,水位应该处于屏幕底部,即 originY 为屏幕高度。可是,当初始化属性的时候,onMeasure 方法尚未执行,屏幕高度也未可知,所以只能在这边对 originY 重新进行赋值。

五、onDraw

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

        setPathData();
        canvas.drawPath(path, paint);

        Rect bounds = region.getBounds();
        canvas.drawBitmap(mBitmap, bounds.left - mBitmap.getWidth()/, bounds.top - mBitmap.getHeight()/, paint);
    }

    //设置波浪路径
    private void setPathData() {
        //每次获取 Path 前需要重置
        path.reset();
        //往左边多画一个波长,否则动画效果会导致部分空白
        path.moveTo(-waveLength + dx, originY- dy);

        //水平方向从 -waveLength + dx 开始画知道画满整个屏幕宽度
        for (int i = -waveLength; i < width; i += waveLength) {
            //一个波长分上半弧与下半弧
            //画上半弧
            path.rQuadTo(waveLength/, waveHeight, waveLength/, );
            //画下半弧
            path.rQuadTo(waveLength/, -waveHeight, waveLength/, );

        }
        path.lineTo(width, height);
        path.lineTo(, height);
        path.close();

        float x = width/;
        region = new Region();
        Region clip = new Region((int)(x-), , (int)x, height*);
        //用一个矩形区域去切割一个path路径得到一个矩形区域
        region.setPath(path, clip);

    }
           

onDraw 方法每次都要重新获取水波纹的路径,水平初始位置计算上 dx,动画只需要改变 dx 的值,即可实现水波纹的水平移动;竖直方向计算上 dy;则可以实现水位的上升。

小船的位置确认,取一个宽度很小(0.1)的矩形与水波纹区域相交,取相交区域的最左上方点作为小船的中心进行绘制。(这样小船的中心实际与水波纹路径有所偏差,但是基本感觉不出来)

六、动画

public void startAnimation() {
        //dx不断地增加
        ValueAnimator animator = ValueAnimator.ofFloat(,);
        animator.setDuration();
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = (float) animation.getAnimatedValue();
                dx = (int) (waveLength*fraction);
                dy += duration;
                postInvalidate();
            }
        });
        animator.start();
    }
           

动画这边先不解释,后面再说。

七、WaveView

完整 WaveView 代码。

public class WaveView extends View {

    //波长
    private int waveLength;
    //波峰的高度
    private int waveHeight;
    //小船图片的id
    private int waveView_boatBitmap;
    //水位的初始位置
    private int duration;
    //水位上涨速度
    private int originY;

    //波浪画笔
    private Paint paint;
    //波浪路径
    private Path path;
    //自定义控件的宽高
    private int width;
    private int height;
    //x 和 y 的偏移量
    private int dx;
    private int dy;
    //小船图片
    private Bitmap mBitmap;

    //小船位置获取辅助区域
    private Region region;

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

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

    public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, , defStyleAttr);
    }

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

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
        waveLength = (int) typeArray.getDimension(R.styleable.WaveView_waveLength, );
        waveHeight = (int) typeArray.getDimension(R.styleable.WaveView_waveHeight, );
        originY = (int) typeArray.getDimension(R.styleable.WaveView_originY, );
        duration = (int) typeArray.getDimension(R.styleable.WaveView_duration, );
        waveView_boatBitmap = (int) typeArray.getDimension(R.styleable.WaveView_boatBitmap, );
        typeArray.recycle();

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = ;       // 缩放图片
        if(waveView_boatBitmap>){
            mBitmap = BitmapFactory.decodeResource(getResources(), waveView_boatBitmap, options);
        }else{
            mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg, options);
        }
    }

    private void init() {
        paint = new Paint();
        paint.setColor(getResources().getColor(R.color.waterColor));
        paint.setStyle(Paint.Style.FILL_AND_STROKE);

        path = new Path();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(width, height);

        //没有设置水位初始位置的时候,从最底部开始
        if(originY == ){
            originY = height;
        }
    }

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

        setPathData();
        canvas.drawPath(path, paint);

        Rect bounds = region.getBounds();
        canvas.drawBitmap(mBitmap, bounds.left - mBitmap.getWidth()/, bounds.top - mBitmap.getHeight()/, paint);
    }

    //设置波浪路径
    private void setPathData() {
        //每次获取 Path 前需要重置
        path.reset();
        //往左边多画一个波长,否则动画效果会导致部分空白
        path.moveTo(-waveLength + dx, originY - dy);

        //水平方向从 -waveLength + dx 开始画知道画满整个屏幕宽度
        for (int i = -waveLength; i < width; i += waveLength) {
            //一个波长分上半弧与下半弧
            //画上半弧
            path.rQuadTo(waveLength/, waveHeight, waveLength/, );
            //画下半弧
            path.rQuadTo(waveLength/, -waveHeight, waveLength/, );

        }
        path.lineTo(width, height);
        path.lineTo(, height);
        path.close();

        float x = width/;
        region = new Region();
        Region clip = new Region((int)(x-), , (int)x, height*);
        //用一个矩形区域去切割一个path路径得到一个矩形区域
        region.setPath(path, clip);

    }

    public void startAnimation() {
        //dx不断地增加
        ValueAnimator animator = ValueAnimator.ofFloat(,);
        animator.setDuration();
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = (float) animation.getAnimatedValue();
                dx = (int) (waveLength*fraction);
                dy += duration;
                postInvalidate();
            }
        });
        animator.start();
    }
}
           

八、附

代码连接:http://download.csdn.net/download/qq_18983205/9931662

后一节对小船效果进行优化,是小船的船头随波浪上下起伏:

(十四)水波纹动画一、效果二、分析三、自定义属性四、onMeasure五、onDraw六、动画七、WaveView八、附