android 小船在 水波上移动

最近有这么个需求看图如下:


要求船动 ,波动,船随波动

实际实现效果如下,勉强通过



分析解决方案:

1:做成帧动画,发现切了200多张图,我也是醉了,为了避免OOM,崩溃的原因,舍弃,再说图片将近40M

2:做成MP4,播放视频发现,小了许多8M,呵呵,但是依旧很大,再尝试的过程中,避免不了视频初始化加载会黑一下,或闪一下的问题,舍弃

3:最后只能采用绘制

于是网上各种搜索,各种学习开始了:

1:先实现一条波,并让其波动

package test.vko.cn.ttapplication.weight;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import test.vko.cn.ttapplication.R;

/**
 * Created by JiaRH on 2015/11/19.
 */
public class ShipWaveView extends View {
    Paint mPaint;
    private int color = Color.parseColor("#50378DCC");

    private int viewWidth;
    private int viewHeight;
    private  int WAVE_HEIGHT = 40;
    private List<PointF> mPointsList;
    private int mWaveWidth = 350;
    private int mLevelLine;
    private Path mWavePath;
    private boolean isMeasured;
    /**
     * 记录当前运动点的位置
     */
    private float[] mCurrentPosition = new float[2];
    private PathMeasure mPathMeature;
    Bitmap boat;
    ValueAnimator valueAnimator;
    private int speed = 3;
    private int moveLen = 0;

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

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

    public ShipWaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initData(context);
        initPaint();

    }

    private void initData(Context context) {

        mPointsList = new ArrayList<>();
        boat = BitmapFactory.decodeResource(getResources(), R.drawable.boat1);
    }

    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mPaint.setStrokeWidth(1.5f);
        mPaint.setStyle(Paint.Style.FILL);
        mWavePath = new Path();

    }



    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        int withSize = MeasureSpec.getSize(widthMeasureSpec);
//        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//        int withMode = MeasureSpec.getMode(widthMeasureSpec);
//        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//        if(withMode==MeasureSpec.AT_MOST&&heightMode==MeasureSpec.AT_MOST){
//            withSize = getMeasuredWidth();
//        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mWavePath.reset();
        mWavePath.moveTo(mPointsList.get(0).x, mPointsList.get(0).y);
        for (int i = 0; i < mPointsList.size() - 2; i = i + 2) {
            mWavePath.quadTo(mPointsList.get(i + 1).x,
                    mPointsList.get(i + 1).y, mPointsList.get(i + 2)
                            .x, mPointsList.get(i + 2).y);
        }

        mWavePath.lineTo(viewWidth, 0);
        mWavePath.lineTo(0, 0);
        mWavePath.close();
        mPathMeature = new PathMeasure(mWavePath, true);
        drawPath(canvas);

    }

    private void drawPath(Canvas canvas) {
        mPaint.setColor(color);
//        mPaint.setColor(Color.TRANSPARENT);
        // mPaint的Style是FILL,会填充整个Path区域
        canvas.drawPath(mWavePath, mPaint);
    }




    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        viewWidth = getWidth();
        viewHeight = getHeight();
        mLevelLine = viewHeight/2;
        if (!isMeasured) {

            isMeasured = true;
            int n = (int) Math.round(viewWidth / mWaveWidth + 0.5);
//            n=1;
            // n个波形需要4n+1个点,但是我们要预留一个波形在左边隐藏区域,所以需要4n+5个点
            for (int i = 0; i < (4 * n + 5 + 4); i++) {
                // 从P0开始初始化到P4n+4,总共4n+5个点
                float x = i * mWaveWidth / 4 - mWaveWidth;
//                float x = i * mWaveWidth / 4;
                float y = 0;
                switch (i % 4) {
                    case 0:
                    case 2:
                        // 零点位于水位线上
                        y = mLevelLine;
                        break;
                    case 1:
                        // 往下波动的控制点
                        y = mLevelLine + WAVE_HEIGHT;
                        break;
                    case 3:
                        // 往上波动的控制点
                        y = mLevelLine - WAVE_HEIGHT;
                        break;
                }
                mPointsList.add(new PointF(x, y));

            }
        }
    }

    public void startMove() {
        ValueAnimator moveValue = ValueAnimator.ofInt(0, mWaveWidth);
        moveValue.setDuration(10000);
        moveValue.setRepeatCount(-1);
        moveValue.setRepeatMode(ValueAnimator.RESTART);
        moveValue.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                moveLen += speed;

                if (Math.abs(moveLen) >= mWaveWidth - 1) {
                    resetPoints();

                }

                movePoints();
                invalidate();
            }

            private void movePoints() {
                for (int i = 0; i < mPointsList.size(); i++) {
                    mPointsList.get(i).x += speed;

                }
            }

            private void resetPoints() {
                moveLen = 0;
                for (int i = 0; i < mPointsList.size(); i++) {
                    mPointsList.get(i).x = (i * mWaveWidth / 4 - mWaveWidth);

                }
            }
        });
        moveValue.start();
    }

    public void setColor(int color) {
        this.color = color;
    }

    public void setmWaveWidth(int mWaveWidth) {
        this.mWaveWidth = mWaveWidth;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public void setWAVE_HEIGHT(int WAVE_HEIGHT) {
        this.WAVE_HEIGHT = WAVE_HEIGHT;
    }

}


