最近产品经理出了一个幺蛾子,说要通过一个小游戏来吸引用户获取奖励,当时过需求的时候,内心何止是一万个草泥马奔腾而过,简直是一亿个草泥马。过需求之后就冷静下来讨论该怎么实现,做完之后发现,其实也没有那么难!总的效果如下:

因为csnd上传最大不能超过2M所以把gif图放到github上了

点击查看效果图

仔细分析需求中的几个难点

1.小人走动效果

2.路线布局

3.小人走动的四个方向

4.动画效果:红包小人矿山铲子这是一类。前进后退踩雷奖励骰子次数这是一类。替换场景是单独的动画效果。

第一个问题:小人走动效果

这里使用的是drawBitmap配合invalidate来实现一个动态走动的效果。小人每一个方向都是4张图片,把这4张图片放到一个一维数组中,每次draw的时候需要的bitmap都从数组里面取。代码如下:

 public GameAnimation    mPersonAnim[]   = new GameAnimation[ANIM_COUNT];public void initAnimation(Context context) {// 这里可以用循环来处理总之我们需要把动画的ID传进去mPersonAnim[ANIM_DOWN] = new GameAnimation(context, new int[] { R.mipmap.img_hero_down_a, R.mipmap.img_hero_down_b, R.mipmap.img_hero_down_c, R.mipmap.img_hero_down_d }, true);mPersonAnim[ANIM_LEFT] = new GameAnimation(context, new int[] { R.mipmap.img_hero_left_a, R.mipmap.img_hero_left_b, R.mipmap.img_hero_left_c, R.mipmap.img_hero_left_d }, true);mPersonAnim[ANIM_RIGHT] = new GameAnimation(context, new int[] { R.mipmap.img_hero_right_a, R.mipmap.img_hero_right_b, R.mipmap.img_hero_right_c, R.mipmap.img_hero_right_d }, true);mPersonAnim[ANIM_UP] = new GameAnimation(context, new int[] { R.mipmap.img_hero_up_a, R.mipmap.img_hero_up_b, R.mipmap.img_hero_up_c, R.mipmap.img_hero_up_d }, true);}
public void DrawAnimation(Canvas Canvas, Paint paint, int x, int y) {// 如果没有播放结束则继续播放if (!mIsend) {Canvas.drawBitmap(mframeBitmap[mPlayID], x, y, paint);long time = System.currentTimeMillis();if (time - mLastPlayTime > ANIM_TIME) {mPlayID++;mLastPlayTime = time;if (mPlayID >= mFrameCount) {// 标志动画播放结束mIsend = true;if (mIsLoop) {// 设置循环播放mIsend = false;mPlayID = 0;}}}}}

这个走动效果是根据大神博客来实现的。

第二个问题:路线布局

看效果图,这是一个写死的不规则的布局,当时完全没有任何思路,后来讨论需求的时候说,总共15个场景,每个场景的路线都写死,并且都是5x6的格式,瞬间就明白使用什么方式了。没错,就是用recyclerview+GridLayoutManager来实现的,因为每一个场景服务端都会返回一个集合,每个单元格服务端都会告诉我们row和col,这样,我们布局起来就so easy了。总的一菊(句)花(话)就是:该显示的显示,不该显示的设置为gone。

@Override protected void mOnBindViewHolder(GameKingRingViewHolder holder, int position) {GameCellBean gameCellBean = list.get(position);if (gameCellBean == null) {return;}updateBlockBg(holder.idIvBlock);// 不同的背景,单元格的背景和颜色也不同if (gameCellBean.row != 0 && gameCellBean.col != 0) {holder.itemView.setVisibility(View.VISIBLE);} else {holder.itemView.setVisibility(View.GONE);}}

第三个问题:小人走动的四个方向

小人要根据布局中的路线去走动,可是布局中的格子是打乱的,谁也不知道下一个格子是向下还是向上(不同的方向,小人的图片不一样)。这里也可以通过服务端返回的row和col来判断,举个栗子,当前小人所在坐标row和col分别为2,2,假设下一个格子的row和col是2,1,那么小人下一步的方向是向左。假设下一个格子的row和col是1,2,那么小人下一步的方向是向上。具体代码如下

private void setPersonForwardDirectionAndSetCurrentGameBean() {if (currentPosition + 1 >= gameCellBeanList.size()) { // 替换场景sendMsgForwardToChangeScene();return;}// 例如当前currentGameBean的rowX=5,columnY=2GameCellBean frontGameBean = gameCellBeanList.get(currentPosition + 1);if (currentGameCellBean.row > frontGameBean.row) { // 向上换行 rowX为5,需要换成4if (currentGameCellBean.col == frontGameBean.col) {currentPosition++;mAnimationState = KingRingPersonAnimation.ANIM_UP;setCurrentGameBean(frontGameBean);}} else if (currentGameCellBean.row == frontGameBean.row) { // 判断左右方向 currentGameBean.columnY=0--frontGameBean.columnY=1if (frontGameBean.col > currentGameCellBean.col) { // 向右currentPosition++;mAnimationState = KingRingPersonAnimation.ANIM_RIGHT;setCurrentGameBean(frontGameBean);} else if (frontGameBean.col < currentGameCellBean.col) { // 向左currentPosition++;mAnimationState = KingRingPersonAnimation.ANIM_LEFT;setCurrentGameBean(frontGameBean);}} else if (currentGameCellBean.row < frontGameBean.row) { // 向下if (currentGameCellBean.col == frontGameBean.col) {currentPosition++;mAnimationState = KingRingPersonAnimation.ANIM_DOWN;setCurrentGameBean(frontGameBean);}}}

第四个问题:动画效果

public void startAnimProps(final int position, int propsType) {// 获取红包小人矿山铲子的动画效果int[] location = new int[2];if (propsType == GameKingRingAdapter.PROPS_RED_TOKEN || propsType == GameKingRingAdapter.PROPS_RED_DYNAMIC) {idIvRed.getLocationInWindow(location);} else if (propsType == GameKingRingAdapter.PROPS_PERSON || propsType == GameKingRingAdapter.PROPS_SHOVEL || propsType == GameKingRingAdapter.PROPS_MOUNTAIN) {idIvMerge.getLocationInWindow(location);}int[] location2 = new int[2];GameKingRingViewHolder viewHolder = (GameKingRingViewHolder) idRecyclerview.findViewHolderForAdapterPosition(position);viewHolder.idIvProps.getLocationInWindow(location2);int disX = location2[0] - location[0];int disY = location2[1] - location[1];ImageView imageView = new ImageView(this);imageView.setImageDrawable(viewHolder.idIvProps.getDrawable());FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);params.setMargins(location2[0], location2[1], 0, 0);// - viewHolder.idIvProps.getTop()idFlOtherContent.addView(imageView, params);idFlOtherContent.setVisibility(View.VISIBLE);viewHolder.idIvProps.setVisibility(View.INVISIBLE);imageView.animate().translationX(-disX).translationY(-disY).setDuration(1000).setListener(new Animator.AnimatorListener() {@Override public void onAnimationStart(Animator animation) {notifyItemChanged(position);}@Override public void onAnimationEnd(Animator animation) {idFlOtherContent.removeAllViews();}@Override public void onAnimationCancel(Animator animation) {}@Override public void onAnimationRepeat(Animator animation) {}}).start();}
public void startRewardBackAnim(int position, final int value) { // 前进或后退效果,踩雷和奖励骰子次数跟这个一样GameKingRingViewHolder gameKingRingViewHolder = (GameKingRingViewHolder) idRecyclerview.findViewHolderForAdapterPosition(position);ImageView idIvProps = gameKingRingViewHolder.idIvProps;ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(idIvProps, "alpha", 1, 0, 1, 0, 1, 0, 1);alphaAnim.setDuration(2000);alphaAnim.addListener(new Animator.AnimatorListener() {@Override public void onAnimationStart(Animator animation) {if (!animationStartFlag) {animationStartFlag = true;}}@Override public void onAnimationEnd(Animator animation) {if (animationStartFlag) {animationStartFlag = false;idIvPerson.removeLastStopCellIndex();idIvPerson.setBackStep(value);}}@Override public void onAnimationCancel(Animator animation) {}@Override public void onAnimationRepeat(Animator animation) {}});alphaAnim.start();}
public void updateSceneUIBySceneIndex(final String scenesName, final boolean forwardOrBack) { // 切换场景if (TextUtils.isEmpty(scenesName)) {return;}final ImageView imageView = new ImageView(this);FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);imageView.setBackgroundDrawable(idLlContent.getBackground());idFlOtherContent.addView(imageView, params);idFlOtherContent.setVisibility(View.VISIBLE);mGameKingRingAdapter.setScenesName(scenesName);checkBg(scenesName);if (forwardOrBack) {mGameKingRingService.updateAdapterList();}idIvPerson.setVisibility(View.GONE);ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(imageView, "translationY", 0, ActivityUtil.getScreenHeight(getApplication()));alphaAnim.setDuration(1000);alphaAnim.addListener(new Animator.AnimatorListener() {@Override public void onAnimationStart(Animator animation) {if (!animationStartFlag) {animationStartFlag = true;}}@Override public void onAnimationEnd(Animator animation) {if (animationStartFlag) {animationStartFlag = false;idFlOtherContent.removeAllViews();if (forwardOrBack) {mGameKingRingService.updateSceneUIBySceneIndexCompletion();}}}@Override public void onAnimationCancel(Animator animation) {}@Override public void onAnimationRepeat(Animator animation) {}});alphaAnim.start();}

最后啰嗦一下,整个界面的布局是使用FrameLayout。Recyclerview是第一层,小人是第二层,所有的其他动画效果是第三层(都是通过动态的添加view,然后对view进行动画处理)。

因为是实际的项目,所以源代码就不开放出来了,但是有时间会出一个demo出来。

android 自定义view之掷骰子小人走动的游戏相关推荐

  1. Android 自定义View之3D骰子旋转

    你可以指定立方体中每一面骰子的点数,颜色和背景,同时也可以指定执行的动画时间和动画插值器 更多有趣的view 使用 在根目录的build.gradle添加这一句代码: allprojects {rep ...

  2. android 自定义view 动画效果,Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又 ...

  3. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  4. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  5. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  6. Android自定义View:ViewGroup(三)

    自定义ViewGroup本质是什么? 自定义ViewGroup本质上就干一件事--layout. layout 我们知道ViewGroup是一个组合View,它与普通的基本View(只要不是ViewG ...

  7. android 自定义图形,Android自定义View之图形图像(模仿360的刷新球自定

    概述: 360安全卫士的那个刷新球(姑且叫它刷新球,因为真的不知道叫什么好,不是dota里的刷新球!!),里面像住了水一样,生动可爱,看似简单,写起来不太简单,本例程只是实现了它的部分功能而已,说实话 ...

  8. android代码实现手机加速功能,Android自定义View实现内存清理加速球效果

    Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...

  9. android中仿qq最新版抽屉,Android 自定义View实现抽屉效果

    Android 自定义View实现抽屉效果 说明 这个自定义View,没有处理好多点触摸问题 View跟着手指移动,没有采用传统的scrollBy方法,而是通过不停地重新布局子View的方式,来使得子 ...

最新文章

  1. for循环递减_判断语句_循环语句
  2. 关于可管理交换机VLAN的四种划分
  3. tensorflow 开始——创建定制化 Estimator(创建自定义评估器)
  4. iOS如何随意的穿插跳跃,push来pop去
  5. 电影编码JPEG2000与H.264
  6. diy一个android手机版下载,原神个人自制版
  7. GoldenGate中使用FILTER,COMPUTE 和SQLEXEC命令
  8. SQL Relay 0.49 发布,SQL 中间层
  9. TouchSlide - 大话主席
  10. matlab如何求开方,matlab中开方怎么表示
  11. 检测键盘的 CAPS LOCK 开关
  12. CSS+DIV-网页变幻(HTML篇)
  13. 7-7 选民投票 (20分)(不区分大小写投票)
  14. Google ArCode官网 ARCode支持机型
  15. SQLServer触发器的使用
  16. 汉诺塔问题的Java实现(递归与非递归)
  17. sqllyog可以连接oracle_sqlyog连接不上数据库
  18. 搭建LDAP服务器详细流程
  19. Spring框架基础入门
  20. PCI总线---PCI设备扫描过程

热门文章

  1. 10.2:Python中的布尔值是什么?
  2. CocosCreator配置资源服务器Nginx开启文件下载
  3. Unity3D经验(3)——飞机弹幕设计
  4. 刚刚小鹏G3i发布| 除了外观和内饰,一文读懂小鹏G3i智能驾驶的三大卖点
  5. 第四课 实战go语言改造php仿优酷-Redis改造优化接口
  6. 您的链接不是私密链接
  7. Unity加载配置文件的几种方式
  8. iOS开发之获取运营商和WIFI
  9. 翻牌模拟器 | Java
  10. 新创建了用户没有计算机图标,装了新系统桌面没有显示我的电脑解决方法