NineOldAndroidsDemos 学习(4) FlakeView
2013-11-05 19:49
197 查看
这个Flake实现的效果还是很酷的,
实现起来也不是很难,首先介绍大体的原理:
1.每个小机器人 (Flake ) 可以理解为一个 "精灵",他有自己的速度,位置,大小,形状,旋转等.
2.而FlakeView就是一个舞台了,在ValueAnimator的驱动下,每一个动画帧 调用invalidate();刷新这个view,在他的onDraw中就会遍历每个flake,根据他们的位置 形状等
将其画出来.
下面从代码角度来分析一下
首先是Flake.java
然后是FlakeView.java
在activity中的操作就是
实现起来也不是很难,首先介绍大体的原理:
1.每个小机器人 (Flake ) 可以理解为一个 "精灵",他有自己的速度,位置,大小,形状,旋转等.
2.而FlakeView就是一个舞台了,在ValueAnimator的驱动下,每一个动画帧 调用invalidate();刷新这个view,在他的onDraw中就会遍历每个flake,根据他们的位置 形状等
将其画出来.
下面从代码角度来分析一下
首先是Flake.java
/** * This class represents a single Droidflake, with properties representing its * size, rotation, location, and speed. *可以理解小机器人的精灵类 他有自己的位置 速度等 */ public class Flake { // These are the unique properties of any flake: its size, rotation, speed, // location, and its underlying Bitmap object float x, y; float rotation; float speed; float rotationSpeed; int width, height; Bitmap bitmap; // This map stores pre-scaled bitmaps according to the width. No reason to create // new bitmaps for sizes we've already seen. //缓存他的bitmap static HashMap<Integer, Bitmap> bitmapMap = new HashMap<Integer, Bitmap>(); /** * Creates a new droidflake in the given xRange and with the given bitmap. Parameters of * location, size, rotation, and speed are randomly determined. * 给定x的边界,然后随机创建小机器人 */ static Flake createFlake(float xRange, Bitmap originalBitmap) { Flake flake = new Flake(); // Size each flake with a width between 5 and 55 and a proportional height flake.width = (int)(5 + (float)Math.random() * 50); float hwRatio = originalBitmap.getHeight() / originalBitmap.getWidth(); flake.height = (int)(flake.width * hwRatio); // Position the flake horizontally between the left and right of the range //让小机器人在x方向上处在屏幕直接,不要跑出屏幕 flake.x = (float)Math.random() * (xRange - flake.width); // Position the flake vertically slightly off the top of the display flake.y = 0 - (flake.height + (float)Math.random() * flake.height); // Each flake travels at 50-200 pixels per second flake.speed = 50 + (float) Math.random() * 150; // Flakes start at -90 to 90 degrees rotation, and rotate between -45 and 45 // degrees per second flake.rotation = (float) Math.random() * 180 - 90; flake.rotationSpeed = (float) Math.random() * 90 - 45; // Get the cached bitmap for this size if it exists, otherwise create and cache one flake.bitmap = bitmapMap.get(flake.width); if (flake.bitmap == null) { flake.bitmap = Bitmap.createScaledBitmap(originalBitmap, (int)flake.width, (int)flake.height, true); bitmapMap.put(flake.width, flake.bitmap); } return flake; } }
然后是FlakeView.java
** * This class is the custom view where all of the Droidflakes are drawn. This class has * all of the logic for adding, subtracting, and rendering Droidflakes. * * 这个view用来承载那些"机器人元素" , 包含添加 ,减少 , 和渲染 小机器人 的功能 */ public class FlakeView extends View { Bitmap droid; // 从R中获得的那个绿色机器人图片 int numFlakes = 0; // 小机器人图片的数目 ArrayList<Flake> flakes = new ArrayList<Flake>(); // 承载 机器人的 list // Animator used to drive all separate flake animations. Rather than have potentially // hundreds of separate animators, we just use one and then update all flakes for each // frame of that single animation. //使用同一个animator来驱动所有的 小机器人 的运动,这个值从0变到1不断重复,其实这个值在这个案例中并没有用到他的值 //可以理解为一个handler +thread ValueAnimator animator = ValueAnimator.ofFloat(0, 1); long startTime, prevTime; // 用来计算 时间间隔和 fps int frames = 0; // 用来计算一秒刷新几帧 Paint textPaint; // 文字 float fps = 0; // frames per second Matrix m = new Matrix(); // 用来移动 旋转 小机器人 String fpsString = ""; String numFlakesString = ""; /** * 初始化一些全局变量 */ public FlakeView(Context context) { super(context); droid = BitmapFactory.decodeResource(getResources(), R.drawable.droid); textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setColor(Color.WHITE); textPaint.setTextSize(24); // This listener is where the action is for the flak animations. Every frame of the // animation, we calculate the elapsed time and update every flake's position and rotation // according to its speed. //在动画的每一帧(动画帧的间隔经过测试是 10ms 小于 人眼可以分别的 1/60 s )都会回调到这个方法,这个方法用来调整 小机器人的 位置 并 通知刷新UI. animator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator arg0) { long nowTime = System.currentTimeMillis(); float secs = (float)(nowTime - prevTime) / 1000f; prevTime = nowTime; for (int i = 0; i < numFlakes; ++i) { Flake flake = flakes.get(i); flake.y += (flake.speed * secs); if (flake.y > getHeight()) { // If a flake falls off the bottom, send it back to the top flake.y = 0 - flake.height; } flake.rotation = flake.rotation + (flake.rotationSpeed * secs); } // Force a redraw to see the flakes in their new positions and orientations invalidate(); } }); animator.setRepeatCount(ValueAnimator.INFINITE);//无限重复播放动画 animator.setDuration(3000); } int getNumFlakes() { return numFlakes; } private void setNumFlakes(int quantity) { numFlakes = quantity; numFlakesString = "numFlakes: " + numFlakes; } /** * Add the specified number of droidflakes. */ void addFlakes(int quantity) { for (int i = 0; i < quantity; ++i) { flakes.add(Flake.createFlake(getWidth(), droid)); } setNumFlakes(numFlakes + quantity); } /** * Subtract the specified number of droidflakes. We just take them off the end of the * list, leaving the others unchanged. */ void subtractFlakes(int quantity) { for (int i = 0; i < quantity; ++i) { int index = numFlakes - i - 1; flakes.remove(index); } setNumFlakes(numFlakes - quantity); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Reset list of droidflakes, then restart it with 8 flakes flakes.clear(); numFlakes = 0; addFlakes(8); // Cancel animator in case it was already running animator.cancel(); // Set up fps tracking and start the animation startTime = System.currentTimeMillis(); prevTime = startTime; frames = 0; animator.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // For each flake: back-translate by half its size (this allows it to rotate around its center), // rotate by its current rotation, translate by its location, then draw its bitmap //遍历每一个 小机器人 for (int i = 0; i < numFlakes; ++i) { Flake flake = flakes.get(i); m.setTranslate(-flake.width/2, -flake.height/2);//移动小机器人的中心到(0,0) m.postRotate(flake.rotation);//旋转小机器人 m.postTranslate(flake.width/2 + flake.x, flake.height/2 + flake.y);//移动小机器人到(x,y) canvas.drawBitmap(flake.bitmap, m, null);//把小机器人画出来 } // fps counter: count how many frames we draw and once a second calculate the // frames per second //计算一秒我们画了多少帧 ++frames; long nowTime = System.currentTimeMillis(); long deltaTime = nowTime - startTime; if (deltaTime > 1000) { float secs = (float) deltaTime / 1000f; fps = (float) frames / secs; fpsString = "fps: " + fps; startTime = nowTime; frames = 0; } canvas.drawText(numFlakesString, getWidth() - 200, getHeight() - 50, textPaint); canvas.drawText(fpsString, getWidth() - 200, getHeight() - 80, textPaint); } public void pause() { // Make sure the animator's not spinning in the background when the activity is paused. animator.cancel(); } public void resume() { animator.start(); } }
在activity中的操作就是
public class Droidflakes extends Activity { FlakeView flakeView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.droidflakes); LinearLayout container = (LinearLayout) findViewById(R.id.container); flakeView = new FlakeView(this); container.addView(flakeView); getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK)); Button more = (Button) findViewById(R.id.more); more.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { flakeView.addFlakes(flakeView.getNumFlakes()); } }); Button less = (Button) findViewById(R.id.less); less.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { flakeView.subtractFlakes(flakeView.getNumFlakes() / 2); } }); if (Integer.parseInt(Build.VERSION.SDK) >= Build.VERSION_CODES.HONEYCOMB) { HoneycombHelper.setup(this); } } private static final class HoneycombHelper { static void setup(final Droidflakes activity) { CheckBox accelerated = (CheckBox) activity.findViewById(R.id.accelerated); accelerated.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { activity.flakeView.setLayerType(//开启硬件加速的方法 isChecked ? View.LAYER_TYPE_NONE : View.LAYER_TYPE_SOFTWARE, null); } }); } } @Override protected void onPause() {//在这时候 停止动画防止不必要的资源浪费 super.onPause(); flakeView.pause(); } @Override protected void onResume() { super.onResume(); flakeView.resume(); } }
相关文章推荐
- NineOldAndroidsDemos 学习(6) AnimationSeeking 和 AnimatorEvents
- NineOldAndroidsDemos 学习(3) PathAnimationActivity
- NineOldAndroidsDemos 学习(7) BouncingBalls
- NineOldAndroidsDemos 学习(1) Toggles
- NineOldAndroidsDemos 学习(5) AnimationCloning 和 AnimationLoading
- NineOldAndroidsDemos 学习(8) MultiPropertyAnimation ReversingAnimation
- NineOldAndroidsDemos 学习(2) ViewPropertyAnimator
- NineOldAndroidsDemos 学习(9) CustomEvaluator
- nineoldandroids学习笔记
- nineoldandroids学习笔记
- 8. Android框架和工具之 NineOldAndroids(动画框架)
- NineOldAndroids:Android 3.0 Animation API的适配库
- Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
- API Demos 2.3 学习笔记 (15)-- Views->Radio Group
- Android Drawable Resource学习(三)、NinePatchDrawable和NinePatch图片的制作
- API Demos 2.3 学习笔记 (16)-- Views->Spinner
- API Demos 2.3 学习笔记 (17)-- Views->Tabs
- Android开源动画框架:NineOldAndroids
- Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果
- Drawable学习之---NinePatchDrawable(.9图片的制作)