国产成人精品亚洲777人妖,欧美日韩精品一区视频,最新亚洲国产,国产乱码精品一区二区亚洲

您的位置:首頁技術(shù)文章
文章詳情頁

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

瀏覽:3日期:2023-12-14 15:52:38
目錄一、項(xiàng)目概述二、開發(fā)環(huán)境三、需求分析四、實(shí)現(xiàn)過程1、拼圖游戲布局繪制2、拼圖游戲時(shí)間計(jì)時(shí)3、拼圖游戲打亂顯示4、拼圖游戲碎片位置切換5、拼圖游戲成功的條件6、拼圖游戲重新開始五、運(yùn)行效果六、項(xiàng)目總結(jié)七、項(xiàng)目源碼一、項(xiàng)目概述

之前有不少粉絲私信我說,能不能用Android原生的語言開發(fā)一款在手機(jī)上運(yùn)行的游戲呢?

說實(shí)話,使用java語言直接開發(fā)游戲這個(gè)需求有點(diǎn)難,因?yàn)橐恍┍容^復(fù)雜的游戲都是通過cocos2D或者Unity3D等游戲引擎開發(fā)出來的,然后再移植到Android手機(jī)當(dāng)中,使用完整的游戲引擎開發(fā)的過程比較簡單,而且界面比較流暢,觀感和體驗(yàn)度都很好。

所以直接使用java開發(fā)的游戲并不多。當(dāng)然,雖說不多但也有。簡單些的比如:2048、拼圖游戲、貪吃蛇、推箱子等,復(fù)雜點(diǎn)的比如:斗地主,這些都可以用java語言開發(fā)。因?yàn)檫@些游戲刷新界面次數(shù)比較少,是可以用java開發(fā)出來的。

所以在這篇博客里面,我們就來開發(fā)一款簡單的拼圖游戲,這款拼圖游戲就和我們小時(shí)候玩的游戲是一樣的,這里面的涉及到的算法不多,可以很容易學(xué)會,是作為入門Android的一個(gè)非常好的實(shí)例。

二、開發(fā)環(huán)境

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

三、需求分析

我們先來看下最終要實(shí)現(xiàn)的效果:

可以看到游戲開始后,開始計(jì)時(shí),然后下面是被打亂的九宮格圖片,最后一塊是空白的,因?yàn)橐舫隹臻g移動(dòng),中間是重新開始按鈕,點(diǎn)擊就會重新計(jì)時(shí)而且拼圖碎片重新打亂,最底下是原圖,方便大家對照著進(jìn)行拼湊。當(dāng)你拼圖完成后,上面的第九塊拼圖會立刻顯示出來補(bǔ)齊整張圖片,然后彈出對話框,告訴你拼圖成功,用時(shí)為多少多少秒,點(diǎn)擊確認(rèn)即可。Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

所以我們分為六個(gè)步驟來實(shí)現(xiàn):

拼圖游戲布局繪制 拼圖游戲時(shí)間計(jì)時(shí) 拼圖游戲打亂顯示 拼圖游戲碎片位置切換 拼圖游戲成功的條件 拼圖游戲重新開始

我們來看下需要準(zhǔn)備的圖片素材:

這里先是一張小熊的樣圖,命名就是yangtu。然后就是將它按九宮格裁剪成的九張圖片,命名格式我來解釋下:我們看第八張我選中的圖片,它的名字為img_xiaoxiong_02x01。這里解釋下為什么是02x01,這就可以看做一個(gè)三行三列的二維數(shù)組,排列方式就和下面一樣。數(shù)組行和列下標(biāo)都是從0開始,所以第八張就是在第2行第1列,所以就是02x01,其他的也以此類推。大家可以自己選圖片進(jìn)行裁剪命名,當(dāng)然也可以直接下載我的源碼,里面就有這些圖片。

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

下面我們就一起來實(shí)現(xiàn)這個(gè)拼圖游戲吧~

四、實(shí)現(xiàn)過程1、拼圖游戲布局繪制

我們首先來分析下游戲的layout布局

再來看下最終實(shí)現(xiàn)的效果圖,先分析一下怎么繪制布局,實(shí)現(xiàn)一個(gè)項(xiàng)目的第一步是將布局按照自己期望的樣子完成。

因?yàn)檫@是一個(gè)上下結(jié)構(gòu),所以我們用一個(gè)線性布局(LinearLayout)來實(shí)現(xiàn)最合適,方向(orientation)設(shè)置為豎直方向(vertical)。可以看到這個(gè)拼圖分為三行三列,所以我們直接將每一行分為一個(gè)小的LinearLayout,一共三個(gè),然后在每個(gè)小的LinearLayout里面水平放三個(gè)圖片按鈕,這樣就實(shí)現(xiàn)了,思路有了,我們來繪制吧。

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

我們來繪制游戲的layout布局

從上至下的第一個(gè)布局是顯示時(shí)間的TextView,我們將它的id設(shè)置為pt_tv_time,layout_width和layout_height都設(shè)置為wrap_content,就是適應(yīng)內(nèi)容大小,然后text文本內(nèi)容設(shè)為“時(shí)間:0”,這個(gè)是方便測試寫上文本的,因?yàn)檫厡懘a可以邊看旁邊的效果變化。

然后layout_gravity設(shè)置為'center',就是設(shè)置自己在父容器(頂層的LinearLayout)中居中,這里補(bǔ)充下知識點(diǎn):

gravity是設(shè)置自身內(nèi)部元素的對齊方式。比如一個(gè)TextView,則是設(shè)置內(nèi)部文字的對齊方式。如果是ViewGroup組件如LinearLayout的話,則為設(shè)置它內(nèi)部view組件的對齊方式。 layout_gravity是設(shè)置自身相當(dāng)于父容器的對齊方式。比如,一個(gè)TextView設(shè)置layout_gravity屬性,則表示這TextView相對于父容器的對齊方式。