2:更具需要添加不同数量的波进来,可以设置波速,振幅,波长,颜色

package test.vko.cn.ttapplication.weight;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.FrameLayout;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by JiaRH on 2015/11/23.
 */
public class WaveMoveView extends FrameLayout {

    private List<ShipWaveView> shipWaveViews;
    private ShipWaveView swv0,swv1,swv2;
    private ShipWaveViewSecond boatView;

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

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


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


    private void init(Context context) {

        shipWaveViews = new ArrayList<>();
        swv0 = new ShipWaveView(context);
        swv1 = new ShipWaveView(context);
        swv2 = new ShipWaveView(context);

        boatView = new ShipWaveViewSecond(context);
        swv0.setSpeed(3);
        swv1.setSpeed(5);
        swv2.setSpeed(7);

        swv0.setColor(Color.parseColor("#8800a9ff"));
        swv1.setColor(Color.parseColor("#8800a9ff"));
        swv2.setColor(Color.parseColor("#10a1ea"));

        swv0.setWAVE_HEIGHT(40);
        swv1.setWAVE_HEIGHT(35);
        swv2.setWAVE_HEIGHT(50);

        swv0.setmWaveWidth(550);
        swv1.setmWaveWidth(600);
        swv2.setmWaveWidth(700);

//        swv0.startBallAni();
        shipWaveViews.add(swv0);
        shipWaveViews.add(swv1);
        shipWaveViews.add(swv2);
       for(ShipWaveView swv : shipWaveViews){
           this.addView(swv);
           swv.startMove();
       }
        this.addView(boatView);
    }
}


3:绘制小船的路径

4:让小船动起来

package test.vko.cn.ttapplication.weight;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

import java.util.ArrayList;
import java.util.List;

import test.vko.cn.ttapplication.R;

/**
 * Created by JiaRH on 2015/11/19.
 */
public class ShipWaveViewSecond extends View {
    Paint mPaint;
    private int color = Color.parseColor("#378DCC");

    private int viewWidth;
    private int viewHeight;
    private final int WAVE_HEIGHT = 20;
    private List<PointF> mPointsList;
    private int mWaveWidth = 300;
    private int mLevelLine;
    private Path mWavePath;
    private boolean isMeasured;
    /**
     * 记录当前运动点的位置
     */
    private float[] mCurrentPosition = new float[2];
    private PathMeasure mPathMeature;
    Bitmap boat;
    ValueAnimator valueAnimator;
    private int speed = 2;
    private int moveLen = 0;

    private float currentCircleX, currentCircleY;
    private float radius = 5;
    ValueAnimator circleValue;

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

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

    public ShipWaveViewSecond(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initData(context);
        initPaint();
        postDelayed(new Runnable() {
            @Override
            public void run() {

                startBallAni();


            }
        }, 300);
    }

    private void initData(Context context) {

        mPointsList = new ArrayList<>();
        boat = BitmapFactory.decodeResource(getResources(), R.drawable.boat2);
    }

