Android实现拼图小游戏
作者:Vivinia_Vivinia 发布时间:2023-03-01 11:25:46
标签:Android,拼图,小游戏
本文实例为大家分享了Android实现拼图小游戏的具体代码,供大家参考,具体内容如下
目标效果:
1.activity_main.xml页面:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.vivinia.puzzle.MainActivity">
<GridLayout
android:id="@+id/gl_main_game"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:rowCount="3"
android:columnCount="5">
</GridLayout>
</RelativeLayout>
2.MainActivity.java页面:
package com.example.vivinia.puzzle;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
/**
* 当前动画是否正在执行
*/
private boolean isAnimRun=false;
/**
*判断游戏是否开始*/
private boolean isGameStart=false;
/**
*利用二维数组创建若干个游戏小方块
*/
private ImageView[][] iv_game_arr = new ImageView[3][5];
/**
*游戏主界面
*/
private GridLayout gl_main_game;
/**
*当前空方块的实例保存
*/
private ImageView iv_null_ImageView;
/**
*当前手势
*/
private GestureDetector mDetector;
//非图片位置可以进行手势滑动
@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event); //手势监听
}
//在图片上可以进行手势滑动
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDetector=new GestureDetector(this, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
return false;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
}
/**
*一瞬间执行的方法
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float v, float v1) {
int type=getDirByGes(e1.getX(),e1.getY(),e2.getX(),e2.getY());
changeByDir(type);
return false;
}
});
setContentView(R.layout.activity_main);
//初始化游戏的若干个小方块
Bitmap bigBm=((BitmapDrawable)getResources().getDrawable(R.drawable.puzzle_bg)).getBitmap();
int everyWidth=bigBm.getWidth()/5; //每个游戏小方块的宽和高
for (int i = 0; i < iv_game_arr.length; i++) {
for (int j = 0; j < iv_game_arr[0].length; j++) {
Bitmap bm=Bitmap.createBitmap(bigBm,j*everyWidth,i*everyWidth,everyWidth,everyWidth);//根据行列来切成若干个游戏小图片
iv_game_arr[i][j]=new ImageView(this);
iv_game_arr[i][j].setImageBitmap(bm); //设置每一个游戏小方块图案
iv_game_arr[i][j].setPadding(2,2,2,2);//设置方块之间的间距
iv_game_arr[i][j].setTag(new GameData(i,j,bm)); //绑定自定义的数据
iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean flag=isHasByNullImageView((ImageView)view);
if(flag){
changeDataByImageView((ImageView)view);
}
}
});
}
}
//初始化游戏主界面,并添加若干个小方块
gl_main_game = (GridLayout) findViewById(R.id.gl_main_game);
for(int i=0;i<iv_game_arr.length;i++){
for(int j=0;j<iv_game_arr[0].length;j++){
gl_main_game.addView(iv_game_arr[i][j]);
}
}
/**
*设置最后一个方块为空的
*/
setNullImageView(iv_game_arr[2][4]);
/**
*初始化随机打乱顺序
*/
randomMove();
isGameStart=true; //开始状态
}
public void changeByDir(int type){
changeByDir(type,true);
}
/**
* 根据手势的方向,获取空方块相应的相邻位置如果存在方块,那么进行数据交换
* @param type 1:上,2:下,3:左,4:右
* @param isAnim true:有动画,false:无动画
*/
public void changeByDir(int type,boolean isAnim){
/**
*获取当前空方块的位置
*/
GameData mNullGameData= (GameData) iv_null_ImageView.getTag();
/**
* 根据方向,设置相应的相邻的位置的坐标
*/
int new_x=mNullGameData.x;
int new_y=mNullGameData.y;
if(type==1){ //要移动的方块在当前空方块的下边
new_x++;
}else if(type==2){ //要移动的方块在当前空方块的下边
new_x--;
}else if(type==3){ //要移动的方块在当前空方块的下边
new_y++;
}
else if(type==4){ //要移动的方块在当前空方块的下边
new_y--;
}
/**
*判断这个新坐标,是否存在
*/
if(new_x>=0&&new_x<iv_game_arr.length&&new_y>=0&&new_y<iv_game_arr[0].length){
if(isAnim) {
/**
*存在的话,开始移动
*/
changeDataByImageView(iv_game_arr[new_x][new_y]);
}else{
changeDataByImageView(iv_game_arr[new_x][new_y],isAnim);
}
}else{
//什么也不做
}
}
/**
*判断游戏结束的方法
*/
public void isGameOver(){
boolean isGameOver=true;
//要便利每个游戏小方块
for(int i=0;i<iv_game_arr.length;i++){
for(int j=0;j<iv_game_arr[0].length;j++){
//为空的方块数据不判断跳过
if(iv_game_arr[i][j]==iv_null_ImageView){
continue;
}
GameData mGameData= (GameData) iv_game_arr[i][j].getTag();
if(!mGameData.isTrue()){
isGameOver=false;
break;
}
}
}
//根据一个开关变量决定游戏是否结束,结束时给提示
if(isGameOver){
Toast.makeText(this,"游戏结束",Toast.LENGTH_LONG).show();
}
}
/**
* 手势判断,是向左还是向右
* @param start_x 手势的起始点x
* @param start_y 手势的起始点y
* @param end_x 手势的终止点x
* @param end_y 手势的起始点y
* @return 1:上,2:下,3:左,4:右
*/
public int getDirByGes(float start_x,float start_y,float end_x,float end_y){
boolean isLeftOrRight=(Math.abs(start_x-end_x)>Math.abs(start_y-end_y))?true:false; //是否左右
if(isLeftOrRight){ //左右
boolean isLeft=start_x-end_x>0?true:false;
if(isLeft){
return 3;
}else{
return 4;
}
}else{ //上下
boolean isUp=start_y-end_y>0?true:false;
if(isUp){
return 1;
}else{
return 2;
}
}
}
/**
* 随机打乱顺序
*/
public void randomMove(){
//打乱的次数
for(int i=0;i<10;i++){
//根据手势开始交换,无动画
int type=(int)(Math.random()*4)+1;
changeByDir(type,false);
}
}
public void changeDataByImageView(final ImageView mImageView) {
changeDataByImageView(mImageView,true);
}
/**
* 利用动画结束之后,交换两个方块的数据
* @param mImageView 点击的方块
* @param isAnim true:有动画,false:无动画
*/
public void changeDataByImageView(final ImageView mImageView,boolean isAnim){
if(isAnimRun){ //如果动画已经开始,则不做交换操作
return;
}
if(!isAnim){ //如果没有动画
GameData mGameData= (GameData) mImageView.getTag();
iv_null_ImageView.setImageBitmap(mGameData.bm);
GameData mNullGameData= (GameData) iv_null_ImageView.getTag();
mNullGameData.bm=mGameData.bm;
mNullGameData.p_x=mGameData.p_x;
mNullGameData.p_y=mGameData.p_y;
setNullImageView(mImageView); //设置当前点击的是空方块
if(isGameStart) {
isGameOver(); //成功时谈一个toast
}
return;
}
/**
*创建一个动画,设置好方向,移动的距离
*/
TranslateAnimation translateAnimation = null;
if(mImageView.getX()>iv_null_ImageView.getX()){ //当前点击的方块在空方块下边
//往上移动
translateAnimation=new TranslateAnimation(0.1f,-mImageView.getWidth(),0.1f,0.1f);
}else if(mImageView.getX()<iv_null_ImageView.getX()){ //当前点击的方块在空方块下边
//往下移动
translateAnimation=new TranslateAnimation(0.1f,mImageView.getWidth(),0.1f,0.1f);
}
else if(mImageView.getX()>iv_null_ImageView.getY()){ //当前点击的方块在空方块下边
//往左移动
translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,-mImageView.getWidth());
}
else if(mImageView.getX()<iv_null_ImageView.getY()){ //当前点击的方块在空方块下边
//往右移动
translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,mImageView.getWidth());
}
/**
* 设置动画的时长
*/
translateAnimation.setDuration(70);
/**
* 设置动画结束之后是否停留
*/
translateAnimation.setFillAfter(true);
/**
* 设置动画结束之后真正的交换数据
*/
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
isAnimRun=true; //动画开始
}
@Override
public void onAnimationEnd(Animation animation) {
isAnimRun=false; //动画结束
/**
*结束之后,清除动画
*/
mImageView.clearAnimation();
GameData mGameData= (GameData) mImageView.getTag();
iv_null_ImageView.setImageBitmap(mGameData.bm);
GameData mNullGameData= (GameData) iv_null_ImageView.getTag();
mNullGameData.bm=mGameData.bm;
mNullGameData.p_x=mGameData.p_x;
mNullGameData.p_y=mGameData.p_y;
setNullImageView(mImageView); //设置当前点击的是空方块
if(isGameStart) {
isGameOver(); //成功时谈一个toast
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
/**
* 执行动画
*/
mImageView.startAnimation(translateAnimation);
}
/**
* 设置某个方块为空方块
* @param mImageView 当前要设置为空的方块的实例
*/
public void setNullImageView(ImageView mImageView){
mImageView.setImageBitmap(null); //设置为空
iv_null_ImageView=mImageView;
}
/**
* 判断当前点击的方块,是否与空方块的位置关系是相邻关系
* @param mImageView 所点击的方块
* @return true:相邻;false:不相邻
*/
public boolean isHasByNullImageView(ImageView mImageView){
/**
*分别获取当前空方块的位置与点击方块的位置,通过x,y两边都差1的方式判断
*/
GameData mNullGameData= (GameData) iv_null_ImageView.getTag(); //空方块身上的数据
GameData mGameData= (GameData)mImageView.getTag(); //点击方块身上的数据
if(mNullGameData.y==mGameData.y&&mGameData.x+1==mNullGameData.x){ //当前点击的方块在空方块的上边
return true;
}else if(mNullGameData.y==mGameData.y&&mGameData.x-1==mNullGameData.x){ //当前点击的方块在空方块的下边
return true;
}else if(mNullGameData.y==mGameData.y+1&&mGameData.x==mNullGameData.x){ //当前点击的方块在空方块的左边
return true;
}else if(mNullGameData.y==mGameData.y-1&&mGameData.x+1==mNullGameData.x){ //当前点击的方块在空方块的右边
return true;
}
return false;
}
/**
* 每个游戏小方块上要绑定的数据
*/
class GameData{
/**
*每个小方块的实际位置x
*/
public int x=0;
/**
*每个小方块的实际位置x
*/
public int y=0;
/**
*每个小方块的图片
*/
public Bitmap bm;
/**
*每个小方块的图片的位置
*/
public int p_x=0;
/**
*每个小方块的图片的位置
*/
public int p_y=0;
public GameData(int x, int y, Bitmap bm) {
this.x = x;
this.y = y;
this.bm = bm;
this.p_x = x;
this.p_y = y;
}
/**
* 每个小方块的位置是否正确
* @return true:正确,false:不正确
*/
public boolean isTrue() {
if(x==p_x&&y==p_y) {
return true;
}
return false;
}
}
}
3.设置去掉标题栏样式
styles.xml页面:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="MyAppTheme" parent="AppTheme">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
</resources>
4.清单文件中使用该样式
AndroidManifest.xml页面:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.vivinia.puzzle">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/MyAppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
源码下载:点击打开链接
来源:https://blog.csdn.net/hester_hester/article/details/73496013
0
投稿
猜你喜欢
- 在正式的进入主题之前,我们先来了解下深拷贝和前拷贝的概念:浅拷贝:会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝,如果属性是基本
- 1、三元运算符:class Program {  
- 1. 前言Spring提供了xml、注解、JavaConfig多种方式来配置bean,不论何种方式,Spring最终都会将bean封装成Be
- 在项目中,时常会有异步调用的需求web.xml配置<servlet> <description>spri
- 所谓c#的委托就是说把函数当参数来传递。这个在js完全就用不着搞什么委托东西,直接转就是了。而对于C#来说则不是这样!一个函数,如果它的参数
- Android开发中,难免会遇到需要加解密一些数据内容存到本地文件、或者通过网络传输到其他服务器和设备的问题,但并不是使用了加密就绝对安全了
- 过滤器实现过滤器需要实现 javax.servlet.Filter 接口。重写三个方法。其中 init() 方法在服务启动时执行,destr
- 采用继承Thead类实现多线程:优势:编写简单,如果需要访问当前线程,只需使用this即可,无需使用Thead.currentThread(
- java 中newInstance()方法和new关键字的区别* 它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新
- 通过ssh实现服务器文件上传下载写在前面的话之前记录过一篇使用apache的FTP开源组件实现服务器文件上传下载的方法,但是后来发现在删除的
- 前后端分离开发中,一般都会遇到请求跨域问题。而且一般也会遇到登陆失效问题。今天就以springboot和vue为例来看如何解决上述问题增加过
- **请注意!请注意!!!**今天讲给大家讲解非常“有用”的设计模式,解释器模式!!!设计模式有三大种类,一种是创建型模式,一种是结构型模式,
- 概述工作电脑用了3年多了,100G的C盘也快吃不消了,每次打开看到C盘红了,总要用清理工具清理一下子.不知道怎么最近清理工具清理
- 本文实例讲述了C#使用Word中的内置对话框的方法,分享给大家供大家参考。具体实现方法如下:使用 Microsoft Office Word
- 算法中递归的一个典型应用就是遍历目标文件夹,并把该文件夹下的所有文件和文件夹打印或显示出来,还可以递归计算出目标文件夹的总大小。本文即以实例
- RestTemplate设计是为了Spring更好的请求并解析Restful风格的接口返回值而设计的,通过这个类可以在请求接口时直接解析对应
- 前言jdchain是京东数科开源的区块链平台,目标是实现一个面向企业应用场景的通用区块链框架系统,能够作为企业级基础设施,为业务创新提供高效
- 什么是递归?用Java写一个简单的递归程序递归的定义递归(recursion):以此类推是递归的基本思想,将规模大的问题转化为规模小的问题来
- 简述:观察Byte值转为字符写入文件如果在java里用byte打印出来只有33 到 126的输出字符比较正常此外发现Byte值为13是空格,
- MyBatis提供了 * 接口,我们可以实现自己的 * ,将其作为一个plugin装入到SqlSessionFactory中。 首先要说的是