Android开心消消乐代码实例详解

突然想要在android上写一个消消乐的代码,在此之前没有系统地学过java的面向对象,也没有任何android相关知识,不过还是会一点C++。8月初开始搭建环境,在这上面花了相当多的时间,然后看了一些视频和电子书,对android有了一个大概的了解,感觉差不多了的时候就开始写了。

疯狂地查阅各种资料,反反复复了好几天后,也算是写出了个成品。原计划有很多地方还是可以继续写下去的,比如UI设计,比如动画特效,时间设计,关卡设计,以及与数据库的连接,如果可以的话还能写个联网功能,当然因为写到后期内心感到格外的疲倦,所以以上内容全部被砍掉了。

所以最后的成果就只有这样了……因为完全没有设计过所以非常难看,功能也只有消方块而已,图片还是从之前写的连连看里搬过来的。反正就算一个小的demo好了。

Android开心消消乐代码实例详解_第1张图片

布局

一开始设计的布局是xml里的,但是后来想了想有64个按钮这样复制下去实在是太不好了,所以就把按钮那一部分用代码布局了。

private void initBtn(int i,int j)
  {
		btn[8*i+j].setBackgroundDrawable
		(this.getResources().getDrawable(getStyle()));
		point p = new point(i,j,btn[8*i+j],id);
		btn[8*i+j].setTag(p);
		map[i][j] = num;
  }
 
//……
    LinearLayout vlayout = (LinearLayout)findViewById(R.id.vlayout);
    TableLayout tlayout = new TableLayout(this);
    TableRow row[] = new TableRow[8];
    for(int i=0;i<8;i++){
    	row[i] = new TableRow(this);
    	row[i].setGravity(Gravity.CENTER);
    	for(int j=0;j<8;j++){
    		btn[8*i+j] = new ImageButton(this);
    		btn[8*i+j].setLayoutParams(new TableRow.LayoutParams(38,38));
    		initBtn(i,j);
    		btn[8*i+j].setOnClickListener(listener);
    		row[i].addView(btn[8*i+j]);
    	}
    	tlayout.addView(row[i]);
    }
//……

其中getStyle()函数返回了按钮的样式id,该id是随机生成的。

point p存储了关于按钮的信息,它在按钮点击事件中会被使用。

android中的按钮有三种状态:点击态、普通态、焦点态。最后一个的意思是用户按方向键盘(或类似功能键)来控制哪个按钮处于焦点,这样就可以不通过鼠标对按钮进行操作。当然在这里并没有用到这个状态,只用到了前面两种,但是因为已经有现成图片了,就把三种状态都写入了btn?.xml(放在drawable文件夹下),使用的时候直接引用这个xml文件就可以了。

btn1.xml:


 
 
   
   
   
   
 

Android开心消消乐代码实例详解_第2张图片

判断是否可消去

在点击事件中,用两个变量记录先后点击的按钮信息,这两个变量是滚动使用的,也就是说,第一次点击存入变量1,第二次点击存入变量2,第三次点击存入变量1,第四次点击存入变量2……额外有一个布尔变来控制信息存入哪个变量。

每次点击后,都计算先后两次点击的按钮是否相邻。如果相邻,那么交换它们的位置。

我们用二维数组mark来记录每个方块是否可以消去,初始化为0。交换后,先一行行地扫描一遍,检查是否有相连的三个以上图案相同的按钮,如果有的话,把它们都做上标记。然后再一列列扫描一遍,同样检查是否有相连的三个以上图案相同的按钮。总体复杂度为O(2*n^2)

当然可以有更快的算法,但是在这个代码里,我们对计算的速度没有要求,甚至希望它慢一些(因为动画效果需要在这里停顿一下),所以就没有必要进行优化了。

那么mark里存储些什么呢?一开始我把mark设计为布尔量,可消去的设为true。后来写到消去后更新按钮后,我突然想到mark里不仅仅可以记录是否可消去,还可以记录消去后应该更新为什么。

我们知道,消去一行按钮后,上面的按钮会掉下来补充空位,也就是说消去的这一行会被上面一行取代。所以我们把这些按钮的mark赋值为1。

