Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> android項目實戰之連連看—實現篇(一)

android項目實戰之連連看—實現篇(一)

編輯:Android開發實例

  在上一篇文章helloPe的android項目實戰之連連看—設計篇中,我們進行了對android中連連看的項目的設計,包括功能模塊的劃分以及核心算法的設計。此文章接上文對android平台連連看程序進入實現階段。在此項目中,根據上文中對於功能的分析,我們將實現以下類(下面即是工程的文件目錄):

                                        

在開發中,我們遵循由下向上的方式,也就是說,我們首先開發位於最底層的類,這種類並不依賴於其他的我們需要實現的類。根據上文的分析,首先我們開發在表示層模塊中的界面顯示類,首先是BoardView類,在android平台下,采用繼承自View類的方式,看此類的代碼,代碼中盡量添加了詳細的注釋:

package nate.llk.view;

/*導入包種種再次略去*/
/**
 * **********************************************
 * @author HelloPe
 ************************************************
 */
public class BoardView extends View {
	/**
	 * xCount x軸方向的圖標數+2
	 */
	protected static final int xCount = 10;
	/**
	 * yCount y軸方向的圖表數+2
	 */
	protected static final int yCount = 12;
	/**
	 * map 連連看游戲棋盤,map中添加的int型在程序中的意思是index,而不是屏幕坐標!
	 */
	protected int[][] map = new int[xCount][yCount];
	/**
	 * iconSize 圖標大小,圖標是正方形,所以一個int變量表示即可
	 */
	protected int iconSize;
	/**
	 * iconCounts 圖標的數目
	 */
	protected int iconCounts=19;
	/**
	 * icons 所有的圖片
	 */
	protected Bitmap[] icons = new Bitmap[iconCounts];
	/**
	 * path 可以連通點的路徑
	 */
	private Point[] path = null;
	/**
	 * selected 選中的圖標
	 */
	protected List<Point> selected = new ArrayList<Point>();
	
