实现树叶飘落的一个自定义view 实际项目中几乎没有这个需求,但是做出来可以学习进步,有兴趣的童鞋可以看看。
先看图吧:
(可能需要少许时间加载,稍等..)
就是这么一个效果。
主要涉及到canvas、paint、bitmap和matrix的使用,这几个类也是自定义view的时候高频率用到的工具。所以熟练掌握这几个类的使用对自定义控件功力的提升是有很大帮助的。
我们从头来理一下实现的过程。
首先,画一片叶子:(叶子资源:)
主要方法就一个:canvas.drawBitmap(leafBitmap, matrix, bitmapPaint);
三个参数:
leafBitmap 通过资源图片获取bitmap (BitmapFactory.decodeResource(context.getResources(), R.drawable.leaf) )
matrix负责位置和角度的变化
bitmapPaint 为画笔Paint的一个实例化对象
搞清楚这个方法过后,调用一下,一片叶子就绘到屏幕上了。
画一片叶子很简单!
然后,当要画很多叶子时,由于每片叶子的位置和角度是不断变化的,我们创建一个类来记录每片叶子自身的位置角度信息。
<span style="font-size:14px;">private class leafPosition {
public int x = 0; //位置x坐标信息
public int y = 0; //位置y坐标信息
public int roate = 0; //角度信息
public int roateChange = 0; //角度变化值
public leafPosition(int x, int y, int roate) {
this.x = x;
this.y = y;
this.roate = roate;
}
}</span>
然后,要同时画很多片叶子,我们需要一个数据容器,这里就用ArrayList来承担这个角色吧。所以new一个。
<span style="font-size:14px;">List leafs = ArrayList<LeafFallingView.leafPosition>();</span>
然后是树叶的产生和变化:
树叶产生时,我们通过当前世界时间和随机数为参照来随机确定它在屏幕的位置和角度,这样,每一片树叶产生的时候就随机的在屏幕不同的位置出现了。
float r = random.nextFloat();
// 根据当前时间随机创建 位置、旋转角度不同的叶子
leafs.add(new leafPosition((int) (((r * 1000 % 100f) / 100f) * viewWidth), 0, (int) nowTime % 360));
这个过程中有一个重要的角色就是matrix,这里主要是平移和旋转的效果,对应两个方法的使用:
matrix.postTranslate(x, y);
matrix.postRotate(roate % 360, nowX + leafWidth / 2, nowY + leafHeight / 2);
关于matrix矩阵的详细原理和介绍可以去度娘或者google一下,按矩阵来乘之类的,很详细,也很晕(线性代数早还给大学老师了)。
这里用到的两个方法,一个平移,一个旋转。postTranslate的参数即是x和y方向的平移距离,postRotate的参数分别是旋转角度,和旋转中心的x、y坐标。这里取叶子的中心为旋转中心。
然后就执行主要流程,先逐渐添加树叶,然后不停地设置所有树叶的位置信息,不停地重绘view就实现了最终的效果了。
最后,可以给自己配一个喜欢的背景颜色或者图片,就大功告成。
贴上代码:
package com.cc.test.view;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.cc.test.R;
/**
* 自定义落叶view
* @author zhangyu
* @date 2016-4-1
* @描述:
*/
public class LeafFallingView extends View {
private static final String TAG = "LeafFallingView";
private Context context;
private Bitmap leafBitmap;
private Paint bitmapPaint, orangePaint;
private int viewWidth, viewHeight, leafWidth, leafHeight;
private Handler handler;
private List<leafPosition> leafs;
private Random random = new Random(333);
public LeafFallingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public LeafFallingView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LeafFallingView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
this.context = context;
leafBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.leaf);
leafWidth = leafBitmap.getWidth();
leafHeight = leafBitmap.getHeight();
initPaint();
handler = new Handler();
leafs = new ArrayList<LeafFallingView.leafPosition>();
setParams();
}
private void initPaint() {
bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setDither(true);
bitmapPaint.setFilterBitmap(true);
orangePaint = new Paint();
orangePaint.setAntiAlias(true);
orangePaint.setColor(Color.parseColor("#FCE59D"));
}
@Override
protected void onDraw(Canvas canvas) {
drawOrangeBackground(canvas);
drawLeaf(canvas);
super.onDraw(canvas);
}
/**
* 画叶子
*
* @param canvas
*/
private void drawLeaf(Canvas canvas) {
for (int i = 0; i < leafs.size(); i++) {
leafPosition leaf = leafs.get(i);
int x = leaf.x;
int y = leaf.y;
int roate = leaf.roate;
int nowX = 0, nowY = 0;
canvas.save();
Matrix matrix = new Matrix();
matrix.postTranslate(x, y);
nowX = nowX + x;
nowY = nowY + y;
matrix.postRotate(roate % 360, nowX + leafWidth / 2, nowY + leafHeight / 2);
canvas.drawBitmap(leafBitmap, matrix, bitmapPaint);
canvas.restore();
}
}
/**
* 画背景
*
* @param canvas
*/
private void drawOrangeBackground(Canvas canvas) {
canvas.drawRect(0, 0, viewWidth, viewHeight, orangePaint);
}
private int setCount = 0; //记录设置次数
/**
* 设置和改变叶子位置和角度参数
*/
public void setParams() {
Log.d(TAG, "setParams.. setCount = " + setCount);
setCount++;
handler.postDelayed(new Runnable() {
public void run() {
long nowTime = System.currentTimeMillis();
nowTime = (nowTime + setCount) / 3;
if (setCount % 7 == 0) {
float r = random.nextFloat();
Log.d(TAG, "r = " + r + ",((r % 100f) / 100f) = " + ((r % 100f) / 100f));
// 根据当前时间随机创建 位置、旋转角度不同的叶子
leafs.add(new leafPosition((int) (((r * 1000 % 100f) / 100f) * viewWidth), 0, (int) nowTime % 360));
}
if (leafs.size() > 300)
leafs.remove(0);
Log.d(TAG, "leafs.size = "+ leafs.size());
for (int i = 0; i < leafs.size(); i++) {
leafPosition leaf = leafs.get(i);
leaf.y += 3;//y方向每次移动3个像素
//根据叶子在集合中的排序位置来产生一个旋转角度(大小方向不完全相同),并且记录下这个角度下次继续执行,保证叶子转向一致,不来回摆动
if(leaf.roateChange == 0)
leaf.roateChange = getRoate(i);
leaf.roate += leaf.roateChange;
}
invalidate();
if (setCount <= 10000) {// 设置10000次,当然也可以不设置次数无限递归下去
setParams();
} else
setCount = 0;
}
}, 20);
}
/**
* 返回几种旋转的值
* @param nowTime
* @return
*/
protected int getRoate(long nowTime) {
switch ((int) (nowTime % 5)) {
case 0:
return 3;
case 1:
return -3;
case 2:
return 4;
case 3:
return -5;
case 4:
return -4;
default:
return 3;
}
}
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
viewWidth = getWidth();
viewHeight = getHeight();
invalidate();
super.onWindowFocusChanged(hasWindowFocus);
}
/**
* 叶子位置
*
* @author zhangyu
* @date 2016-3-28
* @描述:
*/
private class leafPosition {
public int x = 0; //位置x坐标信息
public int y = 0; //位置y坐标信息
public int roate = 0; //角度信息
public int roateChange = 0; //角度变化值
public leafPosition(int x, int y, int roate) {
this.x = x;
this.y = y;
this.roate = roate;
}
}
}
代码不多,也不算太复杂,希望对需要的童鞋有用,有问题还请大家多多指正!