最近有这么个需求看图如下:
要求船动 ,波动,船随波动
实际实现效果如下,勉强通过
分析解决方案:
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