    private void initPaint() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mPaint.setStrokeWidth(1.5f);
        mPaint.setStyle(Paint.Style.STROKE);
        mWavePath = new Path();

    }

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


    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mWavePath.reset();
        mWavePath.moveTo(mPointsList.get(0).x, mPointsList.get(0).y);
        for (int i = 0; i < mPointsList.size() - 2; i = i + 2) {
            mWavePath.quadTo(mPointsList.get(i + 1).x,
                    mPointsList.get(i + 1).y, mPointsList.get(i + 2)
                            .x, mPointsList.get(i + 2).y);
        }

//        mWavePath.lineTo(viewWidth, 0);
//        mWavePath.lineTo(0, 0);
//        mWavePath.close();
        mPathMeature = new PathMeasure(mWavePath, true);
        drawPath(canvas);
        drawBoat(canvas);

    }



    private void drawPath(Canvas canvas) {
        mPaint.setColor(Color.TRANSPARENT);
        // mPaint的Style是FILL,会填充整个Path区域
        canvas.drawPath(mWavePath, mPaint);
    }

    private void drawBoat(Canvas canvas) {
        mPaint.setColor(Color.BLUE);
        if (mCurrentPosition[0] > viewWidth) {
            mCurrentPosition[0] = viewWidth;
            resetAni();
        }
        canvas.drawBitmap(boat, mCurrentPosition[0], mCurrentPosition[1], mPaint);
    }

    private void resetAni() {
//
        if (valueAnimator==null)return;
        if (valueAnimator.isRunning()) {
            valueAnimator.cancel();
            startBallAni();
        }

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        viewWidth = getWidth();
        viewHeight = getHeight();
        mLevelLine = viewHeight/2 - 110;
        currentCircleX = (float) Math.floor(Math.random() * viewWidth);
        currentCircleY = mLevelLine;
        if (!isMeasured) {

            isMeasured = true;
            int n = (int) Math.round(viewWidth / mWaveWidth + 0.5);
//            n=1;
            // n个波形需要4n+1个点,
            for (int i = 0; i < (4 * n)+5; i++) {
                // 从P0开始初始化到P4n+4,总共4n+5个点
//                float x = i * mWaveWidth / 4 - mWaveWidth;
                float x = i * mWaveWidth / 4-boat.getWidth();
                float y = 0;
                switch (i % 4) {
                    case 0:
                    case 2:
                        // 零点位于水位线上
                        y = mLevelLine;
                        break;
                    case 1:
                        // 往下波动的控制点
                        y = mLevelLine + WAVE_HEIGHT;
                        break;
                    case 3:
                        // 往上波动的控制点
                        y = mLevelLine - WAVE_HEIGHT;
                        break;
                }
                mPointsList.add(new PointF(x, y));
                /**
                 * 初始化小球的位置
                 */
                mCurrentPosition[0] = mPointsList.get(0).x;
                mCurrentPosition[1] = mPointsList.get(0).y;
            }
        }
    }


    /**
     * 开始小球的运动
     */
    private void startBallAni() {
        if (mPathMeature==null)return;
        valueAnimator = ValueAnimator.ofFloat(0, mPathMeature.getLength());
        valueAnimator.setDuration(25000);
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.setRepeatCount(-1);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                mPathMeature.getPosTan(value, mCurrentPosition, null);
                postInvalidate();
            }
        });
        valueAnimator.start();
    }
}
5:使用方法:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="test.vko.cn.ttapplication.activitys.ShipWaveActivity">
<ImageView
    android:id="@+id/bgg"
    android:layout_alignParentTop="true"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:scaleType="fitStart"
    android:background="@drawable/bgg"/>
    <test.vko.cn.ttapplication.weight.WaveMoveView
        android:id="@+id/waveView"
        android:layout_alignBottom="@+id/bgg"
        android:layout_width="fill_parent"
        android:layout_height="90dp" />
</LinearLayout>



感谢如下文章及其作者:

参考文章:

http://blog.csdn.net/zhongkejingwang/article/details/38556891(分析的很NB)

http://blog.csdn.net/tianjian4592/article/details/44222565




你可能感兴趣的:(android,动画,波浪)