14 | * 控制CubeGrid的动画执行 15 | */ 16 | public class CubeGridManager { 17 | 18 | /** 19 | * 一个完整动画执行的大小值 20 | */ 21 | public static int ANIM_CYCLE_VALUE = 1300; 22 | /** 23 | * 动画执行每一步的大小值 24 | */ 25 | public static int ANIM_STEP_VALUE = 15; 26 | 27 | /** 28 | * 动画执行每一步的时间值, 单位为毫秒 29 | */ 30 | public static int ANIM_STEP_TIME = 15; 31 | 32 | /** 33 | * 执行动画的单个延时 34 | */ 35 | public static int ANIM_DELAY = 100; 36 | 37 | /** 38 | * 动画循环的周期次数, 默认值为2圈 39 | */ 40 | public int mLoopCount = 50; 41 | 42 | /** 43 | * 整个动画执行的大小值 44 | */ 45 | public int mAnimTotalValue = 0; 46 | 47 | public static final int ANIM_MSG = 103; 48 | 49 | /** 50 | * 动画执行的插值器, 这里使用加速,之后减速来达到ease-in-out的效果 51 | */ 52 | public TimeInterpolator mInterpolator; 53 | 54 | /** 55 | * 对应每个小方块延时的时长 56 | */ 57 | private int[][] mDelayTime = { 58 | { ANIM_DELAY * 2, ANIM_DELAY * 3, ANIM_DELAY * 4 }, { ANIM_DELAY, ANIM_DELAY * 2, ANIM_DELAY * 3 }, 59 | { 0, ANIM_DELAY, ANIM_DELAY * 2 } 60 | }; 61 | 62 | private CubeGridObject[][] mCubeGridObjects; 63 | 64 | private int mRowSize; 65 | private int mColumnSize; 66 | private ICubeGridAnimCallback mCubeGridAnimCallback; 67 | private boolean mStoped; 68 | 69 | /** 70 | * 当前动画的大小值 71 | */ 72 | private int mCurValue = 0; 73 | 74 | private View mAnimView = null; 75 | 76 | private Handler mHandler = new Handler() { 77 | @Override 78 | public void dispatchMessage(Message msg) { 79 | super.dispatchMessage(msg); 80 | if (msg.what == ANIM_MSG) { 81 | if (mStoped) { 82 | return; 83 | } 84 | startAnimLoop(); 85 | } 86 | } 87 | }; 88 | 89 | /** 90 | * 设置方块的参数 91 | */ 92 | public void setUp(CubeGridManagerOption cubeGridManagerOption) { 93 | if (cubeGridManagerOption != null) { 94 | int totalWidth = cubeGridManagerOption.getTotalWidth(); 95 | int totalHeight = cubeGridManagerOption.getTotalHeight(); 96 | 97 | mRowSize = cubeGridManagerOption.getRowSize(); 98 | mColumnSize = cubeGridManagerOption.getColumnSize(); 99 | if (cubeGridManagerOption.getLoopCount() > 0) mLoopCount = cubeGridManagerOption.getLoopCount(); 100 | mAnimTotalValue = ANIM_CYCLE_VALUE * mLoopCount + ANIM_DELAY * 4; 101 | mCubeGridAnimCallback = cubeGridManagerOption.getCubeGridAnimCallback(); 102 | mCubeGridObjects = new CubeGridObject[mRowSize][mColumnSize]; 103 | int cornerSize = cubeGridManagerOption.getCornerSize(); 104 | 105 | Paint paint = new Paint(); 106 | paint.setColor(cubeGridManagerOption.getFillColor()); 107 | int thisX, thisY, nextX, nextY; 108 | for (int i = 0; i < mRowSize; i++) { 109 | for (int j = 0; j < mColumnSize; j++) { 110 | if (j + 1 == mColumnSize) { 111 | nextX = totalWidth; 112 | } else { 113 | nextX = (int) (Math.floor(totalWidth * 1.0f / mColumnSize * (j + 1))); 114 | } 115 | thisX = (int) (Math.floor(totalWidth * 1.0f / mColumnSize * j)); 116 | 117 | if (i + 1 == mRowSize) { 118 | nextY = totalHeight; 119 | } else { 120 | nextY = (int) (Math.floor(totalHeight * 1.0f / mRowSize * (i + 1))); 121 | } 122 | 123 | thisY = (int) (Math.floor(totalHeight * 1.0f / mRowSize * i)); 124 | 125 | mCubeGridObjects[i][j] = new CubeGridObject(thisX, thisY, nextX - thisX, nextY - thisY, paint); 126 | mCubeGridObjects[i][j].setCurLoopCount(1); 127 | mCubeGridObjects[i][j].setMaxLoopCount(Integer.MAX_VALUE); 128 | if (cornerSize > 0) { 129 | if (i == 0 && j == 0) { 130 | mCubeGridObjects[i][j].setCornerLocation(CornerLocation.LEFTTOP); 131 | mCubeGridObjects[i][j].setCornerSize(cornerSize); 132 | } else if (j == 0 && i + 1 == mRowSize) { 133 | mCubeGridObjects[i][j].setCornerLocation(CornerLocation.LEFTBOTTOM); 134 | mCubeGridObjects[i][j].setCornerSize(cornerSize); 135 | } else if (i == 0 && j + 1 == mColumnSize) { 136 | mCubeGridObjects[i][j].setCornerLocation(CornerLocation.RIGHTTOP); 137 | mCubeGridObjects[i][j].setCornerSize(cornerSize); 138 | } else if (i + 1 == mRowSize && j + 1 == mColumnSize) { 139 | mCubeGridObjects[i][j].setCornerLocation(CornerLocation.RIGHTBOTTOM); 140 | mCubeGridObjects[i][j].setCornerSize(cornerSize); 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * 在指定的View上, 做Canvas动画 150 | */ 151 | public void startLoop(final View view) { 152 | mStoped = false; 153 | for (int i = 0; i < mRowSize; i++) { 154 | for (int j = 0; j < mColumnSize; j++) { 155 | mCubeGridObjects[i][j].setCurLoopCount(1); 156 | } 157 | } 158 | mCurValue = 0; 159 | if (mCubeGridAnimCallback != null) { 160 | mCubeGridAnimCallback.onAnimStart(); 161 | } 162 | mAnimView = view; 163 | mHandler.sendEmptyMessage(ANIM_MSG); 164 | } 165 | 166 | /** 167 | * 暂停动画的执行 168 | */ 169 | public void stop() { 170 | if (mCubeGridObjects != null) { 171 | int maxLoopCount = mCubeGridObjects[2][0].getCurLoopCount(); 172 | for (int i = 0; i < mRowSize; i++) { 173 | for (int j = 0; j < mColumnSize; j++) { 174 | mCubeGridObjects[i][j].setMaxLoopCount(maxLoopCount); 175 | } 176 | } 177 | } 178 | } 179 | 180 | /** 181 | * 结束动画的执行 182 | */ 183 | public void destroy() { 184 | mHandler.removeMessages(ANIM_MSG); 185 | } 186 | 187 | public void resetAnim() { 188 | for (int i = 0; i < mRowSize; i++) { 189 | for (int j = 0; j < mColumnSize; j++) { 190 | if (mCubeGridObjects[i][j] != null) { 191 | mCubeGridObjects[i][j].setCurLoopCount(1); 192 | mCubeGridObjects[i][j].setMaxLoopCount(Integer.MAX_VALUE); 193 | } 194 | } 195 | } 196 | } 197 | 198 | /** 199 | * 使用Handler来循环执行动画的显示 200 | */ 201 | private void startAnimLoop() { 202 | if (mAnimView != null) { 203 | if (mCurValue <= mAnimTotalValue) { 204 | setFraction(mCurValue); 205 | mAnimView.invalidate(); 206 | mCurValue += ANIM_STEP_VALUE; 207 | mHandler.sendEmptyMessageDelayed(ANIM_MSG, ANIM_STEP_TIME); 208 | } else { 209 | if (mCubeGridAnimCallback != null) { 210 | mCubeGridAnimCallback.onAnimEnd(); 211 | } 212 | } 213 | } 214 | } 215 | 216 | /** 217 | * 在Canvas画出对应的显示CubeGridObject数组 218 | */ 219 | public void drawCanvas(Canvas canvas) { 220 | for (int i = 0; i < mRowSize; i++) { 221 | for (int j = 0; j < mColumnSize; j++) { 222 | mCubeGridObjects[i][j].drawCubeGrid(canvas); 223 | } 224 | } 225 | } 226 | 227 | /** 228 | * 设置对应每个CubeGridObject显示的比例 229 | * 230 | * @param curAnimValue 指定动画的大小值 231 | */ 232 | private void setFraction(int curAnimValue) { 233 | int curCubeObjectAnimValue = 0; 234 | for (int i = 0; i < mRowSize; i++) { 235 | for (int j = 0; j < mColumnSize; j++) { 236 | curCubeObjectAnimValue = curAnimValue - mDelayTime[i][j]; 237 | if (curCubeObjectAnimValue > 0 && curCubeObjectAnimValue <= ANIM_CYCLE_VALUE * mLoopCount) { 238 | float animRate = (curCubeObjectAnimValue % ANIM_CYCLE_VALUE) * 1.0f / ANIM_CYCLE_VALUE; 239 | int curLoop = curCubeObjectAnimValue / ANIM_CYCLE_VALUE + 1; 240 | if (i == mRowSize - 1 && j == mColumnSize - 1 && mCubeGridAnimCallback != null) { 241 | mCubeGridAnimCallback.onAnimExecute(curLoop); 242 | } 243 | mCubeGridObjects[i][j].setCurLoopCount(curLoop); 244 | mCubeGridObjects[i][j].setFraction(getInterpolatorValue(getAnimFraction(animRate))); 245 | } else { 246 | mCubeGridObjects[i][j].setFraction(1.0f); 247 | } 248 | } 249 | } 250 | if (mCubeGridObjects[mRowSize - 1][0].getCurLoopCount() > mCubeGridObjects[mRowSize - 1][0].getMaxLoopCount()) { 251 | if (mCubeGridAnimCallback != null) { 252 | mCubeGridAnimCallback.onAnimEnd(); 253 | } 254 | mStoped = true; 255 | destroy(); 256 | resetAnim(); 257 | } 258 | } 259 | 260 | /** 261 | * 获取对应动画插值的valueR 262 | */ 263 | private float getInterpolatorValue(float input) { 264 | return getInterpolator().getInterpolation(input); 265 | } 266 | 267 | /** 268 | * 获取相应动画的插值器 269 | */ 270 | private TimeInterpolator getInterpolator() { 271 | if (mInterpolator == null) { 272 | mInterpolator = new EaseInOutCubicInterpolator(); 273 | } 274 | return mInterpolator; 275 | } 276 | 277 | /** 278 | * 根据动画执行的时间比率, 获取对应小方块大小比例 279 | * 0-0.35% 执行 1 -> 0 的缩小动画 280 | * 0.35%-0.7% 执行 0-> 1 的动画 281 | * 0.7% 维持1不变 282 | */ 283 | private float getAnimFraction(float animRate) { 284 | if (animRate <= 0.35f) { 285 | return 1 - (animRate / 0.35f); 286 | } else if (animRate <= 0.7f) { 287 | return (animRate - 0.35f) / 0.35f; 288 | } 289 | return 1f; 290 | } 291 | 292 | /** 293 | * 设置动画的回调 294 | */ 295 | public void setCubeGridAnimCallback(ICubeGridAnimCallback cubeGridAnimCallback) { 296 | mCubeGridAnimCallback = cubeGridAnimCallback; 297 | } 298 | 299 | /** 300 | * ease-in-out 效果文章资料: 301 | * http://easings.net/zh-cn 302 | * https://github.com/ai/easings.net 303 | * https://github.com/Fichardu/EaseAnimationInterpolator 304 | */ 305 | static class EaseInOutCubicInterpolator implements TimeInterpolator { 306 | 307 | @Override 308 | public float getInterpolation(float input) { 309 | if ((input *= 2) < 1.0f) { 310 | return 0.5f * input * input * input; 311 | } 312 | input -= 2; 313 | return 0.5f * input * input * input + 1; 314 | } 315 | } 316 | } 317 | --------------------------------------------------------------------------------