同样,消去一列n个按钮后,每个按钮会被它上面第n个按钮取代,所以我们把这些按钮的mark赋值为n。
这样,在更新的时候,我们只需要遍历二维数组mark,根据它对应的值,来采取相应的操作就可以了。

在这里,要注意顺序性的问题,首先,在赋值mark的时候,应该先扫描横行,再扫描竖列,因为可能会出现十字架或T字型的消去,这里横纵有重叠,而更新的时候以纵为准,所以后扫描纵列来覆盖横行的值。

另外一个要注意的顺序,就是在更新的时候,一定要从上往下扫描。如果从下往上扫描的话,下面图案的更新可能会破坏到上面的图,那么,上面原来存在的可以被消去的方块就已经被破坏了,但是mark还记录着消去方块的索引,这样就会引起错的消去。反之,上面图案的消去是不会破坏下面的图案的。

判断地图是否还存在解

每一轮消去后,我们都需要判断地图上是否还存在解,如果不存在,就要进行更新

因为仅仅是判断存在性,算法略有变化。

我们需要考虑到所有的交换情况,以及它们能否产生可行解,一旦找到一个,我们就可以返回true。所以外循环包含两个,一个扫描横向相邻的所有方块,一个扫描纵向相邻的所有方块。

对于特定的两个方块,我们先交换它们,然后对于两个方块,都计算它们的十字架区域是否存在可行解,之后再把它们交换回来:

 Android开心消消乐代码实例详解_第3张图片

(十字架区域)
总体的最坏时间复杂度是2*(n^2)*2*8。

多线程

在主线程里,按理来说应该有消去 ― 更新这样的画面,但是我发现android是直接把所有东西都计算了出来,然后再去显示UI的,而不是边计算边显示,所以我之前设置的那些一步步更新画面的代码一点儿用也没有,然后我想了想估计是要用多线程来写,在此之前我没有写过多线程的代码,所以花了一天时间看了多线程并把这部分修正了。

大概的想法是这样的:每次点击了两个相邻按钮后,先交换两个按钮,然后调用线程的start()方法。

在线程重写的run()方法里,先计算是否存在解(find函数),如果可以消去,每隔0.03s发送一个消息,通知主线程,主线程设置按钮的alpha值减小(增加透明度),反复10次,这样就做出了按钮慢慢消失的效果。

之后,再停顿0.1s,向主线程发送一个消息,主线程开始进行更新按钮操作。更新之后,因为掉落下来的按钮还可能组成新的可消去的部分,所以继续调用线程的start方法,直到不存在可消去的按钮。

如果不存在解,那么,先判断这是因为用户点击的两个按钮无法产生解,还是之前消去后掉落下来的按钮不会产生解。我们用flag来记录这一状态。如果是前者,先停顿0.3s,再把两个按钮的位置交换回来;如果是后者,说明已经消去所有按钮,这时我们检查是否还存在可行解,如果不存在,向主线程发送一个消息,通知它更新地图。

总之就是重写run,以及消息机制的使用。特别注意的就是run中绝对不能写UI的操作,UI的事情只能交给主线程做,所以才需要不断通知。

还有一个要注意线程之间的执行顺序,调试了很久的一个地方,后来发现是因为主线程那边还没计算出结果,但是次线程已经开始新的计算了,所以此线程用到的是主线程没有更新过的旧数据。后来想到的方法就是等主线程算完了,再回来调用次线程的函数。

@Override 
  public void run() 
  { 
		if(find()){
			flag = false;
			int n = 10;
			alpha = 255;
			while(n--!=0){
  		  wait(30);
  		  mHandler.sendEmptyMessage(0);
			}
  		wait(100);
  		mHandler.sendEmptyMessage(1);
  	
	  }
		else if(flag==true){
			swapMap(p1.x,p1.y,p2.x,p2.y);
  		wait(300);
  		mHandler.sendEmptyMessage(2);
	  	}
		else if(flag==false){
	  	p1 = new point(-2,-2);
  	  p2 = new point(-2,-2);
			if(check()==false){
				mHandler.sendEmptyMessage(3);
			}
		}
  }

