效果:
看完上面的效果图,大家一定都迫不及待地想要试一试了,那就让我们来动手吧。
首先我们定义一个实体类DropLook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
|
public class DropLook {
private float x; private float y; private float rotation; private float speed; private float rotationSpeed; private int width; private int height; private Bitmap bitmap;
public float getX() { return x; }
public void setX(float x) { this.x = x; }
public float getY() { return y; }
public void setY(float y) { this.y = y; }
public float getRotationSpeed() { return rotationSpeed; }
public void setRotationSpeed(float rotationSpeed) { this.rotationSpeed = rotationSpeed; }
public float getRotation() { return rotation; }
public void setRotation(float rotation) { this.rotation = rotation; }
public float getSpeed() { return speed; }
public void setSpeed(float speed) { this.speed = speed; }
public int getWidth() { return width; }
public void setWidth(int width) { this.width = width; }
public Bitmap getBitmap() { return bitmap; }
public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; }
public int getHeight() { return height; }
public void setHeight(int height) { this.height = height; }
}
|
我们定义的实体类很简单,只是设置了如宽高、x,y坐标、下落速度等。接下来我们再创建一个DropLookFactory类,用来创建DropLook对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class DropLookFactory {
private DropLookFactory() {
}
public static DropLook createDropLook(int width, int height,Bitmap originalBitmap) { DropLook look = new DropLook(); if (originalBitmap == null) { throw new NullPointerException("originalBitmap cannot be null"); } look.setWidth(originalBitmap.getWidth()); look.setHeight(originalBitmap.getHeight()); look.setX((float) Math.random() * (width - look.getWidth())); look.setY((float) Math.random() * (height - look.getHeight())); look.setSpeed(20 + (float) Math.random() * 40); look.setRotation((float) Math.random() * 180 - 90); look.setRotationSpeed((float) Math.random() * 90 - 60); look.setBitmap(originalBitmap); return look; }
}
|
其中createDropLook(Context context, float xRange, Bitmap originalBitmap)
的第一个参数代表着下落表情在x轴上的范围,第二个参数代表在y轴上的范围,第三个参数是表情的图片。在createDropLook方法中相信大家都看得懂,主要就是用随机数初始化DropLook的坐标及下落速度等。
好了,下面就是今天的重头戏DropLookView,先来看看onMeasure():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) { mWidth = widthSize; } else { mWidth = Tools.dip2px(getContext(),200); } int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.EXACTLY) { mHeight = heightSize; } else { mHeight = Tools.dip2px(getContext(),200); } setMeasuredDimension(mWidth, mHeight); if (looks.size() == 0) { for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; ++i) { looks.add(DropLookFactory.createDropLook(mWidth, mHeight, mBitmap)); } Log.i(TAG, "num = " + looks.size()); } }
|
onMeasure里主要是对View的测量,如果是wrap_content
的话设置一个默认的宽高度200dp。然后就是初始化DropLook,looks是DropLook类的集合,用于管理DropLook。而DEFAULT_LOOK_NUMS
是默认的looks集合的数量。
接下来就是最关键的onDraw():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); long nowTime = System.currentTimeMillis(); if (nowTime - startTime < 100) { try { Thread.sleep(100 + startTime - nowTime); } catch (InterruptedException e) { e.printStackTrace(); } }
for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; i++) {
DropLook look = looks.get(i); mMatrix.setTranslate(-look.getWidth() / 2, -look.getHeight() / 2); mMatrix.postRotate(look.getRotation()); mMatrix.postTranslate(look.getWidth() / 2 + look.getX(), look.getHeight() / 2 + look.getY()); canvas.drawBitmap(look.getBitmap(), mMatrix, mPaint);
look.setY(look.getY() + look.getSpeed()); if (look.getY() > getHeight()) { look.setY((float) (0 - Math.random() * look.getHeight())); }
look.setRotation(look.getRotation() + look.getRotationSpeed()); }
canvas.restore(); startTime = System.currentTimeMillis(); invalidate(); }
|
一开始判断时间间隔如果没有超过100ms,就让线程睡眠一会。然后就是用drawBitmap的方法把looks里面逐个绘制出来。并且再把look的y轴坐标加上下落速度等,旋转的角度也是如此。最后就是调用invalidate()不断地重绘。总体上并没有什么难点。
以下是DropLookView的完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
public class DropLookView extends View {
private Bitmap mBitmap; List<DropLook> looks = new ArrayList(); private long startTime; private int mWidth; private int mHeight; private Paint mPaint; private static final int DEFAULT_DROP_LOOK_NUMS = 35;
private static final String TAG = "DropLookView";
private Matrix mMatrix = new Matrix();
public DropLookView(Context context) { this(context, null); }
public DropLookView(Context context, AttributeSet attrs) { this(context, attrs, 0); }
public DropLookView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.d_5_xiaoku); }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode == MeasureSpec.EXACTLY) { mWidth = widthSize; } else { mWidth = Tools.dip2px(getContext(),200); } int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.EXACTLY) { mHeight = heightSize; } else { mHeight = Tools.dip2px(getContext(),200); } setMeasuredDimension(mWidth, mHeight); if (looks.size() == 0) { for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; ++i) { looks.add(DropLookFactory.createDropLook(mWidth, mHeight, mBitmap)); } Log.i(TAG, "num = " + looks.size()); } }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.save(); long nowTime = System.currentTimeMillis(); if (nowTime - startTime < 100) { try { Thread.sleep(100 + startTime - nowTime); } catch (InterruptedException e) { e.printStackTrace(); } }
for (int i = 0; i < DEFAULT_DROP_LOOK_NUMS; i++) {
DropLook look = looks.get(i); mMatrix.setTranslate(-look.getWidth() / 2, -look.getHeight() / 2); mMatrix.postRotate(look.getRotation()); mMatrix.postTranslate(look.getWidth() / 2 + look.getX(), look.getHeight() / 2 + look.getY()); canvas.drawBitmap(look.getBitmap(), mMatrix, mPaint);
look.setY(look.getY() + look.getSpeed()); if (look.getY() > getHeight()) { look.setY((float) (0 - Math.random() * look.getHeight())); }
look.setRotation(look.getRotation() + look.getRotationSpeed()); }
canvas.restore(); startTime = System.currentTimeMillis(); invalidate(); }
}
|