查看: 2177|回复: 0

[Android教程] 自定义洒豆子

发表于 2018-2-20 08:00:03

先上效果图

洒豆子的效果,突发奇想,觉得这个动画挺有意思的,就抽空写了一个玩玩

绘制流程:

  定义6个‘’豆子‘’,每个豆子有各自的属性,大小,抛出的速度等,然后控制每个的方向和状态,回弹效果使用差值器 BounceInterpolator

  1. package com.fragmentapp.view.beans;
  2. import android.animation.Animator;
  3. import android.animation.ValueAnimator;
  4. import android.content.Context;
  5. import android.graphics.Canvas;
  6. import android.graphics.Paint;
  7. import android.util.AttributeSet;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.view.animation.BounceInterpolator;
  11. import com.fragmentapp.R;
  12. import com.fragmentapp.helper.RandomUtil;
  13. /**
  14. * Created by liuzhen on 2017/1/17.
  15. */
  16. public class BeansView extends View {
  17. private Paint paint;
  18. private int mWidth;
  19. private int mHeight;
  20. private int top;
  21. private ValueAnimator va;
  22. private Beans beans1,beans2,beans3,beans4,beans5,beans6;
  23. public BeansView(Context context) {
  24. this(context, null);
  25. }
  26. public BeansView(Context context, AttributeSet attrs) {
  27. this(context, attrs, 0);
  28. }
  29. public BeansView(Context context, AttributeSet attrs, int defStyleAttr) {
  30. super(context, attrs, defStyleAttr);
  31. init();
  32. }
  33. private void init() {
  34. Log.e("tag","init");
  35. setWillNotDraw(false);
  36. paint = new Paint();
  37. paint.setAntiAlias(true);
  38. paint.setStyle(Paint.Style.FILL);
  39. paint.setColor(getResources().getColor(R.color.color_ff9c19));
  40. //随机生成球体的大小、
  41. beans1 = new Beans(RandomUtil.random(5,15));
  42. beans2 = new Beans(RandomUtil.random(5,15));
  43. beans3 = new Beans(RandomUtil.random(5,15));
  44. beans4 = new Beans(RandomUtil.random(5,15));
  45. beans5 = new Beans(RandomUtil.random(5,15));
  46. beans6 = new Beans(RandomUtil.random(5,15));
  47. }
  48. @Override
  49. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  50. super.onLayout(changed, left, top, right, bottom);
  51. if (changed) {
  52. mWidth = getWidth();
  53. mHeight = getHeight();
  54. this.top = top;
  55. startAnim();
  56. }
  57. }
  58. @Override
  59. protected void onDraw(Canvas canvas) {
  60. //正常向右掉落,这里也可以利用随机生成方向,这里就固定左边三个右边三个
  61. canvas.drawCircle(beans1.getCx(), beans1.getCy(), beans1.getRadius(), paint);
  62. canvas.drawCircle(beans2.getCx(), beans2.getCy(), beans2.getRadius(), paint);
  63. canvas.drawCircle(beans3.getCx(), beans3.getCy(), beans3.getRadius(), paint);
  64. //让球往左边掉落
  65. canvas.drawCircle(-beans4.getCx()+mWidth, beans4.getCy(), beans4.getRadius(), paint);
  66. canvas.drawCircle(-beans5.getCx()+mWidth, beans5.getCy(), beans5.getRadius(), paint);
  67. canvas.drawCircle(-beans6.getCx()+mWidth, beans6.getCy(), beans6.getRadius(), paint);
  68. }
  69. public void startAnim() {
  70. if (mWidth == 0) return;
  71. beans1.setState(0);
  72. beans1.setOff(0);
  73. beans1.setRand(RandomUtil.random(20));//随机生成抛出的速度值
  74. beans2.setState(0);
  75. beans2.setOff(0);
  76. beans2.setRand(RandomUtil.random(20));
  77. beans3.setState(0);
  78. beans3.setOff(0);
  79. beans3.setRand(RandomUtil.random(20));
  80. beans4.setState(0);
  81. beans4.setOff(0);
  82. beans4.setRand(RandomUtil.random(20));
  83. beans5.setState(0);
  84. beans5.setOff(0);
  85. beans5.setRand(RandomUtil.random(20));
  86. beans6.setState(0);
  87. beans6.setOff(0);
  88. beans6.setRand(RandomUtil.random(20));
  89. va = ValueAnimator.ofFloat(top, mHeight - top);
  90. va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  91. @Override
  92. public void onAnimationUpdate(ValueAnimator animation) {
  93. float val = (float)animation.getAnimatedValue();
  94. beans1.setCy(val);
  95. beans1.move(mWidth);//先移动坐标,实际上是改变了off偏移量的值
  96. beans1.setCx(mWidth / 2 + beans1.getOff());//刷新X轴坐标
  97. beans2.setCy(val);
  98. beans2.move(mWidth);
  99. beans2.setCx(mWidth / 2 + beans2.getOff());
  100. beans3.setCy(val);
  101. beans3.move(mWidth);
  102. beans3.setCx(mWidth / 2 + beans3.getOff());
  103. beans4.setCy(val);
  104. beans4.move(mWidth);
  105. beans4.setCx(mWidth / 2 + beans4.getOff());
  106. beans5.setCy(val);
  107. beans5.move(mWidth);
  108. beans5.setCx(mWidth / 2 + beans5.getOff());
  109. beans6.setCy(val);
  110. beans6.move(mWidth);
  111. beans6.setCx(mWidth / 2 + beans6.getOff());
  112. invalidate();
  113. }
  114. });
  115. va.addListener(new Animator.AnimatorListener() {
  116. @Override
  117. public void onAnimationStart(Animator animator) {
  118. }
  119. @Override
  120. public void onAnimationEnd(Animator animator) {
  121. //防止停止后球体因为半径的不一样而降落到地面的水平不一样,统一水平线
  122. beans1.setCy(mHeight - beans1.getRadius());
  123. beans2.setCy(mHeight - beans2.getRadius());
  124. beans3.setCy(mHeight - beans3.getRadius());
  125. beans4.setCy(mHeight - beans4.getRadius());
  126. beans5.setCy(mHeight - beans5.getRadius());
  127. beans6.setCy(mHeight - beans6.getRadius());
  128. invalidate();
  129. }
  130. @Override
  131. public void onAnimationCancel(Animator animator) {
  132. }
  133. @Override
  134. public void onAnimationRepeat(Animator animator) {
  135. }
  136. });
  137. va.setInterpolator(new BounceInterpolator());//重力差值器
  138. va.setDuration(3000);
  139. va.setRepeatMode(ValueAnimator.RESTART);
  140. va.start();
  141. }
  142. public void stopAnim() {
  143. va.cancel();
  144. va = null;
  145. }
  146. }