再來改變下字體大小,設(shè)置textSize為20sp,sp是像素,補(bǔ)充下單位的知識點(diǎn):

dp: device independent pixels(設(shè)備獨(dú)立像素),不同設(shè)備有不同的顯示效果,和設(shè)備硬件有關(guān)。 px: pixels(像素).,不同設(shè)備顯示效果相同,這個(gè)用的比較多。 pt: point,是一個(gè)標(biāo)準(zhǔn)的長度單位,1pt=1/72英寸,用于印刷業(yè),非常簡單易用。 sp: scaled pixels(放大像素),主要用于字體顯示best for textsize。

最后設(shè)置字體顏色為#FF0000,即紅色。一般是通過colors.xml資源來引用,這里因?yàn)榧t色比較好表示就直接設(shè)置了。

TextView代碼如下:

<TextViewandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:text='時(shí)間 : 0'android:layout_gravity='center'android:textSize='20sp'android:textColor='#FF0000'/>

設(shè)置完成后,我們來看下效果圖:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

接著我們來繪制九宮格拼圖,先設(shè)置第一行這三個(gè)小圖片的外布局,依然是LinearLayout,設(shè)置它的id='@+id/pt_line1',就表示第一行。

orientation選擇的是水平方向,因?yàn)槊恳恍惺撬椒胖玫模琹ayout_gravity設(shè)置為'center',表示居中,代碼如下。

<LinearLayoutandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:orientation='horizontal'android:layout_gravity='center'></LinearLayout>

設(shè)置第一張圖片,選擇的控件是ImageButton,顧名思義:圖片按鈕,正常按鈕就規(guī)規(guī)矩矩的,而圖片按鈕就很好看,一張圖片也可以進(jìn)行點(diǎn)擊,這里設(shè)置它的id='@+id/pt_ib_00x00',方便在MainActivity里面調(diào)用。

00x00不用我多說了吧,上面解釋過了,將九宮格看成3X3的二維數(shù)組,那么行列下標(biāo)就是0行0列,這里每行數(shù)和列數(shù)都用2位數(shù)字表示而已。

設(shè)置src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x00',就是將我們剛剛準(zhǔn)備的圖片資源復(fù)制到這個(gè)mipmap文件夾中進(jìn)行引用,每個(gè)id編號和圖片的名稱是對應(yīng)的。

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

再設(shè)置個(gè)onClick方法,方法名為'onClick',我們后面會在MainActivity里面進(jìn)行編寫點(diǎn)擊事件。第一張圖片的代碼如下:

<ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x00' android:padding='0dp' android:onClick='onClick'/>

依次類推,第二張和第三張圖片,我只要改下id和src就可以了,所以直接放上第一個(gè)小LinearLayout的代碼:

<LinearLayoutandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:orientation='horizontal'android:layout_gravity='center'><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x00' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x01' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x02' android:padding='0dp' android:onClick='onClick'/> </LinearLayout>

來看下顯示效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

那第二行和第三行是不是也一樣照葫蘆畫瓢,沒錯(cuò),直接復(fù)制第一行的代碼,然后修改id和src就行。這里直接給出三個(gè)LinearLayout的代碼:

<LinearLayoutandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:orientation='horizontal'android:layout_gravity='center'><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x00' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x01' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_00x02' android:padding='0dp' android:onClick='onClick'/> </LinearLayout> <LinearLayoutandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:orientation='horizontal'android:layout_gravity='center'><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_01x00' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_01x01' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_01x02' android:padding='0dp' android:onClick='onClick'/> </LinearLayout> <LinearLayoutandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:orientation='horizontal'android:layout_gravity='center'><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_02x00' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_02x01' android:padding='0dp' android:onClick='onClick'/><ImageButton android: android:layout_width='wrap_content' android:layout_height='wrap_content' android:src='http://www.intensediesel.com/bcjs/@mipmap/img_xiaoxiong_02x02' android:padding='0dp' android:onClick='onClick' android:visibility='invisible'/> </LinearLayout>

有一點(diǎn)需要注意的,不知道有沒有同學(xué)發(fā)現(xiàn)——第三行的第三張圖片,也就是右下角的那張圖片,它有個(gè)屬性,其他的圖片都沒有:visibility=“invisible”,這是干什么的呢?

這個(gè)其實(shí)就是設(shè)置控件是否可見,默認(rèn)情況下控件都是可見的(visible),只有設(shè)置visibility='invisible'后,這個(gè)控件才不顯示出來,我們來看下整體效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

OK,九宮格完成后,下面是一個(gè)重新開始的Button。

這個(gè)比較簡單了,主要設(shè)置了onClick=“restart”,這個(gè)后面會在MainActivity里面編寫重新開始游戲的邏輯,還設(shè)置了android:layout_marginTop=“20dp”,這是設(shè)置此控件與上面控件邊距相隔20dp,為了和九宮格保持一定間距,代碼如下:

<Buttonandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:onClick='restart'android:layout_gravity='center'android:text='重新開始'android:layout_marginTop='20dp'/>

顯示效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

最后就是我們的樣圖了,有了我們上面的經(jīng)驗(yàn),這個(gè)應(yīng)該很容易就畫出來了,放置圖片的控件我們一般使用ImageView,然后設(shè)置src='http://www.intensediesel.com/bcjs/@mipmap/yangtu',就顯示了我們的樣圖,最后為了保持距離美,設(shè)置layout_marginTop=“20dp”,代碼如下:

<ImageViewandroid: android:layout_width='wrap_content'android:layout_height='wrap_content'android:layout_gravity='center'android:src='http://www.intensediesel.com/bcjs/@mipmap/yangtu'android:layout_marginTop='20dp'/>

好了,我們來看下效果圖:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