	/**
	 * 構造函數
	 * @param context
	 * @param attrs
	 */
	public BoardView(Context context, AttributeSet attrs) {
		super(context, attrs);
		calIconSize();
		Resources r = getResources();
		//載入連連看中的圖標資源
		loadBitmaps(1, r.getDrawable(R.drawable.fruit_01));
		loadBitmaps(2, r.getDrawable(R.drawable.fruit_02));
		loadBitmaps(3, r.getDrawable(R.drawable.fruit_03));
		loadBitmaps(4, r.getDrawable(R.drawable.fruit_04));
		loadBitmaps(5, r.getDrawable(R.drawable.fruit_05));
		loadBitmaps(6, r.getDrawable(R.drawable.fruit_06));
		loadBitmaps(7, r.getDrawable(R.drawable.fruit_07));
		loadBitmaps(8, r.getDrawable(R.drawable.fruit_08));
		loadBitmaps(9, r.getDrawable(R.drawable.fruit_09));
		loadBitmaps(10, r.getDrawable(R.drawable.fruit_10));
		loadBitmaps(11, r.getDrawable(R.drawable.fruit_11));
		loadBitmaps(12, r.getDrawable(R.drawable.fruit_12));
		loadBitmaps(13, r.getDrawable(R.drawable.fruit_13));
		loadBitmaps(14, r.getDrawable(R.drawable.fruit_14));
		loadBitmaps(15, r.getDrawable(R.drawable.fruit_15));
		loadBitmaps(16, r.getDrawable(R.drawable.fruit_17));
		loadBitmaps(17, r.getDrawable(R.drawable.fruit_18));
		loadBitmaps(18, r.getDrawable(R.drawable.fruit_19));
	}
	/**
	 * 計算圖標的大小
	 */
	private void calIconSize(){
		//取得屏幕的大小
		DisplayMetrics dm = new DisplayMetrics();
        ((Activity) this.getContext()).getWindowManager()
		.getDefaultDisplay().getMetrics(dm);
        iconSize = dm.widthPixels/( xCount );
	}
	/**
	 * 函數目的在於載入圖標資源,同時將一個key(特定的整數標識)與一個圖標進行綁定
	 * @param key 特定圖標的標識
	 * @param d drawable下的資源
	 */
	public void loadBitmaps(int key,Drawable d){
		Bitmap bitmap = Bitmap.createBitmap(iconSize,iconSize,Bitmap.Config.ARGB_8888);
		Canvas canvas = new Canvas(bitmap);
		d.setBounds(0, 0, iconSize, iconSize);
		d.draw(canvas);
		icons[key]=bitmap;  //未用0 號index
	}
	/**
	 * View自帶的,但是在此方法中,有畫路徑(刪除聯通的兩個圖標),
	 * 繪制棋盤的所有圖標(也可理解為刷新,只要此map位置值>0)
	 * 放大第一個選中的圖標(selected.size() == 1)
	 */
	@Override
	protected void onDraw(Canvas canvas) {
		/**
		 * 繪制連通路徑,然後將路徑以及兩個圖標清除
		 */
		if(path != null && path.length >= 2){
			for(int i = 0; i < path.length - 1;++i){
				Paint paint = new Paint();
				paint.setColor(Color.BLUE);
				paint.setStrokeWidth(3);
				paint.setStyle(Paint.Style.STROKE);
				Point p1 = indexToScreen(path[i].x,path[i].y);
				Point p2 = indexToScreen(path[i + 1].x,path[i + 1].y);
				canvas.drawLine(p1.x + iconSize/2, p1.y + iconSize/2, 
						p2.x + iconSize/2, p2.y + iconSize/2, paint);
			}
			map[path[0].x][path[0].y] = 0;
			map[path[path.length - 1].x][path[path.length -1].y] = 0;
			selected.clear();	
			path = null;
		}
		/**
		 * 繪制棋盤的所有圖標 當這個坐標內的值大於0時繪制
		 */
		for(int x = 1;x < xCount - 1; ++x){
			for(int y = 1; y < yCount -1; ++y){
				if(map[x][y]>0){
					Point p = indexToScreen(x, y);
					canvas.drawBitmap(icons[map[x][y]], p.x,p.y,null);
				}
			}
		}
		/**
		 * 繪制選中圖標,當選中時圖標放大顯示
		 */
		//for(Point position:selected){
		if(selected.size() > 0){
			Point position = selected.get(0);
			Point p = indexToScreen(position.x, position.y);
			if(map[position.x][position.y] >= 1){
				canvas.drawBitmap(icons[map[position.x][position.y]],
						null,
						new Rect(p.x-5, p.y-5, p.x + iconSize + 5, p.y + iconSize + 5), null);
			}
		}
		super.onDraw(canvas);
	}
	
	/**
	 * 工具方法
	 * @param x 數組中的橫坐標
	 * @param y 數組中的縱坐標
	 * @return 將圖標在數組中的坐標轉成在屏幕上的真實坐標
	 */
	public Point indexToScreen(int x,int y){
		return new Point(x * iconSize,y * iconSize);
	}
	/**
	 * 工具方法
	 * @param x 屏幕中的橫坐標
	 * @param y 屏幕中的縱坐標
	 * @return 將圖標在屏幕中的坐標轉成在數組上的虛擬坐標
	 */
	public Point screenToIndex(int x,int y){
		int xindex = x / iconSize;
		int yindex = y / iconSize;
		if(xindex < xCount && yindex < yCount){
			return new Point(xindex,yindex);
		}else{
			return new Point(0,0);
		}
	}
	/**
	 * 傳進來path數據更新顯示,也就是將能夠連接的圖標消除
	 * @param path
	 */
	public void drawLine(Point[] path) {
		this.path = path;
		this.invalidate();
	}
	
}

此類當中,主要是實現了將連連看圖標資源的載入並且使之與一個特定的int型key相綁定,所以在後面的對於圖標的貼圖,我們能夠更加方便的操作。當然