复制代码
View Code

这里主要把逻辑封装到单独的对象里面去了,所以view类看起来很清爽

下面是豆子类

  1. package com.fragmentapp.view.beans;
  2. import android.util.Log;
  3. /**
  4. * Created by liuzhen on 2018/1/18.
  5. */
  6. public class Beans {
  7. public Beans(){ }
  8. public Beans(int radius){
  9. this.radius = radius;
  10. }
  11. /**X坐标*/
  12. private float cx;
  13. /**Y坐标*/
  14. private float cy;
  15. /**偏移量*/
  16. private float off;
  17. /**随机生成的速度值*/
  18. private float rand;
  19. /**是否碰到边缘*/
  20. private int state;
  21. /**圆球的大小*/
  22. private float radius;
  23. /**移动 X 坐标,并且碰到边界后回弹*/
  24. public void move(int width){
  25. if (cx < 0 || state == 1) {//碰到左边的边缘
  26. state = 1;
  27. off += rand;
  28. } else if (cx >= width || state == 2) {//碰到右边的边缘
  29. state = 2;
  30. off -= rand;
  31. }else if(state == 0) {
  32. state = 0;
  33. off += rand;
  34. }
  35. // Log.e("tag","-- cx "+(int)cx + " width "+width + " state "+state);
  36. }
  37. public float getCx() {
  38. return cx;
  39. }
  40. public void setCx(float cx) {
  41. this.cx = cx;
  42. }
  43. public float getOff() {
  44. return off;
  45. }
  46. public void setOff(float off) {
  47. this.off = off;
  48. }
  49. public float getRand() {
  50. return rand;
  51. }
  52. public void setRand(float rand) {
  53. this.rand = rand;
  54. }
  55. public int getState() {
  56. return state;
  57. }
  58. public void setState(int state) {
  59. this.state = state;
  60. }
  61. public float getCy() {
  62. return cy;
  63. }
  64. public void setCy(float cy) {
  65. this.cy = cy;
  66. }
  67. public float getRadius() {
  68. return radius;
  69. }
  70. public void setRadius(float radius) {
  71. this.radius = radius;
  72. }
  73. }
复制代码
View Code

主要逻辑集中在move方法中

默认是正常抛出,然后碰到边缘后改变状态往回弹

使用上只关注两个方法就行了

这里是把控件放在了一个dialog里面,这个看个人喜欢,显然dialog不是很适合,或者可以加到下拉库的头部上去,效果应该不错

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:background="@drawable/shape_dialog_bg"
  5. android:padding="@dimen/d20.0"
  6. android:orientation="vertical"
  7. android:id="@+id/root">
  8. <com.fragmentapp.view.beans.BeansView
  9. android:id="@+id/beans"
  10. android:layout_width="@dimen/d350.0"
  11. android:layout_height="@dimen/d300.0"
  12. android:layout_gravity="center_horizontal" />
  13. <!--<View-->
  14. <!--android:layout_width="match_parent"-->
  15. <!--android:layout_height="@dimen/d1.0"-->
  16. <!--android:background="@color/white"/>-->
  17. <TextView
  18. android:id="@+id/tv_val"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:layout_gravity="center_horizontal"
  22. android:layout_marginTop="@dimen/d20.0"
  23. android:text="加载中..."
  24. android:textColor="@color/color_cccccc"
  25. android:textSize="@dimen/d43.0" />
  26. </LinearLayout>
复制代码
View Code

到这里基本完成了,不过这样的效果绘制显然不是很好看,而且很low,所以要优化一下绘制的图形,让它看起来更高大上一些,最优先需要改的肯定是圆球了,各种3D形状,很好看,不过没法绘制,只能网上找个图片直接drawbitmap了,大多的华丽都是跟图片搭配的

然而就是代码了,代码看起来也有点low,也需要优化一下

1:代码优化

  以前的是固定对象,然后绘制,肯定需要稍微动态一点了

2:圆球绘制

  以前的是直接绘制圆,不好看,从网上下载一个圆形 icon,代替圆,看起来更立体一点

这里的做法是把圆形对象也放进实体类里面去,方便统一获取,然后创建一个统一管理实体类的集合

先获取到我们的icon,随机产生圆球的大小,添加进集合

接下来所有的固定的地方都换成for循环来代替

是不是方便多了,看起来简洁多了,可以对比两边的代码,你会发现,哎呦,不错哦

  1. package com.fragmentapp.view.beans;
  2. import android.animation.Animator;
  3. import android.animation.ValueAnimator;
  4. import android.content.Context;
  5. import android.graphics.Bitmap;
  6. import android.graphics.Canvas;
  7. import android.graphics.Matrix;
  8. import android.graphics.Paint;
  9. import android.graphics.drawable.BitmapDrawable;
  10. import android.util.AttributeSet;
  11. import android.util.Log;
  12. import android.view.View;
  13. import android.view.animation.BounceInterpolator;
  14. import com.fragmentapp.R;
  15. import com.fragmentapp.helper.RandomUtil;
  16. import java.util.ArrayList;
  17. import java.util.List;
  18. /**
  19. * Created by liuzhen on 2017/1/17.
  20. */
  21. public class BeansView extends View {
  22. private Paint paint;
  23. private int mWidth;
  24. private int mHeight;
  25. private int top;
  26. private ValueAnimator va;
  27. private List<Beans> beans = null;
  28. public BeansView(Context context) {
  29. this(context, null);
  30. }
  31. public BeansView(Context context, AttributeSet attrs) {
  32. this(context, attrs, 0);
  33. }
  34. public BeansView(Context context, AttributeSet attrs, int defStyleAttr) {
  35. super(context, attrs, defStyleAttr);
  36. init();
  37. }
  38. private void init() {
  39. Log.e("tag","init");
  40. setWillNotDraw(false);
  41. paint = new Paint();
  42. paint.setAntiAlias(true);
  43. paint.setStyle(Paint.Style.FILL);
  44. paint.setColor(getResources().getColor(R.color.color_ff9c19));
  45. beans = new ArrayList<>();
  46. Bitmap bitmap = ((BitmapDrawable)(getResources().getDrawable(R.mipmap.ball))).getBitmap();
  47. //随机生成球体的大小、
  48. for(int i = 0;i < 6; i ++){
  49. int radius = RandomUtil.random(15,30);
  50. final Beans b = new Beans(radius);
  51. b.setDirection(i % 2 == 0 ? Beans.Left : Beans.Right);
  52. b.bitmap = Bitmap.createScaledBitmap(bitmap,radius,radius,true);
  53. beans.add(b);
  54. }
  55. }
  56. @Override
  57. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  58. super.onLayout(changed, left, top, right, bottom);
  59. if (changed) {
  60. mWidth = getWidth();
  61. mHeight = getHeight();
  62. this.top = top;
  63. startAnim();
  64. }
  65. }
  66. @Override
  67. protected void onDraw(Canvas canvas) {
  68. for (Beans b : beans) {
  69. if (b.bitmap != null) {
  70. if (b.getDirection() == Beans.Left) {
  71. canvas.drawBitmap(b.bitmap, b.getCx(), b.getCy(), null);
  72. } else {
  73. canvas.drawBitmap(b.bitmap, -b.getCx() + mWidth, b.getCy(), null);
  74. }
  75. }
  76. }
  77. }
  78. public void startAnim() {
  79. if (mWidth == 0 || beans.size() == 0) return;
  80. for (Beans b : beans) {
  81. b.setState(0);
  82. b.setOff(0);
  83. b.setRand(RandomUtil.random(20));//随机生成抛出的速度值
  84. }
  85. va = ValueAnimator.ofFloat(top, mHeight);
  86. va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  87. @Override
  88. public void onAnimationUpdate(ValueAnimator animation) {
  89. float val = (float)animation.getAnimatedValue();
  90. for (Beans b : beans) {
  91. b.setCy(val - b.getRadius());
  92. b.move(mWidth);//先移动坐标,实际上是改变了off 偏移量的值
  93. b.setCx(mWidth / 2 + b.getOff());//刷新X轴坐标
  94. }
  95. postInvalidate();
  96. }
  97. });
  98. va.setInterpolator(new BounceInterpolator());//重力差值器
  99. va.setDuration(3500);
  100. va.setRepeatMode(ValueAnimator.RESTART);
  101. va.start();
  102. }
  103. public void stopAnim() {
  104. va.cancel();
  105. va = null;
  106. }
  107. }