代码

Android开心消消乐代码实例详解_第4张图片

main.xml



        

MainActivity.java

package com.example.android.market.licensing; 
 
import android.annotation.SuppressLint; 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import android.os.Message; 
import android.widget.ImageButton; 
import android.widget.LinearLayout; 
import android.widget.TableLayout; 
import android.widget.TableRow; 
import android.widget.TextView; 
import android.widget.Toast; 
import android.view.Gravity; 
import android.view.View; 
 
 
@SuppressLint("NewApi") 
public class MainActivity extends Activity implements Runnable { 
  private static final String TAG = "App"; 
  private point p1 = new point(-2,-2); 
  private point p2 = new point(-2,-2);; 
  private boolean isPoint1 = true; 
  private int map[][] = new int[8][8]; 
  private int id; 
  private int mark[][] = new int[8][8]; 
  private Thread thread; 
  private int num; 
  private int alpha = 255; 
  boolean flag = false; 
  private int score = 0; 
  private TextView text; 
  ImageButton[] btn = new ImageButton[64]; 
   
  private int getStyle(){ 
    num = (int)(1+Math.random()*(7-1+1)); 
    switch(num){ 
    case 1:return id = R.drawable.btn1; 
    case 2:return id = R.drawable.btn2; 
    case 3:return id = R.drawable.btn3; 
    case 4:return id = R.drawable.btn4; 
    case 5:return id = R.drawable.btn5; 
    case 6:return id = R.drawable.btn6; 
    case 7:return id = R.drawable.btn7; 
    default:return id = 0; 
    } 
  } 
 
 
  public MainActivity() { } 
 