至此,我們的布局就繪制完成了!

我們來編寫下MainActivity的基本框架

可以先來看下什么都沒有的MainActivity。里面只有onClick()和restart()兩個(gè)新的方法,這是在上面布局中設(shè)置的方法,onClick是圖片按鈕的點(diǎn)擊事件,restart是重新開始按鈕的點(diǎn)擊事件,這兩個(gè)方法的具體實(shí)現(xiàn)邏輯會在下面講到。

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// 設(shè)置要顯示的視圖 setContentView(R.layout.activity_main);}// 圖片按鈕的點(diǎn)擊事件 public void onClick(View view) {}/* 重新開始按鈕的點(diǎn)擊事件*/ public void restart(View view) { }}

這里我們要做的是把所有在布局中用到的控件定義好,然后初始化這些控件

先來定義九個(gè)圖片按鈕,命名方法也是00,01這樣的橫縱坐標(biāo),一個(gè)重啟按鈕和一個(gè)顯示時(shí)間的文本框

// 定義九個(gè)圖片按鈕,命名方法也是00,01這樣的橫縱坐標(biāo) ImageButton ib00,ib01,ib02,ib10,ib11,ib12,ib20,ib21,ib22;// 一個(gè)重啟按鈕 Button restartBtn;// 一個(gè)顯示時(shí)間的文本框 TextView timeTv;

然后我們在onCreate中定義一個(gè)initView()方法,這個(gè)方法是用來初始化控件的

// 初始化layout控件的方法initView();

然后創(chuàng)建該方法,在該方法里面初始化定義的控件,通過findViewById()進(jìn)行綁定控件,將聲明的變量和layout中對應(yīng)的控件進(jìn)行綁定,實(shí)現(xiàn)引用的效果,代碼如下:

/* 初始化控件:綁定9個(gè)圖片按鈕,1個(gè)顯示時(shí)間的文本框,1個(gè)重啟按鈕*/ private void initView() {ib00 = findViewById(R.id.pt_ib_00x00);ib01 = findViewById(R.id.pt_ib_00x01);ib02 = findViewById(R.id.pt_ib_00x02);ib10 = findViewById(R.id.pt_ib_01x00);ib11 = findViewById(R.id.pt_ib_01x01);ib12 = findViewById(R.id.pt_ib_01x02);ib20 = findViewById(R.id.pt_ib_02x00);ib21 = findViewById(R.id.pt_ib_02x01);ib22 = findViewById(R.id.pt_ib_02x02);timeTv = findViewById(R.id.pt_tv_time);restartBtn = findViewById(R.id.pt_btn_restart); }

初始化的完整代碼,可以作為模板:

public class MainActivity extends AppCompatActivity {// 定義九個(gè)圖片按鈕,命名方法也是00,01這樣的橫縱坐標(biāo) ImageButton ib00,ib01,ib02,ib10,ib11,ib12,ib20,ib21,ib22;// 一個(gè)重啟按鈕 Button restartBtn;// 一個(gè)顯示時(shí)間的文本框 TextView timeTv;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// 設(shè)置要顯示的視圖 setContentView(R.layout.activity_main); initView();}private void initView() {ib00 = findViewById(R.id.pt_ib_00x00);ib01 = findViewById(R.id.pt_ib_00x01);ib02 = findViewById(R.id.pt_ib_00x02);ib10 = findViewById(R.id.pt_ib_01x00);ib11 = findViewById(R.id.pt_ib_01x01);ib12 = findViewById(R.id.pt_ib_01x02);ib20 = findViewById(R.id.pt_ib_02x00);ib21 = findViewById(R.id.pt_ib_02x01);ib22 = findViewById(R.id.pt_ib_02x02);timeTv = findViewById(R.id.pt_tv_time);restartBtn = findViewById(R.id.pt_btn_restart); }// 圖片按鈕的點(diǎn)擊事件 public void onClick(View view) {}/* 重新開始按鈕的點(diǎn)擊事件*/ public void restart(View view) { }} 2、拼圖游戲時(shí)間計(jì)時(shí)

完成基本工作后,我們思考下——如何實(shí)現(xiàn)時(shí)間的計(jì)時(shí)操作,這就相當(dāng)于計(jì)時(shí)器的功能。這里我們可以用Handler消息機(jī)制來實(shí)現(xiàn),補(bǔ)充下知識點(diǎn):

Handler:作用就是發(fā)送與處理信息 Message:Handler接收與處理的消息對象

當(dāng)我們的子線程想修改Activity中的UI組件時(shí),我們可以新建一個(gè)Handler對象,通過這個(gè)對象向主線程發(fā)送信息;而我們發(fā)送的信息會先到主線程的MessageQueue進(jìn)行等待,由Looper按先入先出順序取出,再根據(jù)message對象的what屬性分發(fā)給對應(yīng)的Handler進(jìn)行處理!

簡單來說:Handler就是用來發(fā)送消息和處理消息的一種機(jī)制,上面這段話可能聽起來有些懵,不過沒關(guān)系,其實(shí)沒有這么深?yuàn)W,下面會讓大家明白怎么使用它來實(shí)現(xiàn)計(jì)時(shí)的。

先定義個(gè)時(shí)間變量,初值為0,因?yàn)閺?開始計(jì)時(shí)

// 定義計(jì)數(shù)時(shí)間的變量 int time = 0;

然后定義發(fā)送和處理消息的對象handler,我們來重寫handleMessage方法,在方法里面我們進(jìn)行了if判斷,如果這條消息的what值為1,那么時(shí)間time就+1,然后timeTv顯示時(shí)間為time秒,然后繼續(xù)向自己發(fā)送消息。

handler.sendEmptyMessageDelayed(1,1000)這句話的意思就是:延時(shí)1000毫秒后發(fā)送參數(shù)what為1的空信息,這樣它自己就能循環(huán)接收自己發(fā)的消息,實(shí)現(xiàn)計(jì)時(shí)的功能了,就這么簡單。