复制代码
View Code
  1. package com.fragmentapp.view.beans;
  2. import android.graphics.Bitmap;
  3. import android.util.Log;
  4. /**
  5. * Created by liuzhen on 2018/1/18.
  6. */
  7. public class Beans {
  8. public Beans(){ }
  9. public Beans(int radius){
  10. this.radius = radius;
  11. }
  12. public static final int Left = 0;
  13. public static final int Right = 1;
  14. private int direction;
  15. /**X坐标*/
  16. private float cx;
  17. /**Y坐标*/
  18. private float cy;
  19. /**偏移量*/
  20. private float off;
  21. /**随机生成的速度值*/
  22. private float rand;
  23. /**是否碰到边缘*/
  24. private int state;
  25. /**圆球的大小*/
  26. private float radius;
  27. public Bitmap bitmap;
  28. /**移动 X 坐标,并且碰到边界后回弹*/
  29. public void move(int width){
  30. if (cx < 0 || state == 1) {//碰到左边的边缘
  31. state = 1;
  32. off += rand;
  33. } else if (cx >= width || state == 2) {//碰到右边的边缘
  34. state = 2;
  35. off -= rand;
  36. }else if(state == 0) {
  37. state = 0;
  38. off += rand;
  39. }
  40. // Log.e("tag","-- cx "+(int)cx + " width "+width + " state "+state);
  41. }
  42. public float getCx() {
  43. return cx;
  44. }
  45. public void setCx(float cx) {
  46. this.cx = cx;
  47. }
  48. public float getOff() {
  49. return off;
  50. }
  51. public void setOff(float off) {
  52. this.off = off;
  53. }
  54. public float getRand() {
  55. return rand;
  56. }
  57. public void setRand(float rand) {
  58. this.rand = rand;
  59. }
  60. public int getState() {
  61. return state;
  62. }
  63. public void setState(int state) {
  64. this.state = state;
  65. }
  66. public float getCy() {
  67. return cy;
  68. }
  69. public void setCy(float cy) {
  70. this.cy = cy;
  71. }
  72. public float getRadius() {
  73. return radius;
  74. }
  75. public void setRadius(float radius) {
  76. this.radius = radius;
  77. }
  78. public int getDirection() {
  79. return direction;
  80. }
  81. public void setDirection(int direction) {
  82. this.direction = direction;
  83. }
  84. }
复制代码
View Code

下面是下载地址,谢谢收藏 ^_^

GitHub:https://github.com/1024477951/FragmentApp



回复

使用道具 举报