android 小船在 水波上移动
来源:互联网 发布:spark sql mongodb 编辑:程序博客网 时间:2024/04/29 15:57
最近有这么个需求看图如下:
要求船动 ,波动,船随波动
实际实现效果如下,勉强通过
分析解决方案:
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
0 0
- android 小船在 水波上移动
- 图片在网页上移动
- 在窗体上移动控件
- 在窗体上移动控件
- 在指定方向(角度)上移动
- 点P在椭圆上移动
- 视图在画出得路径上移动
- 使用鼠标在面板上移动消息
- Canvas坦克在画布上移动
- 在面板上移动的字
- android软键盘上移动焦点
- android 软键盘弹出 布局上移动
- Android应用程序入门 推箱子游戏开发(二) 键盘事件监听 角色在屏幕上移动
- 经纬度随鼠标在地图上移动随时显示
- 鼠标在菜单上移动时显示菜单提示
- 经纬度随鼠标在地图上移动随时显示
- UIImageView动画:显示了something在屏幕上移动。
- 北航1280--在棋盘上移动--动态规划习题
- 快速排序
- jquery datatable
- jstl标签的<c:foreach>用法
- 咚咚智能音响或将抢占智能家居第一入口
- 【java并发编程】——JAVA CAS
- android 小船在 水波上移动
- Android APP 语言国际化
- appium简明教程(2)——appium的基本概念
- momgodb的认证和授权
- 安装windows操作系统基本方法
- ORACLE绑定变量的使用
- 自学宝典:10个学习Android开发的网站推荐
- ubuntu环境安装 eclipse
- 盘点:10款适合创业团队使用的团队协作工具