  private void initBtn(int i,int j) 
  { 
    btn[8*i+j].setBackgroundDrawable 
    (this.getResources().getDrawable(getStyle())); 
    point p = new point(i,j,btn[8*i+j],id); 
    btn[8*i+j].setTag(p); 
    map[i][j] = num; 
  } 
   
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    LinearLayout vlayout = (LinearLayout)findViewById(R.id.vlayout); 
    TableLayout tlayout = new TableLayout(this); 
    TableRow row[] = new TableRow[8]; 
    for(int i=0;i<8;i++){ 
      row[i] = new TableRow(this); 
      row[i].setGravity(Gravity.CENTER); 
      for(int j=0;j<8;j++){ 
        btn[8*i+j] = new ImageButton(this); 
        btn[8*i+j].setLayoutParams(new TableRow.LayoutParams(38,38)); 
        initBtn(i,j); 
        btn[8*i+j].setOnClickListener(listener); 
        row[i].addView(btn[8*i+j]); 
      } 
      tlayout.addView(row[i]); 
    } 
    text = new TextView(this);    
    text.setText("分数 : 0"); 
    vlayout.addView(tlayout); 
    vlayout.addView(text); 
    while(find()){ 
      updateState(); 
    } 
    thread = new Thread(this);  
  } 
   
  private void wait(int time) 
  { 
    try { 
      Thread.sleep(time); 
    } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
    } 
  } 
   
  @Override  
  public void run()  
  {  
    if(find()){ 
      flag = false; 
      int n = 10; 
      alpha = 255; 
      while(n--!=0){ 
        wait(30); 
        mHandler.sendEmptyMessage(0); 
      } 
      wait(100); 
      mHandler.sendEmptyMessage(1); 
     
    } 
    else if(flag==true){ 
      swapMap(p1.x,p1.y,p2.x,p2.y); 
      wait(300); 
      mHandler.sendEmptyMessage(2); 
    } 
    else if(flag==false){ 
      p1 = new point(-2,-2); 
      p2 = new point(-2,-2); 
      if(check()==false){ 
        mHandler.sendEmptyMessage(3); 
      } 
    } 
  }  
 
   void swapImage() 
  { 
    p1.v.setBackgroundDrawable 
    (MainActivity.this.getResources().getDrawable(p2.id)); 
    p2.v.setBackgroundDrawable 
    (MainActivity.this.getResources().getDrawable(p1.id)); 
    int tmp =p1.id; 
    p1.id = p2.id; 
    p2.id = tmp; 
    p1.v.setTag(p1); 
    p2.v.setTag(p2); 
  } 
   
  private void updateBtn(int x1,int y1,int x2,int y2) 
  { 
    map[x1][y1] = map[x2][y2]; 
    point p = (point)(btn[8*x1+y1].getTag()); 
    p.id = ((point)(btn[8*x2+y2].getTag())).id; 
    btn[8*x1+y1].setTag(p); 
    btn[8*x1+y1].setBackgroundDrawable 
    (MainActivity.this.getResources().getDrawable(p.id)); 
  } 
   
  private void updateBtn(int i,int j) 
  { 
    btn[8*i+j].setBackgroundDrawable 
    (MainActivity.this.getResources().getDrawable(getStyle())); 
    map[i][j] = num; 
    point p = (point)(btn[8*i+j].getTag()); 
    p.id = id; 
    btn[8*i+j].setTag(p); 
  } 
 
  private void hideBtn(){ 
    alpha -= 25; 
    for(int i=0;i<8;i++){ 
      for(int j=0;j<8;j++){ 
        if(mark[i][j]!=0&&mark[i][j]!=2){ 
          btn[8*i+j].getBackground().setAlpha(alpha);      
        } 
      } 
    } 
  } 
 
   
  private void updateState() 
  { 
     
    for(int i=0;i<8;i++){ 
      for(int j=0;j<8;j++){ 
        btn[8*i+j].getBackground().setAlpha(255);       
      } 
    } 
    for(int i=0;i<8;i++){ 
      for(int j=0;j<8;j++){ 
        if(mark[i][j]==1){ 
          for(int k=i;k>0;k--){ 
            updateBtn(k,j,k-1,j); 
          } 
           updateBtn(0,j); 
        } 
        else if(mark[i][j]>=3){ 
          if(i-mark[i][j]>=0) { 
            updateBtn(i,j,i-mark[i][j],j); 
            updateBtn(i-mark[i][j],j); 
          } 
          else{ 
            updateBtn(i,j); 
          } 
        } 
        else if(mark[i][j]==2){
        	updateBtn(i,j);
        }
      } 
    } 
  } 
   
  public Handler mHandler=new Handler()  
  {  
    public void handleMessage(Message msg)  
    {  
      switch(msg.what){ 
        case 0:{ 
          hideBtn(); 
          break; 
        }   
        case 1:{ 
          updateState(); 
          text.setText("分数 " + ((Integer)score).toString()); 
          thread.start(); 
          break; 
        } 
        case 2:{ 
          swapImage(); 
          p1 = new point(-2,-2); 
          p2 = new point(-2,-2); 
          break; 
        } 
        case 3:{ 
          Toast.makeText(MainActivity.this, "已自动生成新地图", Toast.LENGTH_SHORT).show();  
          for(int i=0;i<8;i++){ 
            for(int j=0;j<8;j++){ 
              initBtn(i,j); 
            } 
          } 
          while(find()){ 
            updateState(); 
          } 
          break; 
        } 
      } 
      super.handleMessage(msg);  
    }  
  };    
   
  private boolean find() 
  { 
    for(int i=0;i<8;i++){ 
      for(int j=0;j<8;j++){ 
        mark[i][j] = 0; 
      } 
    } 
    boolean flag = false; 
    // heng 
    for(int i=0;i<8;i++){ 
      int count = 1; 
      for(int j=0;j<7;j++){ 
        if(map[i][j]==map[i][j+1]){ 
          count++; 
          if(count==3){ 
            flag = true; 
            mark[i][j-1] = 1; 
            mark[i][j] = 1; 
            mark[i][j+1] = 1; 
            score += 15; 
          } 
          else if(count>3){ 
            mark[i][j+1] = 1; 
            score += 5; 
          } 
        } 
        else count = 1; 
      } 
    } 
    //shu 
    for(int j=0;j<8;j++){ 
      int count = 1; 
      for(int i=0;i<7;i++){ 
        if(map[i][j]==map[i+1][j]){ 
          count++; 
          if(count==3){ 
            flag = true; 
            if(mark[i][j]==1)score+=10; 
            else score +=15; 
            mark[i-1][j] = 3; 
            mark[i][j] = 3; 
            mark[i+1][j] = 3; 
            for(int k=i-5;k>=0;k--){
            	mark[k][j] = 2; 
            }
          } 
          else if(count>3){ 
            mark[i+1][j] = count; 
            for(int k=1;k=0)mark[i-2*count+2][j] = 0;
            score += 5; 
          } 
        } 
        else count = 1; 
      } 
    } 
    return flag; 
  } 
   
  private boolean check(int i,int j) 
  { 
    //shu 
    int count = 1; 
    if(i>=1&&map[i-1][j]==map[i][j]){ 
      count++; 
      if(i>=2&&map[i-2][j]==map[i][j]){ 
        count++; 
      } 
    } 
    if(count>=3)return true; 
    if(i+1<8&&map[i+1][j]==map[i][j]){ 
      count++; 
      if(i+2<8&&map[i+2][j]==map[i][j]){ 
        count++; 
      } 
    } 
    if(count>=3)return true; 
     
    //heng 
    count = 1; 
    if(j>=1&&map[i][j-1]==map[i][j]){ 
      count++; 
      if(j>=2&&map[i][j-2]==map[i][j]){ 
        count++; 
      } 
    } 
    if(count>=3)return true; 
     
    if(j+1<8&&map[i][j+1]==map[i][j]){ 
      count++; 
      if(j+2<8&&map[i][j+2]==map[i][j]){ 
        count++; 
      } 
    } 
    if(count>=3)return true; 
     
    return false; 
  } 
   
  private void swapMap(int x1,int y1,int x2,int y2) 
  { 
    int tmp = map[x1][y1]; 
    map[x1][y1] = map[x2][y2]; 
    map[x2][y2] = tmp; 
  } 
   
  private boolean check() 
  { 
     
    //heng 
    for(int i=0;i<8;i++){ 
      for(int j=0;j<7;j++){ 
        swapMap(i,j,i,j+1); 
        if(check(i,j)){ 
          swapMap(i,j,i,j+1); 
          return true; 
        } 
        if(check(i,j+1)){ 
          swapMap(i,j,i,j+1); 
          return true; 
        } 
        swapMap(i,j,i,j+1); 
      } 
    } 
    //shu 
    for(int j=0;j<8;j++){ 
      for(int i=0;i<7;i++){ 
        swapMap(i,j,i+1,j); 
        if(check(i,j)){ 
          swapMap(i,j,i+1,j); 
          return true; 
        } 
        if(check(i+1,j)){ 
          swapMap(i,j,i+1,j); 
          return true; 
        } 
        swapMap(i,j,i+1,j); 
      } 
    } 
    return false; 
  } 
   
  ImageButton.OnClickListener listener = new ImageButton.OnClickListener(){//创建监听对象   
    @SuppressLint("NewApi") 
 
    public void onClick(View v) { 
      if(isPoint1){ 
        p1 = (point)v.getTag(); 
        isPoint1 = false; 
      } 
      else { 
        p2 = (point)v.getTag(); 
        isPoint1 = true; 
      } 
     
      if(((p1.x-p2.x==1||p1.x-p2.x==-1)&&p1.y==p2.y)|| 
        (p1.y-p2.y==1||p1.y-p2.y==-1)&&p1.x==p2.x){ 
        flag = true; 
        swapMap(p1.x,p1.y,p2.x,p2.y); 
        swapImage(); 
        thread.start(); 
      } 
    } 
 
  }; 
} 

point.java

package com.example.android.market.licensing;
import android.view.View;
public class point {
	public int x;
	public int y;
	View v;
	int id;
	point(int _x,int _y,View _v,int _id){
		x = _x;
		y = _y;
		v = _v;
		id = _id;
	}
	point(int _x,int _y){
		x = _x;
		y = _y;
	}
}

AndroidManifest.xml




  
    
      
        
        
      
    
  
  
  
  
  

以上所述是小编给大家介绍的Android开心消消乐代码实例详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

你可能感兴趣的:(Android开心消消乐代码实例详解)