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

因为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. Mac vscode 调试打印有问题 输出缓冲区 “\r\n“
  2. 犟泥巴php集训营,想要开发自己的PHP框架需要那些知识储备?
  3. POJ-3662 Telephone Lines 二分+双端队列
  4. PHP 7.3声称速度比PHP 5快3倍还多,值得更新了!
  5. java使用druid maven_SpringMVC+Spring+Mybatis整合,使用druid连接池,声明式事务,maven配置...
  6. mysql web日志_mysql日志管理
  7. Java黑皮书课后题第5章:*5.9(找出得最高分的前两个学生)编写程序,提示用户输入学生的个数、每个学生名字及分数,最后显示获得最高分的学生
  8. 开发人员必读的11本最具影响力书籍
  9. 前端学习(686):for循环
  10. 百度SEO emlog虚拟源码商城模板
  11. angular cli_使用Angular CLI连接到服务器的最佳方法
  12. 2021 Gartner云数据库魔力象限,阿里云、华为云成国内唯二
  13. 菜鸟学习Spring——60s配置XML方法实现简单AOP
  14. “编程能力差,90%会输在这点上!”谷歌开发:方法不对,努力也白费
  15. python是什么意思中文、好学吗-学习python难吗?是不是越低级的程序越难学,越高级的程序越简单?...
  16. Android Studio导入从Github下载的源码
  17. WebRequest中的工厂方法模式
  18. 我的电脑上的软件推荐
  19. 安川机器人如何注释化指令_安川机器人 命令介绍 内部版
  20. 【蓝桥杯单片机组模块】13、NEC 红外通信 - vs1838B

热门文章

  1. 专访纪鹏程:为什么说易点租是互联网金融公司
  2. js 数组对象sort()排序(升序降序)
  3. 写给计算机老师的一封信800,给老师的一封信800字(精选6篇)
  4. 4G DTU及工业无线路由器的铁塔检测应用
  5. IBM AIX 5.3 系统管理 -- 磁盘存储管理二
  6. Latex 入门笔记(2) 常用语法
  7. HTTP(Request)
  8. Linux C通过域名解析得到IP地址
  9. 使用LibJpg保存JPG图像或数据
  10. 内核调试:一次多线程调试与KASAN检测实例