高仿墨迹天气 白天晴天
Posted 吴佳峻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高仿墨迹天气 白天晴天相关的知识,希望对你有一定的参考价值。
简介
一直对墨迹天气的绚丽的场景蛮感兴趣的,趁有时间,自己就高仿了其中的一个场景,其他场景呢,也是类似的,主要是写对象的AI也就是逻辑了。
先看看效果吧,动态效果比较坑,太模糊
高清图
代码分析
来看看代码结构吧
这里使用了SurfaceView而不是用的view,其实这个天气的场景绘制更像是游戏开发,使用SurfaceView会更灵活。
public SceneSurfaceView(Context context, AttributeSet attrs)
super(context, attrs);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
这就是构造方法了,实现
SurfaceHolder.Callback
来监听事件
@Override
public void surfaceCreated(SurfaceHolder holder)
Log.d("weather", "surfaceCreated");
if (renderThread == null)
renderThread = new RenderThread(surfaceHolder, getContext());
renderThread.start();
在
surface
创建回调中, 我们生成了一个
RenderThread
线程来专门做逻辑与绘制。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
width = getMeasuredWidth();
height = getMeasuredHeight();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.d("weather", "onMeasure width=" + width + ",height=" + height);
if (renderThread != null)
renderThread.setWidth(width);
renderThread.setHeight(height);
记录下测量的宽高
@Override
public void surfaceDestroyed(SurfaceHolder holder)
Log.d("weather", "surfaceDestroyed");
renderThread.getRenderHandler().sendEmptyMessage(1);
销毁的时候要发一个消息,具体做什么,下面在来说
下面是RenderThread源码
public class RenderThread extends Thread
private Context context;
private SurfaceHolder surfaceHolder;
private RenderHandler renderHandler;
private Scene scene;
public RenderThread(SurfaceHolder surfaceHolder, Context context)
this.context = context;
this.surfaceHolder = surfaceHolder;
scene = new Scene(context);
//add scene/actor
scene.setBg(BitmapFactory.decodeResource(context.getResources(), R.drawable.bg0_fine_day));
scene.add(new BirdUp(context));
scene.add(new CloudLeft(context));
scene.add(new CloudRight(context));
scene.add(new BirdDown(context));
scene.add(new SunShine(context));
@Override
public void run()
Log.d("weather", "run");
//在非主线程使用消息队列
Looper.prepare();
renderHandler = new RenderHandler();
renderHandler.sendEmptyMessage(0);
Looper.loop();
public RenderHandler getRenderHandler()
return renderHandler;
public class RenderHandler extends Handler
@Override
public void handleMessage(Message msg)
switch (msg.what)
case 0:
if (scene.getWidth() != 0 && scene.getHeight() != 0)
draw();
renderHandler.sendEmptyMessage(0);
break;
case 1:
Looper.myLooper().quit();
break;
private void draw()
Canvas canvas = surfaceHolder.lockCanvas();
if (canvas != null)
scene.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
public void setWidth(int width)
scene.setWidth(width);
public void setHeight(int height)
scene.setHeight(height);
在构造方法中添加了场景背景,上下两个鸟,左右各一个云彩和阳光。
这里在run方法里生成了一个线程的消息队列,注意额不是主线程的,其实也可以在里面搞个while循环,就像一般的游戏处理一样, 但是如果使用消息队列,会更轻巧有效。
大家再来看看RenderHandler
情况分2种,一个是绘制的一个是退出的,基本也就这2种了。
还记得在surfaceDestroyed中调用的退出吧,现在就在这了,呵呵
然后就是最重要的draw方法了
绘制是在Scene中操作的,来看看代码
public class Scene
private Context context;
private int width;
private int height;
private Bitmap bg;
private List<Actor> actors = new ArrayList<Actor>();
private Paint paint;
public Scene(Context context)
this.context = context;
paint = new Paint();
paint.setAntiAlias(true);
public void setBg(Bitmap bg)
this.bg = bg;
public void add(Actor actor)
actors.add(actor);
public void draw(Canvas canvas)
canvas.drawBitmap(bg, new Rect(0, 0, bg.getWidth(), bg.getHeight()), new Rect(0, 0, width, height), paint);
for (Actor actor : actors)
actor.draw(canvas,width,height);
可以在场景中绘制一个背景图和
Actor
列表
Actor是啥呢,就是对象呗,像鸟啊,云啊,雨啊等等吧
public abstract class Actor
protected Context context;
protected Matrix matrix = new Matrix();
protected Actor(Context context)
this.context = context;
public abstract void draw(Canvas canvas, int width, int height);
这是一个抽象类,
Context
可以加载资源文件,
Matrix
来描述对象的变换,抽象方法
draw
就是咱们的逻辑和绘制方法喽
来看看上边的那个小鸟的代码吧
public class BirdUp extends Actor
private static final int[] imgs = new int[]R.drawable.finedayup_1, R.drawable.finedayup_2, R.drawable.finedayup_3, R.drawable.finedayup_4, R.drawable.finedayup_5, R.drawable.finedayup_6, R.drawable.finedayup_7, R.drawable.finedayup_8;
float initPositionX;
float initPositionY;
boolean isInit;
List<Bitmap> frames;
RectF box;
RectF targetBox;
int curFrameIndex;
long lastTime;
Paint paint = new Paint();
protected BirdUp(Context context)
super(context);
frames = new ArrayList<Bitmap>();
box = new RectF();
targetBox = new RectF();
paint.setAntiAlias(true);
@Override
public void draw(Canvas canvas, int width, int height)
//逻辑处理
//初始化
if (!isInit)
initPositionX = width * 0.117F;
initPositionY = height * 0.35F;
matrix.reset();
matrix.postTranslate(initPositionX, initPositionY);
for (int res : imgs)
frames.add(BitmapFactory.decodeResource(context.getResources(), res));
box.set(0, 0, frames.get(0).getWidth(), frames.get(0).getHeight());
isInit = true;
lastTime = System.currentTimeMillis();
return;
//移动
matrix.postTranslate(2, 0);
//边界处理
matrix.mapRect(targetBox, box);
if (targetBox.left > width)
matrix.postTranslate(-targetBox.right, 0);
//取得帧动画图片
long curTime = System.currentTimeMillis();
curFrameIndex = (int) ((curTime - lastTime) / 500 % 8);
Bitmap curBitmap = frames.get(curFrameIndex);
//绘制
canvas.save();
canvas.drawBitmap(curBitmap, matrix, paint);
canvas.restore();
主要逻辑就在
draw
了, 注释写的也比较清除了,先初始化操作,加载资源,设定起始位置,然后就是每帧的移动逻辑,和边界逻辑处理,就是跑到最右边,再把他拉到最左边,呵呵,下面是小鸟动画的处理,我这里是
500
毫秒更换一下图片,也就是说看到的小鸟的动画,其实是隔
500
毫秒更换了一次图片产生的效果,下面就只绘制了,好了
so easy
吧
!
这里要特别说明一个方法matrix.mapRect(targetBox, box);这个方法比较重要,大家以后肯定会经常用到,意思是啥呢,box这个参数是原始的图片大小数据,targetBox是经过Matrix矩阵变换后产生的数据。
好了,下面的鸟其实跟上面的逻辑一样的,只是起始位置不一样。
云彩呢,和小鸟逻辑也差不多,但是需要注意一个地方, 我把云彩给放大了2倍
matrix.reset();
matrix.setScale(2f, 2f);
matrix.mapRect(targetBox, box);
matrix.postTranslate(initPositionX - targetBox.width() / 2, initPositionY - targetBox.height() / 2);
这里初始位置呢, 我也根据放大后的宽和高进行了处理,大家注意啊,先放缩和先设置位置,出来的效果是不一样的。 大家可以自行试试效果。
现在来看看我们的阳光代码吧
这里就贴一些关键代码了, 全部代码可以在我的github上下载
//旋转
matrix.mapRect(targetBox, box);
matrix.postRotate(0.5F, targetBox.centerX(), targetBox.centerY());
//透明度变化
if (alphaUp)
alpha++;
else
alpha--;
if (alpha >= 255)
alphaUp = false;
if (alpha <= 0)
alphaUp = true;
paint.setAlpha(alpha);
//绘制
canvas.drawBitmap(frame, matrix, paint);
主要就是介绍一下,使用矩阵来进行旋转操作和透明度操作怎么来做。这里要注意一下的是旋转的时候,要设置中心点。
结束语
好了,代码说的也差不多啦,知识点也都过了一下。回过头来看看,要实现这么一个效果也不是那么难是吧。 呵呵。 当然了实现的还是比较仓促的,就是简单的一个架子和一个场景,如果感兴趣的话可以添加更多的对象AI逻辑,更多的场景。其实如果做到最后优化好的话应该是这样的一个情况,每个场景一个xml或者其他脚本语言吧,然后解析这个xml来动态生成一个场景。当然也不是很难。真正做天气的话,可以这样做。
Github地址
https://github.com/wu928320442/MojiWeather
以上是关于高仿墨迹天气 白天晴天的主要内容,如果未能解决你的问题,请参考以下文章