此類中還需要存在一些必要的工具函數,比如說screenToIndex方法等,因為我們是自定義View在屏幕上繪圖,需要用到屏幕坐標,但是同時,連連看游戲

中,我們還需要知道圖標的索引(由於圖標都是等長等寬,容易實現屏幕坐標與index索引之間的轉換),以使方便操作。當然,此類中最重要的還是重寫

的onDraw函數;此函數中首先判斷path是否為null並且是否兩個及以上的元素,我們之前定義path變量時,是將其作為保存連通路徑的工具。(path中

的值也就是連通路徑我們將在連接算法實現時中加入)這裡我們首先在onDraw函數中繪制出線條(如果連通),隨後將路徑的首尾中的map值設為0,程

序中,第0行與最後一行map值始終為0,第0列與最後一列map值始終為0,map中的值0為0代表此處已經沒有了圖標,根據前面與圖標資源的綁定值與

map中的值對應,map中的值為幾則在相應的index上貼上相應的圖標。在onDraw函數中,還有一個功能就是將選擇的第一個圖標放大,以提醒玩家。

最後繪制(貼圖),如前面所說,map值為多少就在對應位置貼上相應的圖標資源,有前面載入資源時可知並沒有對應於0的圖標資源,為0時即不貼圖。

為了防止代碼混亂,上面的BoardView 類並沒有實現全部的功能,如touch事件的監聽,連接算法的實現,判斷是否無解等等。所以我們將BoardView

類進行擴展,繼承BoardView的GameView(這樣做也使代碼不至於太混亂)。限於篇幅,我們可以先將GameView中用於監聽剩余時間的內部類實現

(該類實現了Runnable接口):

	/**
	 * 用於更新剩余時間的線程
	 * @author helloPe
	 *
	 */
	class RefreshTime implements Runnable{

		@Override
		public void run() {
			if(isContinue){
				while(leftTime > 0 && !isStop){
					timerListener.onTimer(leftTime);
					leftTime --;
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			if(isStop && leftTime > 0){
				if(win())
					;//setMode(WIN);
				else
				setMode(PAUSE);
			}
			//setMode(LOSE);
			else if(leftTime == 0){
				setMode(LOSE);
			}
		}
	}
	/**
	 * 停止顯示的時間
	 */
	public void stopTimer(){
		isStop = true;
		isContinue = false;
	}
	/**
	 * 設置繼續
	 */
	public void setContinue(){
		isContinue = true;
		isStop = false;
		refreshTime = new RefreshTime();
		Thread t = new Thread(refreshTime);    //注意正確啟動一個實現Runnable接口的線程
		t.start();
	}

上面已經提過,此線程用於控制游戲的時間。

在此,再介紹自定義的幾個接口,

public interface OnStateListener{
	public void OnStateChanged(int StateMode);
}

只含有一個方法,主要對於游戲狀態的變換的監聽,比如pause,stop等等。

public interface OnTimerListener{
	public void onTimer(int leftTime);
}

用於監聽剩余時間,與上面線程不同的是,此方法中利用上面線程的leftTime的結果,主要用於更新游戲中用於提醒玩家的時間進度條。

public interface OnToolsChangeListener{
	public void onRefreshChanged(int count);
	public void onTipChanged(int count);
}

tool即是我們的游戲中提供給玩家的兩個工具,一個是refresh一下游戲界面,即將現有的棋盤重新打亂(當然,現有圖表數量不變),另一個是之前提過的hint的自動幫助功能,幫助玩家找到一組能夠連通的圖標。當然,這兩種工具都有次數的限制。

  BoardView類及時間線程類的開發與介紹到此,後面我們將完整的實現游戲棋盤的繪制與touch事件的處理,以及游戲核心算法中連接算法、hint自動幫助算法與判斷是否無解算法的實現。這些代碼的處理都在繼承自BoardView類的GameView類中。

  之所以寫本系列的文章,為了記錄android小項目的經歷,增加實戰的能力,做個總結。並不是為了做出多麼新穎的項目,當然也是向不少的網友學習了的!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved