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

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

因为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=2
		GameCellBean frontGameBean = gameCellBeanList.get(currentPosition + 1);
		if (currentGameCellBean.row > frontGameBean.row) { // 向上换行 rowX为5,需要换成4
			if (currentGameCellBean.col == frontGameBean.col) {
				currentPosition++;
				mAnimationState = KingRingPersonAnimation.ANIM_UP;
				setCurrentGameBean(frontGameBean);
			}
		} else if (currentGameCellBean.row == frontGameBean.row) { // 判断左右方向 currentGameBean.columnY=0--frontGameBean.columnY=1
			if (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出来。



猜你喜欢

转载自blog.csdn.net/u010648159/article/details/80638788