當(dāng)然最開始要發(fā)送它一條消息,讓它這個(gè)方法運(yùn)轉(zhuǎn)起來,我們在onCreate這個(gè)方法里面加上了一條handler.sendEmptyMessageDelayed(1,1000); 這樣在游戲一開始過了1s,handler就發(fā)送了一條what為1的空消息。然后它自己又立馬接收到了,進(jìn)行時(shí)間加1,又自己發(fā)送給自己消息,實(shí)現(xiàn)計(jì)時(shí)!

這是定義的handler的代碼:

// 定義發(fā)送和處理消息的對象handler Handler handler = new Handler(){@Override// 重寫handleMessage方法,根據(jù)msg中what的值判斷是否執(zhí)行后續(xù)操作public void handleMessage(Message msg) { if (msg.what==1) {time++;timeTv.setText('時(shí)間 : '+time+' 秒');// 指定延時(shí)1000毫秒后發(fā)送參數(shù)what為1的空信息handler.sendEmptyMessageDelayed(1,1000); }} };

這是在onCreate方法里面定義的一條消息

handler.sendEmptyMessageDelayed(1,1000);

我們來看下運(yùn)行效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

除此之外,我們還需要在重新開始游戲后進(jìn)行重新計(jì)時(shí),這里又要怎么實(shí)現(xiàn)呢?

這里我們只需要在restart方法里面先停止handler的消息發(fā)送,保證時(shí)間不會再繼續(xù)+1了,然后將時(shí)間重新歸0,顯示當(dāng)前時(shí)間,最后每隔1s發(fā)送參數(shù)what為1的消息msg,這樣就實(shí)現(xiàn)了重新開始計(jì)時(shí),代碼如下:

/* 重新開始按鈕的點(diǎn)擊事件*/ public void restart(View view) {// 停止handler的消息發(fā)送handler.removeMessages(1);// 將時(shí)間重新歸0,并且重新開始計(jì)時(shí)time = 0;timeTv.setText('時(shí)間 : '+time+' 秒');// 每隔1s發(fā)送參數(shù)what為1的消息msghandler.sendEmptyMessageDelayed(1,1000); }

點(diǎn)擊重新開始后的實(shí)現(xiàn)效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

至此,我們的計(jì)時(shí)功能就實(shí)現(xiàn)了!

3、拼圖游戲打亂顯示

首先定義一個(gè)image數(shù)組,里面存放每張碎片(九宮格圖片)的id,int型數(shù)組是可以存放圖片的id的,但是不能存放圖片,注意這個(gè)區(qū)別。

// 將每張碎片的id存放到數(shù)組中,便于進(jìn)行統(tǒng)一的管理,int型數(shù)組存放的肯定是int型變量 private int[]image = {R.mipmap.img_xiaoxiong_00x00,R.mipmap.img_xiaoxiong_00x01,R.mipmap.img_xiaoxiong_00x02,R.mipmap.img_xiaoxiong_01x00,R.mipmap.img_xiaoxiong_01x01,R.mipmap.img_xiaoxiong_01x02, R.mipmap.img_xiaoxiong_02x00,R.mipmap.img_xiaoxiong_02x01,R.mipmap.img_xiaoxiong_02x02};

再聲明一個(gè)imageIndex數(shù)組,它來存放上面圖片數(shù)組的下標(biāo),一共九張圖片,所以下標(biāo)為0-8,它存儲的也就是0-8。我們?yōu)榱俗屔厦婢艔垐D片被打亂,所以,這里的下標(biāo)等下會被打亂。

// 聲明上面圖片數(shù)組下標(biāo)的數(shù)組,隨機(jī)排列這個(gè)數(shù)組,九張圖片,下標(biāo)為0-8 private int[]imageIndex = new int[image.length];

下面我們寫一個(gè)函數(shù)disruptRandom( ),來實(shí)現(xiàn)進(jìn)入游戲拼圖就打亂顯示的效果

先給下標(biāo)數(shù)組每個(gè)元素賦值,下標(biāo)是i,值就為i,就是imageIndex[i] = i。

// 給下標(biāo)數(shù)組每個(gè)元素賦值,下標(biāo)是i,值就為ifor (int i = 0; i < imageIndex.length; i++) { imageIndex[i] = i;}

然后進(jìn)行20次for循環(huán),隨機(jī)選擇兩個(gè)角標(biāo)對應(yīng)的值進(jìn)行交換。先定義兩個(gè)角標(biāo)rand1和rand2,rand1 = (int)(Math.random()*(imageIndex.length-1));這里我來重點(diǎn)解釋一下:Math.random()產(chǎn)生的隨機(jī)數(shù)為0~1之間的小數(shù) 此處說的0~1是包含左不包含右,即包含0不包含1!

ps:我在這里卡了2h至少,因?yàn)檫@個(gè)小細(xì)節(jié)點(diǎn)沒注意到,所以一定不能想當(dāng)然,要查資料以求準(zhǔn)確。

Math.random()的值域?yàn)閇0,1),然后imageIndex.length-1就是8其實(shí),*8那就是[0,8),再int取整最終值域?yàn)閧0,1,2,3,4,5,6,7},因?yàn)閕nt取整只會取整數(shù)位,不會四舍五入!

再用do-while循環(huán)實(shí)現(xiàn)了rand2的生成,之所以在do-while里面生成rand2,是為了判斷二次生成的角標(biāo)和第一次是否相同,不同則break立刻跳出循環(huán),執(zhí)行swap交換;若第二次生成的與第一次相同,則重新進(jìn)入do-while循環(huán)生成rand2,這部分代碼如下:

