Android拼圖游戲 玩轉(zhuǎn)從基礎(chǔ)到應(yīng)用手勢(shì)變化
相信大家在小的時(shí)候都玩過拼圖游戲,現(xiàn)如今,手機(jī)普及,能在手機(jī)上玩的游戲越來越多,于是乎,重溫小時(shí)候,編寫這個(gè)簡易拼圖游戲,而且也能進(jìn)一步加深A(yù)ndroid的一些基礎(chǔ)知識(shí)。
老規(guī)矩,先是效果圖:

這里我把為了演示效果,把圖片打亂的很少,在代碼里可以更改。
首先,有個(gè)默認(rèn)的圖片,可以用來拼圖,也可以選擇你喜歡的圖片進(jìn)行拼圖,拼圖的過程會(huì)記錄移動(dòng)的步數(shù),并且當(dāng)游戲勝利的時(shí)候會(huì)彈出一個(gè)笑臉提示,游戲勝利,用了多少步數(shù)。
ps:感興趣的完全可以繼續(xù)在這上面進(jìn)行擴(kuò)展,比如增加游戲難度的選項(xiàng),可以將圖片分成更多的小方塊等等。
大體思路:將大圖切割成各個(gè)小方塊,用數(shù)組記錄每個(gè)小方塊的信息,用GridLayout來顯示各個(gè)小方塊,并且將某個(gè)小方塊標(biāo)記為空白方塊(空白方塊可以和相鄰方塊進(jìn)行交換),在GridLayout上的各個(gè)小方塊上增加點(diǎn)擊事件和在整個(gè)屏幕上添加手勢(shì)事件,每次點(diǎn)擊或者有手勢(shì)時(shí),判斷小方塊是否能移動(dòng),最后在游戲勝利時(shí)彈出勝利提示。
話不多說,接下來,就是一步步實(shí)現(xiàn)拼圖游戲的過程啦。
1.小方塊相關(guān)的類。
這是小方塊的各種變量的item,類,用來管理將大圖切割成每個(gè)小方塊的每個(gè)小方塊的信息。很簡單,就是各種變量和Setter和Getter方法直接上代碼~
/**
* Created by yyh on 2016/10/21.
*/
public class GameItemView{
/**
* 每個(gè)小方塊的信息
*/
//每個(gè)小方塊的實(shí)際位置x,
private int x=0;
//每個(gè)小方塊的實(shí)際位置y,
private int y=0;
//每個(gè)小方塊的圖片,
private Bitmap bm;
//每個(gè)小方塊的圖片位置x,
private int p_x=0;
//每個(gè)小方塊的圖片位置y.
private int p_y=0;
public GameItemView(int x, int y, Bitmap bm) {
super();
this.x = x;
this.y = y;
this.bm = bm;
this.p_x=x;
this.p_y=y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public Bitmap getBm() {
return bm;
}
public void setBm(Bitmap bm) {
this.bm = bm;
}
public int getP_x() {
return p_x;
}
public void setP_x(int p_x) {
this.p_x = p_x;
}
public int getP_y() {
return p_y;
}
public void setP_y(int p_y) {
this.p_y = p_y;
}
/**
* 判斷每個(gè)小方塊的位置是否正確
* @return
*/
public boolean isTrue(){
if (x==p_x&&y==p_y){
return true;
}
return false;
}
}
2.主界面的布局
主界面很簡單,一個(gè)Button,用來換圖片,一個(gè)ImageView,用來顯示原圖,一個(gè)GridLayout用來進(jìn)行拼圖游戲,最后,一個(gè)TextView,用來顯示完成這個(gè)拼圖用了多少步數(shù)。布局如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="@+id/ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/bt_choice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="選擇圖片"
android:adjustViewBounds="true"
/>
</LinearLayout>
<ImageView
android:id="@+id/iv"
android:layout_below="@id/ll"
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/haizei"
android:layout_marginTop="3dp"
></ImageView>
<!-- 游戲的主界面-->
<GridLayout
android:layout_marginTop="3dp"
android:layout_below="@id/iv"
android:id="@+id/gl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnCount="5"
android:rowCount="3"
android:adjustViewBounds="true"
>
</GridLayout>
<TextView
android:id="@+id/tv_step"
android:layout_below="@id/gl"
android:layout_marginTop="3dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已用步數(shù):0"
android:textSize="26sp"
/>
</RelativeLayout>
3.打開圖片選擇圖片
給Button設(shè)置點(diǎn)擊事件,調(diào)用startActivityForResult(Intent intent,int requestCode);方法,來獲取圖片。
bt_choice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent= new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOICE_PHOTO);//打開相冊(cè)
}
});
在Activity中重寫onActivityResult(int requestCode, int resultCode, Intent data)方法來顯示選擇的圖片,以及初始化游戲。(圖片選擇完以后,就要進(jìn)行圖片的切割,和拼圖游戲的開始。)
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case CHOICE_PHOTO:
if (resultCode==RESULT_OK){
//判斷手機(jī)系統(tǒng)版本
if (Build.VERSION.SDK_INT>=19){
handleImageOnKitKat(data);
//得到imageview中的圖片
BitmapDrawable bitmapDrawable= (BitmapDrawable) photo.getDrawable();
bt_tupan=bitmapDrawable.getBitmap();
//將原來GridLayout中的小方塊移除,
removeGameItem();
//將新圖切割成小方塊并加入GridLayout.
setGameItem();
//開始游戲
startGame();
}else {
handleImageBeforeKitKat(data);
//得到imageview中的圖片
BitmapDrawable bitmapDrawable= (BitmapDrawable) photo.getDrawable();
bt_tupan=bitmapDrawable.getBitmap();
//將原來GridLayout中的小方塊移除,
removeGameItem();
//將新圖切割成小方塊并加入GridLayout.
setGameItem();
//開始游戲
startGame();
}
}
}
}
然后是選擇圖片的具體方法的實(shí)現(xiàn)函數(shù)。注釋很清楚,不多說。我們的重點(diǎn)在拼圖以及手勢(shì)變化的具體實(shí)現(xiàn),這里選擇圖片的方式很多。不多講,網(wǎng)上有現(xiàn)成的框架。
//手機(jī)不大于19的取數(shù)據(jù)方法
private void handleImageBeforeKitKat(Intent data) {
Uri uri =data.getData();
String imagePath = getImagePath(uri, null);
displayImage(imagePath);
}
/**
* 手機(jī)大于19的取數(shù)據(jù)方法
* @param data
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
private void handleImageOnKitKat(Intent data) {
String imagePath=null;
Uri uri=data.getData();
if (DocumentsContract.isDocumentUri(this,uri)){
//如果是document類型的url,則通過document的id處理。
String docId=DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())){
String id =docId.split(":")[1];//解析出數(shù)字格式的id;
String selection= MediaStore.Images.Media._ID+"="+id;
imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
}else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())){
Uri contenturi= ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId));
imagePath=getImagePath(contenturi,null);
}
}else if ("content".equalsIgnoreCase(uri.getScheme())){
//如果不是document類型的uri,則使用普通的方式處理。
imagePath=getImagePath(uri,null);
}
displayImage(imagePath);
}
/**
* 顯示圖片
* @param imagePath //圖片的路徑。
*/
private void displayImage(String imagePath) {
if (imagePath != null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
if (isHeigthBigWidth(bitmap)) {
Bitmap bt = rotaingImageView(bitmap);//將圖片旋轉(zhuǎn)90度。
Bitmap disbitmapt = ajustBitmap(bt);
photo.setImageBitmap(disbitmapt);
} else {
Bitmap disbitmap = ajustBitmap(bitmap);
photo.setImageBitmap(disbitmap);
}
}
}
/**
* 調(diào)整圖片的方向
* @param bitmap
* @return
*/
private Bitmap rotaingImageView(Bitmap bitmap) {
//旋轉(zhuǎn)圖片 動(dòng)作
Matrix matrix = new Matrix();;
matrix.postRotate(270);
// 創(chuàng)建新的圖片
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return resizedBitmap;
}
/**
* 得到圖片的路徑
* @param externalContentUri
* @param selection
* @return
*/
private String getImagePath(Uri externalContentUri, String selection) {
String path=null;
Cursor cursor=getContentResolver().query(externalContentUri, null, selection, null, null);
if (cursor!=null){
if (cursor.moveToFirst()){
path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
}
cursor.close();
return path;
}
4.拼圖的各個(gè)小方塊的形成過程。
看著各個(gè)小方塊,我們用GridLayout來實(shí)現(xiàn)是最為方便的。所以,用一個(gè)GridLayout來顯示大圖切割后的各個(gè)小方塊,用一個(gè)ImageView數(shù)組來保存各個(gè)小方塊的信息,并且,我們默認(rèn)把最后一個(gè)小方塊設(shè)置為空白方塊。
首先是各種需要的變量。注釋很清楚。
/** * 利用二維數(shù)組創(chuàng)建若干個(gè)游戲小方框 */ private ImageView [][] iv_game_arr=new ImageView[3][5]; /** *游戲主界面 * */ private GridLayout gl_game_layout; //小方塊的行和列 private int i; private int j; /**空方塊的全局變量*/ private ImageView iv_null_imagview;
接著是從Imageview獲取圖片,并且將圖片按照一定的行和列進(jìn)行切割(這里將拼圖設(shè)置為3行5列)。將切割后的各個(gè)小方塊的信息保存在一個(gè)ImageView數(shù)組中。給每個(gè)小方塊設(shè)置Tag,和點(diǎn)擊監(jiān)聽。
private void setGameItem() {
//調(diào)整圖片的尺寸
Bitmap abitmap=ajustBitmap(bt_tupan);
int ivWidth=getWindowManager().getDefaultDisplay().getWidth()/5;//每個(gè)游戲小方塊的寬和高。切成正方形
int tuWidth=abitmap.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(abitmap,j*tuWidth,i*tuWidth,tuWidth,tuWidth);
iv_game_arr[i][j]=new ImageView(this);
iv_game_arr[i][j].setImageBitmap(bm);//設(shè)置每一個(gè)小方塊的圖案
iv_game_arr[i][j].setLayoutParams(new RelativeLayout.LayoutParams(ivWidth, ivWidth));
//設(shè)置方塊之間的間距
iv_game_arr[i][j].setPadding(2, 2, 2, 2);
iv_game_arr[i][j].setTag(new GameItemView(i, j, bm)); //綁定自定義數(shù)據(jù)
iv_game_arr[i][j].setOnClickListener(new View.OnClickListener() {
.......
);
當(dāng)然,我們選擇的圖片不可能都是符合標(biāo)準(zhǔn)的大小的,所以,在切割圖片之前我們要對(duì)圖片進(jìn)行調(diào)整。將圖片調(diào)整為5:3的比例。(這樣切割成3行5列的小方塊才能正確切割)這里對(duì)于width,我把每個(gè)小方塊的間隔事先也算到里面去。
//調(diào)整圖片的大小
private Bitmap ajustBitmap(Bitmap bitmap) {
int width=getWindowManager().getDefaultDisplay().getWidth()-(iv_game_arr[0].length-1)*2;
int heigth=width/5*3;
Bitmap scaledBitmap=Bitmap.createScaledBitmap(bitmap, width, heigth, true);
return scaledBitmap;
}
將每個(gè)小方格放入到GridLayout中。
/**
* 將小方格放入GridLayout
*/
private void startGame() {
tv_step.setText("已用步數(shù):0");
for (i = 0; i <iv_game_arr.length; i++){
for (j = 0; j <iv_game_arr[0].length; j++){
gl_game_layout.addView(iv_game_arr[i][j]);
}
}
//將最后一個(gè)方塊設(shè)置為設(shè)置空方塊。
setNullImageView(iv_game_arr[i-1][j-1]);
5.小方塊的點(diǎn)擊事件和手勢(shì)判斷過程。
這里是拼圖游戲的核心,弄懂了小方塊的移動(dòng)變化規(guī)律,也就弄懂了拼圖游戲。
對(duì)于點(diǎn)擊事件,首先拿到被點(diǎn)擊的小方塊的各種信息(位置、圖案)和空白小方塊的位置信息,判斷被點(diǎn)擊的小方塊是否和空白小方塊相鄰,如果相鄰,就移動(dòng)交換數(shù)據(jù)(用TranslateAnimation來實(shí)現(xiàn)移動(dòng)動(dòng)畫),如果不相鄰則無操作。
a.判斷點(diǎn)擊方塊和空白方塊是否相鄰的方法
/**
* 判斷當(dāng)前點(diǎn)擊的方塊,是否和空方塊相鄰。
* @param imageView 當(dāng)前點(diǎn)擊的方塊
* @return true:相鄰。 false:不相鄰。
*/
public boolean isAdjacentNullImageView(ImageView imageView){
//獲取當(dāng)前空方塊的位置與點(diǎn)擊方塊的位置
GameItemView null_gameItemView= (GameItemView) iv_null_imagview.getTag();
GameItemView now_gameItem_view = (GameItemView) imageView.getTag();
if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()+1==null_gameItemView.getX()){//當(dāng)前點(diǎn)擊的方塊在空方塊的上面
return true;
}else if(null_gameItemView.getY()==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()+1){//當(dāng)前點(diǎn)擊的方塊在空方塊的下面
return true;
}else if(null_gameItemView.getY()==now_gameItem_view.getY()+1&&now_gameItem_view.getX()==null_gameItemView.getX()){//當(dāng)前點(diǎn)擊的方塊在空方塊的左面
return true;
}else if(null_gameItemView.getY()+1==now_gameItem_view.getY()&&now_gameItem_view.getX()==null_gameItemView.getX()){ ////當(dāng)前點(diǎn)擊的方塊在空方塊的右面
return true;
}
return false;
}
b.接著是如果相鄰就進(jìn)入方塊數(shù)據(jù)交換的方法
這里有個(gè)方法重載,是否需要?jiǎng)赢嬓Ч?,沒有動(dòng)畫效果的數(shù)據(jù)交換是為初始化游戲時(shí)打亂拼圖做準(zhǔn)備的。這里將核心交換代碼列出。每次交換后還要判斷是否游戲勝利(即是否拼圖完成~)。
//得到點(diǎn)擊方塊綁定的數(shù)據(jù)
GameItemView gameItemView = (GameItemView) itemimageView.getTag();
//將空方塊的圖案設(shè)置為點(diǎn)擊方塊
iv_null_imagview.setImageBitmap(gameItemView.getBm());
//得到空方塊綁定的數(shù)據(jù)
GameItemView null_gameItemView = (GameItemView) iv_null_imagview.getTag();
//交換數(shù)據(jù)(將點(diǎn)擊方塊的數(shù)據(jù)傳入空方塊)
null_gameItemView.setBm(gameItemView.getBm());
null_gameItemView.setP_x(gameItemView.getP_x());
null_gameItemView.setP_y(gameItemView.getP_y());
//設(shè)置當(dāng)前點(diǎn)擊的方塊為空方塊。
setNullImageView(itemimageView);
if (isStart){
isGameWin();//成功時(shí),會(huì)彈一個(gè)吐司。
}
c.交換時(shí)的動(dòng)畫設(shè)置
交換設(shè)置動(dòng)畫時(shí),首先判斷移動(dòng)的方向,根據(jù)方向設(shè)置不同的移動(dòng)動(dòng)畫,然后再監(jiān)聽動(dòng)畫完成后,進(jìn)行數(shù)據(jù)交換操作。即上面b.接著是如果相鄰就進(jìn)入方塊數(shù)據(jù)交換的方法.最后執(zhí)行動(dòng)畫。
//1.創(chuàng)建一個(gè)動(dòng)畫,設(shè)置方向,移動(dòng)的距離
//判斷方向,設(shè)置動(dòng)畫
if (itemimageView.getX()>iv_null_imagview.getX()){//當(dāng)前點(diǎn)擊的方塊在空方塊的上面
//下移
translateAnimation = new TranslateAnimation(0.1f,-itemimageView.getWidth(),0.1f,0.1f);
}else if (itemimageView.getX()<iv_null_imagview.getX()){//當(dāng)前點(diǎn)擊的方塊在空方塊的下面
//上移
boolean f=itemimageView.getX()<iv_null_imagview.getX();
//Log.i("點(diǎn)擊方塊","sssssssssssssssssssssssss"+f);
translateAnimation = new TranslateAnimation(0.1f,itemimageView.getWidth(),0.1f,0.1f);
}else if (itemimageView.getY()>iv_null_imagview.getY()){//當(dāng)前點(diǎn)擊的方塊在空方塊的左面
//右移
translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,-itemimageView.getWidth());
}else if(itemimageView.getY()<iv_null_imagview.getY()){//當(dāng)前點(diǎn)擊的方塊在空方塊的右面
//左移
translateAnimation=new TranslateAnimation(0.1f,0.1f,0.1f,itemimageView.getWidth());
}
//2.設(shè)置動(dòng)畫的各種參數(shù)
translateAnimation.setDuration(80);
translateAnimation.setFillAfter(true);
//3.設(shè)置動(dòng)畫的監(jiān)聽
translateAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
isAminMove=true;
}
@Override
public void onAnimationEnd(Animation animation) {
//動(dòng)畫結(jié)束,交換數(shù)據(jù)
......
}
//動(dòng)畫執(zhí)行
itemimageView.startAnimation(translateAnimation);
點(diǎn)擊事件的流程就完了,接下來是手勢(shì)判斷的事件。即不僅可以通過點(diǎn)擊小方塊進(jìn)行移動(dòng),也可以通過手勢(shì)移動(dòng)小方塊。
One.創(chuàng)建手勢(shì)對(duì)象
在onFling方法中完成手勢(shì)相關(guān)的操作。
//創(chuàng)建手勢(shì)對(duì)象
gestureDetector =new GestureDetector(this, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
//手勢(shì)相關(guān)的操作
......
}
接著我們onFling方法中做具體操作
Two.判斷手勢(shì)移動(dòng)的方向
根據(jù)返回值的不同得到不同的移動(dòng)方向。
/**
* 增加手勢(shì)滑動(dòng),根據(jù)手勢(shì)判斷是上下左右滑動(dòng)
* @param start_x 手勢(shì)起始點(diǎn)x
* @param start_y 手勢(shì)起始點(diǎn)y
* @param end_x 手勢(shì)終止點(diǎn) x
* @param end_y 手勢(shì)終止點(diǎn)y
* @return 1:上 2:下 3:左 4:右
*/
public int getDirctionByGesure(float start_x,float start_y,float end_x,float end_y){
boolean isLeftOrRight =(Math.abs(end_x-start_x)>Math.abs(end_y-start_y))?true:false; //是否是左右
if(isLeftOrRight){//左右
boolean isLeft=(end_x-start_x)>0?false:true;
if(isLeft){
return 3;
}else {
return 4;
}
}else{//上下
boolean isUp=(end_y-start_y)>0?false:true;
if (isUp){
return 1;
}else {
return 2;
}
}
}
Three.根據(jù)空方塊和移動(dòng)的方向,判斷能否移動(dòng)以及進(jìn)行移動(dòng)操作。
因?yàn)槭鞘謩?shì),移動(dòng)的肯定是空方塊周圍的方塊,所以重點(diǎn)就是要判斷空方塊在要移動(dòng)的方塊的那個(gè)方向,再根據(jù)方向判斷能否移動(dòng),進(jìn)行移動(dòng)操作。(其中changeDateByImageView()中的方法就是具體的方塊交換數(shù)據(jù)及移動(dòng)的操作。就是點(diǎn)擊事件的那個(gè)方法。)
/**重載changeByDirGes(int type)方法;
* 根據(jù)手勢(shì)的方向,對(duì)空方塊相鄰位置的方塊進(jìn)行移動(dòng)。
* @param type 方向的返回值 1:上 2:下 3:左 5:右
* @param isAnim 是否有動(dòng)畫 true:有動(dòng)畫,false:無動(dòng)畫
*/
public void changeByDirGes(int type,boolean isAnim){
//1.獲取當(dāng)前空方塊的位置。
GameItemView null_gameItemView= (GameItemView) iv_null_imagview.getTag();
int new_x=null_gameItemView.getX();
int new_y=null_gameItemView.getY();
//2.根據(jù)方向,設(shè)置相應(yīng)相鄰的位置坐標(biāo)。
if (type==1){//說明空方塊在要移動(dòng)的方塊的上面。
new_x++;
}else if (type==2){//空方塊在要移動(dòng)的方塊的下面
new_x--;
}else if (type==3){//空方塊在要移動(dòng)的方塊的左面
new_y++;
}else if (type==4){//空方塊在要移動(dòng)的方塊的右面
new_y--;
}
//3.判斷這個(gè)新坐標(biāo)是否存在
if(new_x>=0&&new_x<iv_game_arr.length&&new_y>=0&&new_y<iv_game_arr[0].length){
//存在,可以移動(dòng)交換數(shù)據(jù)
if(isAnim){//有動(dòng)畫
changeDateByImageView(iv_game_arr[new_x][new_y]);
}else{
changeDateByImageView(iv_game_arr[new_x][new_y],isAnim);
}
}else{
//什么也不做
}
}
好了,手勢(shì)事件也就大功告成了~
當(dāng)然這里有兩個(gè)要注意的地方。1.首先要對(duì)當(dāng)前的Activity設(shè)置onTouchEvent()方法,將onTouch事件交由手勢(shì)去處理,其次也要設(shè)置dispatchTouchEvent()方法,在里面也要向下分發(fā)給手勢(shì)事件,如果不設(shè)置向下分發(fā)給手勢(shì)判斷,那么在GridLayout里,就只能觸發(fā)點(diǎn)擊事件而手勢(shì)事件就不會(huì)起作用了。2.要增加一個(gè)是否在移動(dòng)過程中的flag,如果在移動(dòng)過程中,就什么也不做,要不然每次點(diǎn)擊小方塊即使在移動(dòng)過程中,也會(huì)觸發(fā)點(diǎn)擊事件從而又進(jìn)行動(dòng)畫移動(dòng),造成不好的用戶體驗(yàn)。
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
gestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
6.游戲開始打亂方塊以及游戲結(jié)束時(shí)彈出Toast提示的方法。
代碼很簡單,直接上代碼,其中,彈出的Toast是一個(gè)帶有自定義View動(dòng)畫的Toast.
//隨機(jī)打亂圖片的順序
public void randomOrder(){
//打亂的次數(shù),為了測(cè)試方便,設(shè)置很小。
for (int i=0;i<5;i++){
//根據(jù)手勢(shì),交換數(shù)據(jù),無動(dòng)畫。
int type = (int) (Math.random()*4)+1;
// Log.i("sssssssssfdfdfd","交換次數(shù)"+i+"type的值"+type);
changeByDirGes(type, false);
}
}
/**
* 判斷游戲結(jié)束的方法
*/
public void isGameWin(){
//游戲勝利標(biāo)志
boolean isGameWin =true;
//遍歷每個(gè)小方塊
for (i = 0; i <iv_game_arr.length; i++){
for (j = 0; j <iv_game_arr[0].length; j++){
//為空的方塊不判斷 跳過
if (iv_game_arr[i][j]==iv_null_imagview){
continue;
}
GameItemView gameItemView= (GameItemView) iv_game_arr[i][j].getTag();
if (!gameItemView.isTrue()){
isGameWin=false;
//跳出內(nèi)層循環(huán)
break;
}
}
if (!isGameWin){
//跳出外層循環(huán)
break;
}
}
//根據(jù)一個(gè)開關(guān)變量覺得游戲是否結(jié)束,結(jié)束時(shí)給提示。
if (isGameWin){
// Toast.makeText(this,"游戲勝利",Toast.LENGTH_SHORT).show();
ToastUtil.makeText(this,"恭喜你,游戲勝利,用了"+step+"步",ToastUtil.LENGTH_SHORT,ToastUtil.SUCCESS);
step=0;
}
}
好了,重要的部分都已經(jīng)完了,這里還有個(gè)自定義View的Toast,關(guān)于Toast的詳解,將在下篇文章,這里先簡單說明下自定義Toast的實(shí)現(xiàn)過程。
首先,新建一個(gè)SuccessToast類,(笑臉包括左眼,有眼,笑臉?。?。我們將核心的過程給出。利用動(dòng)畫,實(shí)現(xiàn)動(dòng)態(tài)笑臉畫制過程。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.STROKE);
//話笑臉弧
canvas.drawArc(rectF, 180, endAngle, false, mPaint);
mPaint.setStyle(Paint.Style.FILL);
if (isSmileLeft) {
//如果是左眼,畫左眼
canvas.drawCircle(mPadding + mEyeWidth + mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
}
if (isSmileRight) {
//如果是有眼,畫右眼。
canvas.drawCircle(mWidth - mPadding - mEyeWidth - mEyeWidth / 2, mWidth / 3, mEyeWidth, mPaint);
}
}
/**
* 開始動(dòng)畫的方法
* @param startF 起始值
* @param endF 結(jié)束值
* @param time 動(dòng)畫的時(shí)間
* @return
*/
private ValueAnimator startViewAnim(float startF, final float endF, long time) {
//設(shè)置valueAnimator 的起始值和結(jié)束值。
valueAnimator = ValueAnimator.ofFloat(startF, endF);
//設(shè)置動(dòng)畫時(shí)間
valueAnimator.setDuration(time);
//設(shè)置補(bǔ)間器。控制動(dòng)畫的變化速率
valueAnimator.setInterpolator(new LinearInterpolator());
//設(shè)置監(jiān)聽器。監(jiān)聽動(dòng)畫值的變化,做出相應(yīng)方式。
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mAnimatedValue = (float) valueAnimator.getAnimatedValue();
//如果value的值小于0.5
if (mAnimatedValue < 0.5) {
isSmileLeft = false;
isSmileRight = false;
endAngle = -360 * (mAnimatedValue);
//如果value的值在0.55和0.7之間
} else if (mAnimatedValue > 0.55 && mAnimatedValue < 0.7) {
endAngle = -180;
isSmileLeft = true;
isSmileRight = false;
//其他
} else {
endAngle = -180;
isSmileLeft = true;
isSmileRight = true;
}
//重繪
postInvalidate();
}
});
if (!valueAnimator.isRunning()) {
valueAnimator.start();
}
return valueAnimator;
}
然后新建一個(gè)success_toast_layout.xml,完成toast的布局。布局是左右(左邊笑臉view,右邊TextView的提示。)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00000000"
android:orientation="vertical">
<LinearLayout
android:id="@+id/base_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:layout_marginTop="25dp"
android:background="@drawable/background_toast"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.example.yyh.puzzlepicture.activity.Util.SuccessToast
android:id="@+id/successView"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical|left"
android:layout_margin="10px"
android:gravity="center_vertical|left" />
</LinearLayout>
<TextView
android:id="@+id/toastMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:padding="10dp"
android:text="New Text" />
</LinearLayout>
</LinearLayout>
最后新建一個(gè)ToastUtil類,管理自定義的Toast.
/**
* Created by yyh on 2016/10/25.
*/
public class ToastUtil {
public static final int LENGTH_SHORT = 0;
public static final int LENGTH_LONG = 1;
public static final int SUCCESS = 1;
static SuccessToast successToastView;
public static void makeText(Context context, String msg, int length, int type) {
Toast toast = new Toast(context);
switch (type) {
case 1: {
View layout = LayoutInflater.from(context).inflate(R.layout.success_toast_layout, null, false);
TextView text = (TextView) layout.findViewById(R.id.toastMessage);
text.setText(msg);
successToastView = (SuccessToast) layout.findViewById(R.id.successView);
successToastView.startAnim();
text.setBackgroundResource(R.drawable.success_toast);
text.setTextColor(Color.parseColor("#FFFFFF"));
toast.setView(layout);
break;
}
}
toast.setDuration(length);
toast.show();
}
}
這樣就可以在ManiActivity中調(diào)用這個(gè)自定義Toast了。
好了,完結(jié)。
游戲源碼:拼圖游戲的實(shí)現(xiàn)過程
gitHub:拼圖游戲。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android實(shí)現(xiàn)拼圖小游戲
- 基于Android平臺(tái)實(shí)現(xiàn)拼圖小游戲
- Android實(shí)現(xiàn)美女拼圖游戲詳解
- Android實(shí)現(xiàn)九宮格拼圖游戲
- Android自定義View實(shí)現(xiàn)拼圖小游戲
- Android利用ViewDragHelper輕松實(shí)現(xiàn)拼圖游戲的示例
- Android實(shí)現(xiàn)滑塊拼圖驗(yàn)證碼功能
- Android 簡單的實(shí)現(xiàn)滑塊拼圖驗(yàn)證碼功能
- Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼
- Android實(shí)現(xiàn)九格智能拼圖算法
相關(guān)文章
總結(jié)安卓(Android)中常用的跳轉(zhuǎn)工具
在大家日常開發(fā)的時(shí)候經(jīng)常會(huì)用到各式各樣的跳轉(zhuǎn),如跳轉(zhuǎn)到QQ、微信聊天界面、跳轉(zhuǎn)到聯(lián)系人界面或者跳轉(zhuǎn)到瀏覽器和照相機(jī)等等之類的,本文將常用到的一些跳轉(zhuǎn)集合到一起,這樣更方便大家以后使用,有需要的小伙伴們可以參考借鑒。2016-08-08
Android編程連接MongoDB及增刪改查等基本操作示例
這篇文章主要介紹了Android編程連接MongoDB及增刪改查等基本操作,簡單介紹了MongoDB功能、概念、使用方法及Android操作MongoDB數(shù)據(jù)庫的基本技巧,需要的朋友可以參考下2017-07-07
android Tween Animation屬性設(shè)置方法實(shí)例
在Android開發(fā)中,Animation是用來給控件制作效果的,本文介紹二種實(shí)現(xiàn)方法2013-11-11
Android超清晰6.0權(quán)限申請(qǐng)AndPermission
這篇文章主要介紹了Android超清晰6.0權(quán)限申請(qǐng)AndPermission,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android自定義SeekBar實(shí)現(xiàn)視頻播放進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android自定義SeekBar實(shí)現(xiàn)視頻播放進(jìn)度條的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
Android編程實(shí)現(xiàn)根據(jù)不同日期計(jì)算天數(shù)差的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)根據(jù)不同日期計(jì)算天數(shù)差的方法,涉及Android調(diào)用日期類Calendar實(shí)現(xiàn)時(shí)間運(yùn)算的相關(guān)技巧,需要的朋友可以參考下2016-03-03
Mac OS X 下有關(guān)Android adb用法詳解
這篇文章主要介紹了Mac OS X 下有關(guān)Android adb用法詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android取消EditText自動(dòng)獲取默認(rèn)焦點(diǎn)
本文主要介紹了Android取消EditText自動(dòng)獲取焦點(diǎn)默認(rèn)行為的方法,具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-03-03

