Android如何自定义View实现拼图小游戏
发表于:2024-11-30 作者:千家信息网编辑
千家信息网最后更新 2024年11月30日,这篇文章将为大家详细讲解有关Android如何自定义View实现拼图小游戏,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。PuzzleLayoutView:publi
千家信息网最后更新 2024年11月30日Android如何自定义View实现拼图小游戏
这篇文章将为大家详细讲解有关Android如何自定义View实现拼图小游戏,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
PuzzleLayoutView:
public class PuzzleLayoutView extends RelativeLayout implements View.OnClickListener { //表示将其切成2*2拼图(默认4块) private int mColumn = 2; //容器的内边距 private int mPadding; //每个块块的边距(横,纵 3:表示间距为3dp) private int mMargin = 3; //存储ImageView private ImageView[] mGamePintuItems; //Item的宽度(一致) private int mItemWidth; //游戏的图片 private Bitmap mBitmap; //切图后的存储 private ListmItemBitmaps; //操作次数 private boolean once; //容器宽度(游戏面板 高宽一致) private int mWidth; //设置游戏是否成功 private boolean isGameSuccess; //设置游戏是否失败 private boolean isGameOver; public GamePintuListner mListner; public PuzzleLayoutView(Context context) { this(context, null); } public PuzzleLayoutView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PuzzleLayoutView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics());//将dp转化为px,或xp转化为px mPadding = min(getPaddingLeft(), getPaddingRight(), getPaddingTop(), getPaddingBottom()); } //接口方法 public interface GamePintuListner { void nextLevel(int nextLevel);//下一关 void timechanged(int currentTime);//关卡时间 void gameover();//游戏结束 } public void setOnGamePintuListner(GamePintuListner mListner) { this.mListner = mListner; } private int level = 1; private static final int TIME_CHANGED = 0X123; private static final int NEXT_LEVEL = 0X124; private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case TIME_CHANGED: if (isGameSuccess || isGameOver) return; if (mListner != null) { mListner.timechanged(mTime); //时间结束后,游戏结束 if (mTime == 0) { isGameOver = true; mListner.gameover(); } } mTime--; //延迟1秒发送 handler.sendEmptyMessageDelayed(TIME_CHANGED, 1000); break; case NEXT_LEVEL: level = level + 1;//切换到下一关 if (mListner != null) { mListner.nextLevel(level); } else { nextLevel(); } default: break; } } }; private boolean isTimeEnabled = false; private int mTime; /** * 设置是否启动时间 (默认不启动) * * @param isTimeEnabled */ public void setTimeEnabled(boolean isTimeEnabled) { this.isTimeEnabled = isTimeEnabled; } /** * 获取当前布局的大小(正方形) */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //取宽和高中的最小值 mWidth = Math.min(getMeasuredHeight(), getMeasuredWidth()); if (!once) { //调用进行切图,以及排序(方法) initBitmap(); //调用设置ImageView(Item)的宽高等属性(方法) initItem(); //判断是否开启时间(方法调用) checkTimeEnable(); once = true; } setMeasuredDimension(mWidth, mWidth);//强制调用使面板为正方形 } /** * 判断是否开启时间 */ private void checkTimeEnable() { if (isTimeEnabled) { //根据当前等级设置时间 countTimeBaseLevel(); //通知线程更新关卡时间 handler.sendEmptyMessage(TIME_CHANGED); } } private void countTimeBaseLevel() { mTime = (int) Math.pow(2, level) * 60;//第一关120秒 第二关:240 第三关:480 } /** * 进行切图,以及排序方法 */ private void initBitmap() { //将图片引入 if (mBitmap == null) { mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic_view);//注意此处的导包 } mItemBitmaps = ImageSplitterUtil.sqlitImage(mBitmap, mColumn);//返回长度为4 (2*2) //使用sort进行乱排序 Collections.sort(mItemBitmaps, new Comparator () { public int compare(ImagePieceBean a, ImagePieceBean b) {//注意此处的a,b //是否大于0.5具有不确定性 return Math.random() > 0.5 ? 1 : -1; } }); } /** * 设置ImageView(Item)的宽高等属性方法 */ private void initItem() { //容器的宽度-Item内边距 =所有小块块加起来的/Item个数(宽度) 2:左边和右边边距 mItemWidth = (mWidth - mPadding * 2 - mMargin * (mColumn - 1)) / mColumn; mGamePintuItems = new ImageView[mColumn * mColumn];//界面块块个数相* //生成我们的Item,设置Rule(Item间的关系,高矮等) for (int i = 0; i < mGamePintuItems.length; i++) { ImageView item = new ImageView(getContext()); /** * item点击事件 */ item.setOnClickListener(this); item.setImageBitmap(mItemBitmaps.get(i).getBitmap());//此前以进行过乱排序 mGamePintuItems[i] = item;//保存Item item.setId(i + 1); //在Item的tag中存储了index item.setTag(i + "_" + mItemBitmaps.get(i).getIndex()); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(mItemWidth, mItemWidth); //[设置游戏规则] //设置Item间横向间隙,通过rightMargin //不是最后一列 if ((i + 1) % mColumn != 0) { lp.rightMargin = mMargin; } //不是第一列 if (i % mColumn != 0) { lp.addRule(RelativeLayout.RIGHT_OF, mGamePintuItems[i - 1].getId()); } //如果不是第一行,设置topMargin和rule if (i + 1 > mColumn) { lp.topMargin = mMargin; lp.addRule(RelativeLayout.BELOW, mGamePintuItems[i - mColumn].getId()); } addView(item, lp);//添加到RelativeLayout中 } } /** * 当过关失败,时间停止时调用此方法(重新开始此关卡) */ public void restart() { isGameOver = false;//重置当前关卡 mColumn--; nextLevel(); } public void nextLevel() { this.removeAllViews();//移除当前所有View mAnimLayout = null; mColumn++;//由2*2 变为3*3游戏面版 isGameSuccess = false;//游戏未成功(新的开始) checkTimeEnable();//下一关时间重新计算 initBitmap(); initItem(); } /** * 获取多个参数的最小值 */ private int min(int... params) {//...传多个参数 int min = params[0];//获取最小的 for (int param : params) {//发现最小的则赋值 if (param < min) { min = param; } } return min; } /** * 点击事件 */ private ImageView mFirst;//点击的IItem private ImageView mSecond; public void onClick(View v) { if (isAniming) return; //两次点击同一个Item if (mFirst == v) { mFirst.setColorFilter(null); mFirst = null; return; } if (mFirst == null) { mFirst = (ImageView) v; mFirst.setColorFilter(Color.parseColor("#5551c4d4"));//设置选中Item时的颜色(55为半透明) } else { mSecond = (ImageView) v; //交换我们的Item exchangeView(); } } /** * 动画层 */ private RelativeLayout mAnimLayout; //设置图片进行切换时用户疯狂点击 private boolean isAniming; /** * 交换我们的Item */ private void exchangeView() { mFirst.setColorFilter(null);//去除颜色状态(高亮) //调用构造我们的动画层方法 setUpAnimLayout(); //进行图片的交换 ImageView first = new ImageView(getContext()); final Bitmap firstBitmap = mItemBitmaps.get(getImageIdByTag((String) mFirst.getTag())).getBitmap(); first.setImageBitmap(firstBitmap); LayoutParams lp = new LayoutParams(mItemWidth, mItemWidth); lp.leftMargin = mFirst.getLeft() - mPadding; lp.topMargin = mFirst.getTop() - mPadding; first.setLayoutParams(lp); mAnimLayout.addView(first);//添加至动画层 ImageView second = new ImageView(getContext()); final Bitmap secondBitmap = mItemBitmaps.get(getImageIdByTag((String) mSecond.getTag())).getBitmap(); second.setImageBitmap(secondBitmap); LayoutParams lp2 = new LayoutParams(mItemWidth, mItemWidth); lp2.leftMargin = mSecond.getLeft() - mPadding; lp2.topMargin = mSecond.getTop() - mPadding; second.setLayoutParams(lp2); mAnimLayout.addView(second);//添加至动画层 //设置动画 TranslateAnimation animFirst = new TranslateAnimation(0, mSecond.getLeft() - mFirst.getLeft(), 0, mSecond.getTop() - mFirst.getTop()); animFirst.setDuration(500);//设置动画时间 animFirst.setFillAfter(true);//设置动画结束的位置 first.startAnimation(animFirst);//启动动画 TranslateAnimation animSecond = new TranslateAnimation(0, -mSecond.getLeft() + mFirst.getLeft(), 0, -mSecond.getTop() + mFirst.getTop()); animSecond.setDuration(500);//设置动画时间 animSecond.setFillAfter(true);//设置动画结束的位置 second.startAnimation(animSecond);//启动动画 /** * 监听动画事件 */ animFirst.setAnimationListener(new Animation.AnimationListener() { public void onAnimationStart(Animation animation) { mFirst.setVisibility(View.INVISIBLE);//隐藏动画 mSecond.setVisibility(View.INVISIBLE); isAniming = true; } public void onAnimationRepeat(Animation animation) { } public void onAnimationEnd(Animation animation) { String firstTag = (String) mFirst.getTag(); String secondTag = (String) mSecond.getTag(); mFirst.setImageBitmap(secondBitmap); mSecond.setImageBitmap(firstBitmap); mFirst.setTag(secondTag); mSecond.setTag(firstTag); mFirst.setVisibility(View.VISIBLE);//显示隐藏的图片 mSecond.setVisibility(View.VISIBLE); //此处为空,并不是将对象设置为null 而是将mFirst与Bitmap对象链接的线断开 mFirst = mSecond = null;//回到初始状态 mAnimLayout.removeAllViews();//移除动画层的两个View //调用判断游戏成功时的方法 checkSuccess(); isAniming = false; } }); } /** * 判断游戏是否成功 */ private void checkSuccess() { boolean isSuccess = true; for (int i = 0; i < mGamePintuItems.length; i++) { ImageView imageView = mGamePintuItems[i]; //getImageIndex:上面的方法名(注意此处方法名) if (getImageIndex((String) imageView.getTag()) != i) { isSuccess = false; } } if (isSuccess) { isGameSuccess = true; handler.removeMessages(TIME_CHANGED);//进入下一关时的时间 Log.e("TAG", "SUCCESS"); Toast.makeText(getContext(), "Success,level up 游戏升级!!!", Toast.LENGTH_LONG).show(); handler.sendEmptyMessage(NEXT_LEVEL); } } /** * 根据tag获取Id */ public int getImageIdByTag(String tag) { String[] split = tag.split("_"); return Integer.parseInt(split[0]);//拿ID } public int getImageIndex(String tag) { String[] split = tag.split("_"); return Integer.parseInt(split[1]);//拿ID } /** * 构造我们的动画层 */ private void setUpAnimLayout() { if (mAnimLayout == null) { mAnimLayout = new RelativeLayout(getContext()); addView(mAnimLayout);//添加到游戏面板中 } }}
工具类:ImageSplitterUtil
public class ImageSplitterUtil { /** * 传入bitmap,切成piece*piece块 */ public static ListsqlitImage(Bitmap bitmap, int piece) { List ImagePieceBeans = new ArrayList<>(); int width = bitmap.getWidth();//拿到图片宽高 int height = bitmap.getHeight(); int pieceWidth = Math.min(width, height) / piece;//得到每一块的宽度 for (int i = 0; i < piece; i++) {//切第一行 for (int j = 0; j < piece; j++) {//循环切第二,三行 ImagePieceBean ImagePieceBean = new ImagePieceBean(); ImagePieceBean.setIndex(j + i * piece);//第一次i为0,第0行 j++递增 0-6 int x = j * pieceWidth;//第一次循环X,Y为0 int y = i * pieceWidth; ImagePieceBean.setBitmap(Bitmap.createBitmap(bitmap, x, y, pieceWidth, pieceWidth)); ImagePieceBeans.add(ImagePieceBean); } } return ImagePieceBeans; }}
实体类:ImagePieceBean
public class ImagePieceBean { private int index; //表示当前第几块 private Bitmap bitmap; //当前图片 public ImagePieceBean() { } //快捷键构造方法 Source 倒3 public ImagePieceBean(int index, Bitmap bitmap) { this.index = index; this.bitmap = bitmap; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public Bitmap getBitmap() { return bitmap; } public void setBitmap(Bitmap bitmap) { this.bitmap = bitmap; } public String toString() { return "ImagePiece [index=" + index + ", bitmap=" + bitmap + "]"; }}
使用方法:GameActivity
/** * 总结: * 1.自定义控件选择,九宫格,RelativeLayout, id+Rule * 2.切图 * 3.动画图层 * 4.pause resume restart * 5.游戏时间 Handler sendMessageDelayed() 延迟一秒发送线程 */public class GameActivity extends AppCompatActivity { private PuzzleLayoutView puzzleLayoutView; private TextView mLevel, mTime; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_game); mLevel = this.findViewById(R.id.id_level); mTime = this.findViewById(R.id.id_time); puzzleLayoutView = this.findViewById(R.id.puzzle_layout_view); puzzleLayoutView.setTimeEnabled(true); //监听事件 puzzleLayoutView.setOnGamePintuListner(new PuzzleLayoutView.GamePintuListner() { public void timechanged(int currentTime) { //此处为int 注意加"" mTime.setText(currentTime + "秒"); } public void nextLevel(final int nextLevel) { //弹出提示框 new AlertDialog.Builder(GameActivity.this).setTitle("游戏信息") .setMessage("游戏升级").setPositiveButton("进入下一关", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { //游戏结束后,调用下一关 puzzleLayoutView.nextLevel(); mLevel.setText("第" + +nextLevel + "关"); } }).show(); } public void gameover() { //弹出提示框 new AlertDialog.Builder(GameActivity.this).setTitle("游戏信息") .setMessage("游戏结束!").setPositiveButton("是否继续该关卡?", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { puzzleLayoutView.restart();//重新启动 } }).setNegativeButton("是否放弃该游戏!", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { finish(); } }).show(); } }); }}
对应布局:activity_game
注意:pic_view图片资源自行更换
关于"Android如何自定义View实现拼图小游戏"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
动画
时间
方法
图片
一关
关卡
宽度
最小
成功
块块
事件
容器
篇文章
面板
排序
小游戏
一致
个数
位置
信息
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
主机数据库无法登录是什么意思
网络安全动画小视频
建立健全网络安全制服
中指数据库密码时限
房屋销售管理系统数据库c语言
网络安全监察大队职责
ui需要懂软件开发么
狼狗视频软件开发
数据库完整性约束
武汉数据库培训视频教程
服务器按作用分类
软件开发个人如何缴税
奇迹世界全球服务器
怎么解决网络安全法
中国网易版我的世界服务器招人
羽生柚子服务器不是包吃包住的吗
音频工作站服务器
今天网络安全股走势分析
山西网络技术开发口碑推荐
网络安全稿件
网络安全军事上
网络安全知识判断题答案
吴江网络技术咨询
数据库备份数据位置
网络安全课后总结jpg格式
华为云的服务器x86
工业网络安全
音频工作站服务器
征信数据库的题
大型游戏机软件开发