//規(guī)定20次,隨機(jī)選擇兩個(gè)角標(biāo)對應(yīng)的值進(jìn)行交換int rand1,rand2;for (int j = 0; j < 20; j++) {// 隨機(jī)生成第一個(gè)角標(biāo)// Math.random()產(chǎn)生的隨機(jī)數(shù)為0~1之間的小數(shù) 此處說的0~1是包含左不包含右,即包含0不包含1// Math.random()的值域?yàn)閇0,1),然后*8就是[0,8),再int取整最終值域?yàn)閧0,1,2,3,4,5,,6,7} rand1 = (int)(Math.random()*(imageIndex.length-1));// 第二次隨機(jī)生成的角標(biāo),不能和第一次隨機(jī)生成的角標(biāo)相同,如果相同,就不方便交換了 do {rand2 = (int)(Math.random()*(imageIndex.length-1));// 判斷第一次和第二次生成的角標(biāo)是否相同,不同則break立刻跳出循環(huán),執(zhí)行swap交換if (rand1!=rand2) { break;}// 若第二次生成的與第一次相同,則重新進(jìn)入do-while循環(huán)生成rand2 }while (true); swap(rand1,rand2); }

這里的swap方法很簡單,就是交換兩個(gè)數(shù)的值,只不過這里參數(shù)是數(shù)組的下標(biāo):

// 交換數(shù)組指定角標(biāo)(0-7這八個(gè)自然數(shù))上的數(shù)據(jù) private void swap(int rand1, int rand2) {int temp = imageIndex[rand1];imageIndex[rand1] = imageIndex[rand2];imageIndex[rand2] = temp; }

這里有個(gè)整個(gè)游戲的一個(gè)核心點(diǎn):我們打亂的拼圖下標(biāo)是{0,1,2,3,4,5,6,7}這八個(gè),第九張拼圖的下標(biāo)是不參與打亂的,有同學(xué)問為什么?是因?yàn)榈诰艔垐D片是不顯示出來的,而且不會參與到拼圖中,所以我們是將第九個(gè)圖片按鈕就設(shè)置成第九張圖片,然后invisible。

最后我們將每個(gè)圖片按鈕設(shè)置圖片,這時(shí)候 imageIndex[i]就是被打亂的下標(biāo),有可能是這樣的順序:{2,6,5,4,1,7,0,3,8},也有可能是這樣的順序{1,3,0,5,2,7,4,6,8}等等,不管怎么樣, imageIndex[8]一直是8,上面解釋過。代碼如下:

// ib00是綁定的第一塊圖片按鈕,設(shè)置圖片資源,// imageIndex[i]就是被打亂的下標(biāo),然后image[x]就表示對應(yīng)下標(biāo)為x的圖片的idib00.setImageResource(image[imageIndex[0]]);ib01.setImageResource(image[imageIndex[1]]);ib02.setImageResource(image[imageIndex[2]]);ib10.setImageResource(image[imageIndex[3]]);ib11.setImageResource(image[imageIndex[4]]);ib12.setImageResource(image[imageIndex[5]]);ib20.setImageResource(image[imageIndex[6]]);ib21.setImageResource(image[imageIndex[7]]);ib22.setImageResource(image[imageIndex[8]]);

綜上,disruptRandom()的整體邏輯代碼如下:

// 隨機(jī)打亂數(shù)組當(dāng)中元素,以不規(guī)則的形式進(jìn)行圖片顯示 private void disruptRandom() {// 給下標(biāo)數(shù)組每個(gè)元素賦值,下標(biāo)是i,值就為ifor (int i = 0; i < imageIndex.length; i++) { imageIndex[i] = i;}//規(guī)定20次,隨機(jī)選擇兩個(gè)角標(biāo)對應(yīng)的值進(jìn)行交換int rand1,rand2;for (int j = 0; j < 20; j++) {// 隨機(jī)生成第一個(gè)角標(biāo)// Math.random()產(chǎn)生的隨機(jī)數(shù)為0~1之間的小數(shù) 此處說的0~1是包含左不包含右,即包含0不包含1// Math.random()的值域?yàn)閇0,1),然后*8就是[0,8),再int取整最終值域?yàn)閧0,1,2,3,4,5,,6,7} rand1 = (int)(Math.random()*(imageIndex.length-1));// 第二次隨機(jī)生成的角標(biāo),不能和第一次隨機(jī)生成的角標(biāo)相同,如果相同,就不方便交換了 do {rand2 = (int)(Math.random()*(imageIndex.length-1));// 判斷第一次和第二次生成的角標(biāo)是否相同,不同則break立刻跳出循環(huán),執(zhí)行swap交換if (rand1!=rand2) { break;}// 若第二次生成的與第一次相同,則重新進(jìn)入do-while循環(huán)生成rand2 }while (true);// 交換兩個(gè)角標(biāo)上對應(yīng)的值 swap(rand1,rand2);}//隨機(jī)排列到指定的控件上//ib00是綁定的第一塊圖片按鈕,設(shè)置圖片資源,imageIndex[i]就是被打亂的圖片數(shù)組下標(biāo),然后image[x]就表示對應(yīng)下標(biāo)為x的圖片的idib00.setImageResource(image[imageIndex[0]]);ib01.setImageResource(image[imageIndex[1]]);ib02.setImageResource(image[imageIndex[2]]);ib10.setImageResource(image[imageIndex[3]]);ib11.setImageResource(image[imageIndex[4]]);ib12.setImageResource(image[imageIndex[5]]);ib20.setImageResource(image[imageIndex[6]]);ib21.setImageResource(image[imageIndex[7]]);ib22.setImageResource(image[imageIndex[8]]); }

實(shí)現(xiàn)效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

4、拼圖游戲碎片位置切換

我們完成亂序后,這時(shí)候拼圖碎片還不能移動(dòng),所以我們要設(shè)置點(diǎn)擊事件,來移動(dòng)拼圖。

拼圖移動(dòng)的規(guī)則也要注意一下:只有和空白區(qū)域在同一行或者同一列相鄰的拼圖才能移動(dòng),只要知道了這個(gè)邏輯,實(shí)現(xiàn)起來就不難了。

我們來編寫九個(gè)圖片按鈕的onClick()方法

這里因?yàn)榫艂€(gè)id不同的imagebutton點(diǎn)擊事件的邏輯相同,所以我們使用switch 語句來編寫,根據(jù)它們的id來執(zhí)行移動(dòng),按照從左到右、從上到下的順序進(jìn)行了case設(shè)置。移動(dòng)我們定義了move()函數(shù),將它單獨(dú)封裝成了一個(gè)方法,下面就會講到。點(diǎn)擊事件的代碼如下:

public void onClick(View view) {int id = view.getId();//九個(gè)按鈕執(zhí)行的點(diǎn)擊事件的邏輯應(yīng)該是相同的,如果有空格在周圍,可以改變圖片顯示的位置,否則點(diǎn)擊事件不響應(yīng)switch (id) { case R.id.pt_ib_00x00:move(R.id.pt_ib_00x00,0);break; case R.id.pt_ib_00x01:move(R.id.pt_ib_00x01,1);break; case R.id.pt_ib_00x02:move(R.id.pt_ib_00x02,2);break; case R.id.pt_ib_01x00:move(R.id.pt_ib_01x00,3);break; case R.id.pt_ib_01x01:move(R.id.pt_ib_01x01,4);break; case R.id.pt_ib_01x02:move(R.id.pt_ib_01x02,5);break; case R.id.pt_ib_02x00:move(R.id.pt_ib_02x00,6);break; case R.id.pt_ib_02x01:move(R.id.pt_ib_02x01,7);break; case R.id.pt_ib_02x02:move(R.id.pt_ib_02x02,8);break;} }

我們來編寫九個(gè)圖片按鈕的move()方法

先定義變量,imageX是每行的圖片個(gè)數(shù),imageY是每列的圖片個(gè)數(shù),imgCount是圖片的總數(shù)目,也就是9個(gè)。blankSwap是空白區(qū)域的位置,就是8,這里的位置我們還是按照從左到右、從上到下的順序排列的,第一張圖片的位置是0,對照九宮格應(yīng)該理解了吧。

blankImgid就是空白區(qū)域的按鈕id,我們這里直接固定了R.id.pt_ib_02x02,就是第九個(gè)圖片按鈕,它一直是空白區(qū)域!

// 每行的圖片個(gè)數(shù) private int imageX = 3;// 每列的圖片個(gè)數(shù) private int imageY = 3;// 圖片的總數(shù)目 private int imgCount = imageX*imageY;// 空白區(qū)域的位置 private int blankSwap = imgCount-1;// 初始化空白區(qū)域的按鈕id private int blankImgid = R.id.pt_ib_02x02;

定義完要用到的變量,我們來寫move方法,這里我每句都寫上了注釋,這里就不再贅述了。強(qiáng)調(diào)幾點(diǎn):

1.可以移動(dòng)的條件有兩個(gè):

在同一行,列數(shù)相減,絕對值為1,可移動(dòng) 在同一列,行數(shù)相減,絕對值為1,可以移動(dòng)

2.兩個(gè)參數(shù): imagebuttonId是被選中的圖片的id,site是該圖片在9宮格的位置(0-8)

3.將移動(dòng)后的圖片按鈕設(shè)為不可見的,即顯示為空白區(qū)域

4.移動(dòng)之前是不可見的,移動(dòng)之后將圖標(biāo)按鈕設(shè)置為可見

5.進(jìn)行移動(dòng)后將改變角標(biāo)的過程記錄到存儲圖片位置的數(shù)組當(dāng)中

/*表示移動(dòng)指定位置的按鈕的函數(shù),將圖片和空白區(qū)域進(jìn)行交換*/ //imagebuttonId是被選中的圖片的id,site是該圖片在9宮格的位置(0-8) private void move(int imagebuttonId, int site) {//判斷選中的圖片在第幾行,imageX為3,所以進(jìn)行取整運(yùn)算int sitex = site / imageX;//判斷選中的圖片在第幾列,imageY為3,所以進(jìn)行取模運(yùn)算int sitey = site % imageY;//獲取空白區(qū)域的坐標(biāo),blankx為行坐標(biāo),blanky為列坐標(biāo)int blankx = blankSwap / imageX;int blanky = blankSwap % imageY;//可以移動(dòng)的條件有兩個(gè)//1.在同一行,列數(shù)相減,絕對值為1,可移動(dòng) 2.在同一列,行數(shù)相減,絕對值為1,可以移動(dòng)int x = Math.abs(sitex-blankx);int y = Math.abs(sitey-blanky);if ((x==0&&y==1)||(y==0&&x==1)){// 通過id,查找到這個(gè)可以移動(dòng)的按鈕 ImageButton clickButton = findViewById(imagebuttonId);// 將這個(gè)選中的圖片設(shè)為不可見的,即顯示為空白區(qū)域 clickButton.setVisibility(View.INVISIBLE);// 查找到空白區(qū)域的按鈕 ImageButton blankButton = findViewById(blankImgid);// 將空白區(qū)域的按鈕設(shè)置為圖片,image[imageIndex[site]就是剛剛選中的圖片,因?yàn)檫@在上面disruptRandom()設(shè)置過 blankButton.setImageResource(image[imageIndex[site]]);// 移動(dòng)之前是不可見的,移動(dòng)之后將控件設(shè)置為可見 blankButton.setVisibility(View.VISIBLE);// 將改變角標(biāo)的過程記錄到存儲圖片位置的數(shù)組當(dāng)中 swap(site,blankSwap);// 新的空白區(qū)域位置更新等于傳入的點(diǎn)擊按鈕的位置 blankSwap = site;// 新的空白圖片id更新等于傳入的點(diǎn)擊按鈕的id blankImgid = imagebuttonId;} }

運(yùn)行效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

5、拼圖游戲成功的條件

上面我們已經(jīng)實(shí)現(xiàn)了拼圖碎片進(jìn)行移動(dòng)的效果,但是并沒有拼圖游戲成功的效果和提示,所以,我們要在剛剛的move方法的最后加上一個(gè)判斷的方法judgeGameOver();顧名思義:判斷游戲結(jié)束。

我們來實(shí)現(xiàn)一下判斷游戲結(jié)束的邏輯

在方法里面先定義一個(gè)loop標(biāo)志位,然后要遍歷下標(biāo)數(shù)組,判斷是否它的imageIndex[i]==i,就是說所有拼圖的下標(biāo)全部對應(yīng)正確的位置。比如:第1張圖片的下標(biāo)是0,imageIndex[0]的值也是0,顯示第一張圖片。所有圖片都滿足,也就是說此時(shí)拼圖成功。如果一個(gè)不滿足,則未成功,所有l(wèi)oop置為false,繼續(xù)判斷。

boolean loop = true; //定義標(biāo)志位loop for (int i = 0; i < imageIndex.length; i++) { if (imageIndex[i]!=i) {loop = false;break; } }

如果拼圖成功了,則handler.removeMessages(1)進(jìn)行停止計(jì)時(shí),而且設(shè)置ib00.setClickable(false)禁止玩家繼續(xù)移動(dòng)按鈕,還有就是第九塊空白區(qū)域顯示出圖片,即下標(biāo)為8的第九張拼圖。

if (loop) {// 拼圖成功了// 停止計(jì)時(shí) handler.removeMessages(1);// 拼圖成功后,禁止玩家繼續(xù)移動(dòng)按鈕 ib00.setClickable(false); ib01.setClickable(false); ib02.setClickable(false); ib10.setClickable(false); ib11.setClickable(false); ib12.setClickable(false); ib20.setClickable(false); ib21.setClickable(false); ib22.setClickable(false);// 拼圖成功后,第九塊空白顯示出圖片,即下標(biāo)為8的第九張圖片 ib22.setImageResource(image[8]); ib22.setVisibility(View.VISIBLE);

我們再來實(shí)現(xiàn)一下游戲結(jié)束時(shí)的對話框

對話框要用到AlertDialog.Builder對象,它的使用就是固定套路,我來補(bǔ)充知識點(diǎn):

第一步:創(chuàng)建AlertDialog.Builder對象 第二步:設(shè)置對話框的內(nèi)容:setMessage()方法來指定顯示的內(nèi)容 第三步:調(diào)用setPositive/Negative/NeutralButton()設(shè)置:確定,取消,中立按鈕 第四歩:調(diào)用create()方法創(chuàng)建這個(gè)對象 第五歩:調(diào)用show()方法來顯示我們的AlertDialog對話框

非常簡單,按照上面的流程,我們來設(shè)置下對話框:

// 彈出提示用戶成功的對話框,并且設(shè)置確實(shí)的按鈕// 第一步:創(chuàng)建AlertDialog.Builder對象 AlertDialog.Builder builder = new AlertDialog.Builder(this);// 調(diào)用setIcon()設(shè)置圖標(biāo),setTitle()或setCustomTitle()設(shè)置標(biāo)題// 第二步:設(shè)置對話框的內(nèi)容:setMessage()方法來指定顯示的內(nèi)容 builder.setMessage('恭喜,拼圖成功!您用的時(shí)間為'+time+'秒')// 第三步:調(diào)用setPositive/Negative/NeutralButton()設(shè)置:確定,取消,中立按鈕 .setPositiveButton('確認(rèn)',null);// 第四歩:調(diào)用create()方法創(chuàng)建這個(gè)對象 AlertDialog dialog = builder.create();// 第五歩:調(diào)用show()方法來顯示我們的AlertDialog對話框 dialog.show();

實(shí)現(xiàn)效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

6、拼圖游戲重新開始

我們在上面實(shí)現(xiàn)了拼圖游戲成功的條件和提示了,現(xiàn)在到了最后一步——如何讓游戲重新開始?

我們來看下拼圖成功后,點(diǎn)擊重新開始,目前只能重新計(jì)時(shí),拼圖并沒有打亂,而且第九塊還沒有隱藏,所以,接下來我們的思路很明確,在重新開始的restart方法中編寫打亂和隱藏圖片的邏輯。

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

我們來實(shí)現(xiàn)重新開始游戲時(shí)的按鈕狀態(tài)還原

首先,這些按鈕已經(jīng)被設(shè)置成不可點(diǎn)擊了,所以我們先要將它們設(shè)置為可以點(diǎn)擊,就是設(shè)置ib00.setClickable(true),因?yàn)檫@部分代碼都是一樣的,所以我們將它單獨(dú)封裝成一個(gè)restore方法。

另外,還要還原被點(diǎn)擊的圖片按鈕變成初始化的模樣, ImageButton clickBtn = findViewById(blankImgid)其實(shí)就是綁定最后一次被隱藏的那塊拼圖,然后clickBtn.setVisibility(View.VISIBLE)將它顯示出來。ImageButton blankBtn = findViewById(R.id.pt_ib_02x02)就是綁定的第九塊拼圖,blankBtn.setVisibility(View.INVISIBLE)設(shè)置為不可見。最后blankImgid = R.id.pt_ib_02x02來初始化空白區(qū)域的按鈕id。

restore()的代碼如下:

// 狀態(tài)還原函數(shù),我們把它封裝起來 private void restore() {// 拼圖游戲重新開始,允許移動(dòng)碎片按鈕ib00.setClickable(true);ib01.setClickable(true);ib02.setClickable(true);ib10.setClickable(true);ib11.setClickable(true);ib12.setClickable(true);ib20.setClickable(true);ib21.setClickable(true);ib22.setClickable(true);//還原被點(diǎn)擊的圖片按鈕變成初始化的模樣ImageButton clickBtn = findViewById(blankImgid);clickBtn.setVisibility(View.VISIBLE);//默認(rèn)隱藏第九張圖片ImageButton blankBtn = findViewById(R.id.pt_ib_02x02);blankBtn.setVisibility(View.INVISIBLE);//初始化空白區(qū)域的按鈕idblankImgid = R.id.pt_ib_02x02;blankSwap = imgCount - 1; }

最后,我們在restart()中實(shí)現(xiàn)重新開始的邏輯

將狀態(tài)還原 將拼圖重新打亂 停止handler的消息發(fā)送 將時(shí)間重新歸0,并且重新開始計(jì)時(shí) 每隔1s發(fā)送參數(shù)what為1的消息msg

/* 重新開始按鈕的點(diǎn)擊事件*/ public void restart(View view) {//將狀態(tài)還原 restore();// 將拼圖重新打亂disruptRandom();// 停止handler的消息發(fā)送handler.removeMessages(1);// 將時(shí)間重新歸0,并且重新開始計(jì)時(shí)time = 0;timeTv.setText('時(shí)間 : '+time+' 秒');// 每隔1s發(fā)送參數(shù)what為1的消息msghandler.sendEmptyMessageDelayed(1,1000); }

重新開始游戲后的效果:

Android Studio做超好玩的拼圖游戲 附送詳細(xì)注釋源碼

至此,拼圖游戲的所有功能已經(jīng)實(shí)現(xiàn)完畢,我先休息下,手腕已經(jīng)打酸了。如果你看到這里,我真的很欣慰,說明你是個(gè)很有耐心而且熱愛Android的學(xué)生,有熱情有耐心,再困難的東西都可以學(xué)會。

五、運(yùn)行效果

Android Studio實(shí)現(xiàn)拼圖游戲

六、項(xiàng)目總結(jié)

這次實(shí)現(xiàn)的拼圖游戲,說它簡單,其實(shí)它實(shí)現(xiàn)起來也并不是那么簡單,還是會有很多比較難的邏輯點(diǎn),需要思考才能寫出來;說它難,其實(shí)也不算難,比起來我前面發(fā)的那些項(xiàng)目【天氣預(yù)報(bào)】、【飲食搭配】來說邏輯實(shí)現(xiàn)還是比較簡單的,畢竟它只有一個(gè)MainActivity和一個(gè)layout。所以,說一個(gè)項(xiàng)目的難易得看你選的參照物了。

這篇文章一共25000多個(gè)字,820行,我寫這篇文章,不連上寫代碼時(shí)間,前后一共11個(gè)小時(shí),前面構(gòu)思和注釋了4個(gè)小時(shí),然后具體寫了7個(gè)小時(shí),中間只有喝水a(chǎn)nd上廁所。可以說我完全是按照開發(fā)這款拼圖游戲的邏輯順序來寫下這篇教程。就是我們平時(shí)怎么開發(fā)Android項(xiàng)目,這篇博客就是怎么寫的。

我之所以寫的這么詳細(xì),也是因?yàn)楝F(xiàn)在網(wǎng)上缺少一個(gè)從頭到尾講實(shí)現(xiàn)過程的Android項(xiàng)目的教程,因?yàn)檫@實(shí)在太花時(shí)間了,我深有體會,極少有人一步一步地去把實(shí)現(xiàn)過程寫出來,但是我還是決定寫下這篇教程,為了讓更多的人喜歡上Android,讓更多的人對Android不再陌生,讓小白們不再望而卻步,讓小白們有個(gè)很好的實(shí)現(xiàn)案例,這是我的想法。

當(dāng)然,我也是正在學(xué)習(xí)Android的選手之一,才疏學(xué)淺,知識淺薄,文章中難免會有紕漏和錯(cuò)誤,還希望大佬們批評指正。

七、項(xiàng)目源碼

這次的拼圖游戲項(xiàng)目是一個(gè)非常好的Android實(shí)現(xiàn)案例,涉及到很多常用的控件和知識點(diǎn),希望大家拿到源碼后,能對照著教程和注釋好好學(xué)習(xí)掌握。

源碼幾乎每條語句我都加上了注釋,這么良心的博主,點(diǎn)個(gè)三連支持下吧,源碼就送你啦,祝大家身體健康,學(xué)習(xí)愉快~

鏈接:拼圖游戲源碼

面向陽光時(shí),陰影在你背后。背向陽光時(shí),陰影在你眼前。世界從未改變,改變的只是我們面對世界的方向!加油!你值得更好!

到此這篇關(guān)于帶你用Android Studio做超好玩的拼圖游戲,0基礎(chǔ)小白也能包你學(xué)會,附送帶有超詳細(xì)注釋源碼的文章就介紹到這了,更多相關(guān)Android拼圖游戲內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Android
相關(guān)文章:
主站蜘蛛池模板: 姚安县| 泰州市| 柳河县| 大兴区| 济南市| 桂东县| 塘沽区| 固镇县| 铜山县| 吉首市| 宁城县| 新和县| 金阳县| 洛阳市| 宁城县| 临夏县| 勐海县| 翼城县| 阳山县| 汾西县| 桐城市| 上栗县| 蕉岭县| 余江县| 辉县市| 阜宁县| 视频| 巴林左旗| 汨罗市| 调兵山市| 浦东新区| 阜平县| 麻江县| 浙江省| 永平县| 怀仁县| 秦皇岛市| 疏附县| 台湾省| 广饶县| 梓潼县|