AS+kotlin+SurfaceView最佳實踐之打造六子棋小遊戲

1.源碼:

https://gitee.com/zouchengxin/liu_zi_qi

2.效果:

http://39.106.207.193:8080/liu_zi_qi.mp4

3.App下載:

https://gitee.com/zouchengxin/liu_zi_qi/blob/master/app/release/app-release.apk (放心下載)

4.實現功能:

  • 雙人對戰
  • 悔棋
  • 遊戲退出
  • 遊戲重新開始

5.未實現功能:

由於時間有限,技術限制等原因未能實現的功能

  • 人機對戰:額,這個實現有點複雜,不想做了
  • 藍牙對戰:這個藍牙模塊也沒接觸過

6.遊戲說明:

雙方各6個棋子,只能上下左右移動一格,2個相連棋子可以幹掉對方一個棋子,3個棋子不行,2個棋子不相連也不行,2個棋子與對方棋子有間隔也不行.不如:白白黑空空可以幹掉黑子,白白空黑空,白空白黑空,白白黑空白這些都不行

7.代碼:

MainActivity

package com.zcx.liu_zi_qi

import android.content.Intent
import android.os.Bundle
import android.os.PersistableBundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Toast

class MainActivity:AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    /**
     * 人機對戰
     */
    fun onRenJi(view:View){
        Toast.makeText(this,"正在進入人機對戰", Toast.LENGTH_SHORT).show()
        val intent=Intent()
        intent.setClass(this,GameActivity::class.java)
        intent.putExtra("model",GameModel.RENJI)
        startActivity(intent)
    }

    /**
     * 雙人PK
     */
    fun onTwo(view:View){
        Toast.makeText(this,"正在進入雙人對戰", Toast.LENGTH_SHORT).show()
        val intent=Intent()
        intent.setClass(this,GameActivity::class.java)
        intent.putExtra("model",GameModel.TWO)
        startActivity(intent)
    }

    /**
     * 藍牙對戰
     */
    fun onLanYa(view:View){
        Toast.makeText(this,"正在進入藍牙對戰", Toast.LENGTH_SHORT).show()
        val intent=Intent()
        intent.setClass(this,GameActivity::class.java)
        intent.putExtra("model",GameModel.LANYA)
        startActivity(intent)
    }
}

主活動類:3個按鈕的顯示和點擊事件的編寫

GameActivity

package com.zcx.liu_zi_qi
import android.annotation.TargetApi
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.Point
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.collections.HashMap

class GameActivity : AppCompatActivity(),Handler.Callback {
    val handle=Handler(this)
    var windowWidth=0
    var windowHeight=0
    lateinit var bt_regret:Button
    lateinit var bt_restart:Button
    lateinit var bt_exit:Button
    lateinit var tv_black:TextView
    lateinit var tv_white:TextView
    lateinit var gameView:GameUI

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val display=(getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
        val size=Point()
        display.getSize(size)
        windowWidth=size.x
        windowHeight=size.y
        println("($windowWidth,$windowHeight)")
        init()
    }

    /**
     * 初始化
     */
    fun init(){
        gameView= GameUI(this)
        gameView.layoutParams=ViewGroup.LayoutParams(windowWidth*3/5,ViewGroup.LayoutParams.MATCH_PARENT)

        val userView=LayoutInflater.from(this).inflate(R.layout.user_ui,null)
        userView.layoutParams=ViewGroup.LayoutParams(windowWidth*2/5,ViewGroup.LayoutParams.MATCH_PARENT)

        bt_regret=userView.findViewById<Button>(R.id.bt_regret)
        bt_regret.setOnClickListener {
            synchronized(Map){
                if(!Map.history.isEmpty()){
                    Map.list.removeAll(Map.list)
                    Map.list=Map.copyList(Map.history.last().first)
                    Map.map=Map.copyMap(Map.history.last().second)

                    Map.printList(Map.history.last().first)
                    Map.printMap(Map.history.last().second)
                    Map.history.remove(Map.history.last())
                    Map.printList()
                    Map.printMap()
                }else{
                    Toast.makeText(this,"官人,不能再悔了", Toast.LENGTH_LONG).show()
                }

            }
        }
        bt_restart=userView.findViewById<Button>(R.id.bt_restart)
        bt_restart.setOnClickListener {
            val builder=AlertDialog.Builder(this)
            builder.setTitle("提示")
            builder.setMessage("是否重新開始")
            builder.setPositiveButton("確定",
                object :DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {
                        gameView.exit()
                        this@GameActivity.init()
                    }

                })
            builder.setNegativeButton("取消",
                object :DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {

                    }

                })
            builder.create().show()
        }
        bt_exit=userView.findViewById<Button>(R.id.bt_exit)
        bt_exit.setOnClickListener {
            val builder=AlertDialog.Builder(this)
            builder.setTitle("提示")
            builder.setMessage("是否退出遊戲")
            builder.setPositiveButton("確定",
                object :DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {
                        gameView.exit()
                        val intent= Intent()
                        intent.setClass(this@GameActivity,MainActivity::class.java)
                        this@GameActivity.finish()
                    }

                })
            builder.setNegativeButton("取消",
                object :DialogInterface.OnClickListener{
                    override fun onClick(dialog: DialogInterface?, which: Int) {

                    }

                })
            builder.create().show()
        }
        tv_black=userView.findViewById<TextView>(R.id.tv_black)
        tv_white=userView.findViewById<TextView>(R.id.tv_white)


        val layout=LinearLayout(this)
        layout.layoutParams=ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)
        layout.addView(gameView)
        layout.addView(userView)
        setContentView(layout)
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    override fun handleMessage(msg: Message?): Boolean {
        when(msg?.what){
            1 ->{
                val builder=AlertDialog.Builder(this)
                builder.setTitle("提示")
                val str=when(msg.obj){ChessManType.WHITE ->"白子" else -> "黑子"}
                builder.setMessage("${str}獲勝,是否開始下一局")
                builder.setPositiveButton("開始下一局",
                    object :DialogInterface.OnClickListener{
                        override fun onClick(dialog: DialogInterface?, which: Int) {
                            gameView.exit()
                            this@GameActivity.init()
                        }

                    })
                builder.setNegativeButton("退出",
                    object :DialogInterface.OnClickListener{
                        override fun onClick(dialog: DialogInterface?, which: Int) {

                        }

                    })
                builder.create().show()
            }
            2 ->{
                val map=msg.obj as HashMap<String, String>
                if(Map.gameModel==GameModel.RENJI){
                    tv_black.setText("黑子(電腦):"+map.get("blackCount"))
                }else{
                    tv_black.setText("黑子:"+map.get("blackCount"))
                }
                tv_white.setText("白子:"+map.get("whiteCount"))

                when(Map.whoChess){
                    ChessManType.WHITE ->{
                        tv_white.setBackgroundResource(R.color.btnSelect)
                        tv_black.setBackgroundResource(R.color.btnNotSelect)
                    }
                    else ->{
                        tv_black.setBackgroundResource(R.color.btnSelect)
                        tv_white.setBackgroundResource(R.color.btnNotSelect)
                    }
                }

            }
        }
        return true
    }

    override fun onBackPressed() {
        if(gameView.isExit) {
            super.onBackPressed()
        }else{
            Toast.makeText(this,"再按一次退出",Toast.LENGTH_LONG).show()
            gameView.isExit=true
        }
    }
}

遊戲活動:手機橫屏,3/5寬度的SurfaceView(顯示棋盤),2/5顯示用戶界面(黑棋,白棋個數,悔棋,重新開始,退出遊戲按鈕)

GameUI

package com.zcx.liu_zi_qi

import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.graphics.*
import android.os.Build
import android.os.Message
import android.util.Range
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.widget.Toast

class GameUI constructor(context:Context):SurfaceView(context),SurfaceHolder.Callback{
    private lateinit var thread:Thread//繪圖線程
    private lateinit var chessBoard:ChessBoard//棋盤實例
    private var isChessable=true//標記是否可下子
    private var whoWin:ChessManType?=null//哪方獲勝,null代表沒有勝利者
    var isExit=false//標記是否退出遊戲
    private lateinit var background:Bitmap//背景圖片

    init {
        this.setZOrderOnTop(true)
        //this.setBackgroundResource(R.drawable.background)
        background=BitmapFactory.decodeResource(context.resources,R.drawable.background)
        this.holder.setFormat(PixelFormat.TRANSLUCENT)
        this.holder.addCallback(this)
        Map.list.removeAll(Map.list)
        Map.history.removeAll(Map.history)
        for (i in 0..3){
            val arr= Array<Int>(4,{it -> 0})
            for (j in 0..3){
                if(i==0||(i==1&&(j==0||j==3))){
                    arr[j]=2
                }
                if(i==3||(i==2&&(j==0||j==3))){
                    arr[j]=1
                }
            }
            Map.map[i]=arr
            Toast.makeText(context,"遊戲初始化完成", Toast.LENGTH_SHORT).show()
        }
    }
    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {

    }

    override fun surfaceDestroyed(holder: SurfaceHolder?) {
        thread.join()
    }

    override fun surfaceCreated(holder: SurfaceHolder?) {
        thread= Thread(GameThread(holder))
        thread.start()
    }

    inner class GameThread(val holder: SurfaceHolder?):Runnable{
        override fun run() {
            while(true){
                if(isExit){
                    return
                }
                var canvas= holder?.lockCanvas()
                //canvas?.drawColor(Color.WHITE)
                canvas?.drawBitmap(background, Rect(0,0,background.width,background.height),
                    Rect(0,0,canvas.width,canvas.height),Paint())
                canvas?.let {
                    chessBoard=ChessBoard(it)
                    chessBoard.update()
                }
                holder?.unlockCanvasAndPost(canvas)
                if(whoWin!=null){
                    val msg=Message.obtain()
                    msg.what=1
                    msg.obj=whoWin
                    (context as GameActivity).handle.sendMessage(msg)
                    return
                }
            }

        }

    }

    /**
     * 監聽觸摸事件
     */
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        val x= event?.getX()
        val y=event?.getY()
        //println("(x,y)=($x,$y)")
        if (x != null&&y!=null) {
            selectChessMan(x,y)
        }
        return true
    }

    /**
     * 下子
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private fun selectChessMan(x:Float,y:Float){
        val ij=chessBoard.xy2ij(Pair(x,y))
        val i=ij?.first
        val j=ij?.second
        println("(i,j)=($i,$j)")
        synchronized(Map){//同步代碼塊
            isChessable=when(Map.whoChess){
                ChessManType.WHITE -> if (Map.map[i!!][j!!]!=2) true else false
                else -> if (Map.map[i!!][j!!]!=1) true else false
            }
            if(isChessable){
                Map.list.forEach {
                    if(x in Range(it.cx-100,it.cx+100)
                        && y in Range(it.cy-100,it.cy+100)){
                        Map.list.filter { it.isSelect }.forEach {
                            it.isSelect=false
                        }
                        it.isSelect=true
                    }
                }
                moveChessMan(i!!,j!!)
            }

        }

    }

    /**
     * 移動棋子
     */
    private fun moveChessMan(i:Int,j:Int){
        if(Map.map[i][j]!=0){//目標位置有子
            return
        }
        Map.list.filter { it.isSelect }.forEach {
            if(it.move(i,j)){
                //println(Map.printMap())
                it.isSelect=false
                Map.whoChess=when(Map.whoChess){
                    ChessManType.WHITE -> ChessManType.BLACK
                    else -> ChessManType.WHITE
                }
                it.check()
                val msg=Message.obtain()
                msg.what=2
                val map=kotlin.collections.HashMap<String,String>()
                map.put("blackCount",Map.list.filter { it.type==ChessManType.BLACK }.size.toString())
                map.put("whiteCount",Map.list.filter { it.type==ChessManType.WHITE }.size.toString())
                msg.obj=map
                (context as GameActivity).handle.sendMessage(msg)
                checkWin()
            }


        }

    }

    /**
     * 判斷輸贏
     */
    fun checkWin(){
        if(Map.list.filter { it.type==ChessManType.WHITE }.size==0){
            whoWin=ChessManType.BLACK
        }
        if(Map.list.filter { it.type==ChessManType.BLACK }.size==0){
            whoWin=ChessManType.WHITE
        }
    }

    /**
     * 退出遊戲
     */
    fun exit(){
        isExit=true
        thread.join()

    }



}

繼承SurfaceView,循環繪製棋盤,棋子.監聽觸摸事件並做出響應

ChessBoard:

package com.zcx.liu_zi_qi

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF

/**
 *棋盤類
 */
class ChessBoard(val canvas: Canvas) {
    val width = canvas.height - 200//棋盤寬度
    val height = canvas.height - 200//棋盤高度
    val block = width / 3
    val rect: RectF = RectF(100f, 100f, (width + 100).toFloat(), (height + 100).toFloat())

    /**
     *內部棋子類
     * i:橫座標(0,1,2,3)
     * j:縱座標(0,1,2,3)
     */
    inner class ChessMan(var i:Int=0,var j:Int=0,var type:ChessManType=ChessManType.WHITE):Cloneable{
        var cx=this@ChessBoard.rect.left+j*block//棋子中心x座標
        var cy=this@ChessBoard.rect.top+i*block//棋子中心y座標
        var isSelect=false//標記棋子是否被選中
        var callMoveDirection= arrayListOf<ChessManDirection>()//棋子可移動方向

        override fun toString():String{
            return "{i:${this.i},j:${this.j},cx:${this.cx},cy:${this.cy},type:${this.type},isSelect:${this.isSelect}}"
        }

        fun copy(): ChessMan {
            val temp=ChessMan(i,j)
            temp.cx=cx
            temp.cy=cy
            temp.type=type
            temp.isSelect=isSelect
            return temp
        }
        /**
         * 繪製棋子
         */
        fun draw(r:Float=50f){
            val paint=Paint()
            paint.style=Paint.Style.FILL_AND_STROKE
            paint.strokeWidth=4f
            paint.color=when(type){
                ChessManType.WHITE -> Color.WHITE
                else -> Color.BLACK
            }
            canvas.drawCircle(cx,cy,r,paint)

            paint.color=Color.BLACK
            paint.style=Paint.Style.STROKE
            canvas.drawCircle(cx,cy,r,paint)
            if(isSelect){
                paint.color=Color.YELLOW
                paint.strokeWidth=6f
                paint.strokeCap=Paint.Cap.SQUARE//設置筆刷圖形樣式
                paint.strokeMiter=10f//設置筆畫傾斜度
                canvas.drawRect(
                    RectF((cx-60).toFloat(),(cy-60).toFloat(),(cx+60).toFloat(),(cy+60).toFloat()),
                    paint
                )
            }
        }

        /**
         * 移動棋子
         */
        fun move(i:Int,j:Int):Boolean{
            if((Math.abs(this.i-i)<1&&this.i==i)
                ||(Math.abs(this.j-j)<1&&this.j==j)){
                if(Map.history.size>5){//歷史記錄大於5,清空
                    Map.history.removeAll(Map.history)
                }
                Map.history.add(Pair(Map.copyList(),Map.copyMap()))
                this.cx=this@ChessBoard.rect.left+j*block
                this.cy=this@ChessBoard.rect.top+i*block
                Map.map[this.i][this.j]=0
                this.i=i
                this.j=j
                Map.map[i][j]=when(type){
                    ChessManType.BLACK ->2
                    else -> 1
                }
                return true
                //Map.printMap()
                //Map.printList()
            }
            return false
        }

        /**
         * 檢查
         */
        fun check() {
            val tp=when(type){ChessManType.WHITE ->1 else ->2}
            val _tp=when(type){ChessManType.WHITE ->2 else ->1}
            when(i){
                0 ->{
                    if(Map.map[1][j]==tp&&Map.map[2][j]==_tp&&Map.map[3][j]==0){
                        Map.map[2][j]=0
                        Map.list.filter { it.i==2&&it.j==this.j }[0].destroy()
                    }
                }
                1 ->{
                    if(Map.map[0][j]==tp&&Map.map[2][j]==_tp&&Map.map[3][j]==0){
                        Map.map[2][j]=0
                        Map.list.filter { it.i==2&&it.j==this.j }[0].destroy()
                    }
                    if(Map.map[0][j]==0&&Map.map[2][j]==tp&&Map.map[3][j]==_tp){
                        Map.map[3][j]=0
                        Map.list.filter { it.i==3&&it.j==this.j }[0].destroy()
                    }
                }
                2 ->{
                    if(Map.map[0][j]==0&&Map.map[1][j]==_tp&&Map.map[3][j]==tp){
                        Map.map[1][j]=0
                        Map.list.filter { it.i==1&&it.j==this.j }[0].destroy()
                    }
                    if(Map.map[0][j]==_tp&&Map.map[1][j]==tp&&Map.map[3][j]==0){
                        Map.map[0][j]=0
                        Map.list.filter { it.i==0&&it.j==this.j }[0].destroy()
                    }
                }
                3 ->{
                    if(Map.map[0][j]==0&&Map.map[1][j]==_tp&&Map.map[2][j]==tp){
                        Map.map[1][j]=0
                        Map.list.filter { it.i==1&&it.j==this.j }[0].destroy()
                    }
                }
            }
            when(j){
                0 ->{
                    if(Map.map[i][1]==tp&&Map.map[i][2]==_tp&&Map.map[i][3]==0){
                        Map.map[i][2]=0
                        Map.list.filter { it.j==2&&it.i==this.i }[0].destroy()
                    }
                }
                1 ->{
                    if(Map.map[i][0]==tp&&Map.map[i][2]==_tp&&Map.map[i][3]==0){
                        Map.map[i][2]=0
                        Map.list.filter { it.j==2&&it.i==this.i }[0].destroy()
                    }
                    if(Map.map[i][0]==0&&Map.map[i][2]==tp&&Map.map[i][3]==_tp){
                        Map.map[i][3]=0
                        Map.list.filter { it.j==3&&it.i==this.i }[0].destroy()
                    }
                }
                2 ->{
                    if(Map.map[i][0]==0&&Map.map[i][1]==_tp&&Map.map[i][3]==tp){
                        Map.map[i][1]=0
                        Map.list.filter { it.j==1&&it.i==this.i }[0].destroy()
                    }
                    if(Map.map[i][0]==_tp&&Map.map[i][1]==tp&&Map.map[i][3]==0){
                        Map.map[i][0]=0
                        Map.list.filter { it.j==0&&it.i==this.i }[0].destroy()
                    }
                }
                3 ->{
                    if(Map.map[i][0]==0&&Map.map[i][1]==_tp&&Map.map[i][2]==tp){
                        Map.map[i][1]=0
                        Map.list.filter { it.j==1&&it.i==this.i }[0].destroy()
                    }
                }
            }

        }

        /**
         * 銷燬
         */
        fun destroy(){
            Map.list.remove(this)

        }
    }

    /**
     *繪製棋盤
     */
    fun drawChessBoard(){
        val paint=Paint()
        paint.color=Color.BLUE
        paint.style=Paint.Style.STROKE
        paint.strokeWidth=8f
        for (i in 0..2){
            canvas.drawRect(
                RectF(rect.left,rect.top+i*block.toFloat(),rect.right,rect.top+(i+1)*block.toFloat()),
                paint
            )
            canvas.drawRect(
                RectF(rect.left+i*block.toFloat(),rect.top,rect.left+(i+1)*block.toFloat(),rect.bottom),
                paint
            )
        }
    }

    /**
     * 更新棋盤
     */
     fun update(){
        this.drawChessBoard()
        synchronized(Map){
            if(Map.list.isEmpty()){
                Map.list.removeAll(Map.list)
                for (i in 0..3){
                    for (j in 0..3){
                        if(Map.map[i][j]==1){
                            Map.list.add(ChessMan(i,j,ChessManType.WHITE))
                        }
                        if(Map.map[i][j]==2){
                            Map.list.add(ChessMan(i,j,ChessManType.BLACK))
                        }
                    }
                }
            }


            Map.list.forEach {
                //println(it.toString())
                it.draw()
            }
        }

    }

    /**
     * 將x,y轉換成i,j
     */
    fun xy2ij(pair: Pair<Float,Float>): Pair<Int, Int>? {
        val x=pair.first
        val y=pair.second
        if(x>rect.right+block/2){
            return null
        }
        val j=((x-rect.left+block/2)/block).toInt()
        val i=((y-rect.top+block/2)/block).toInt()
        return Pair(i,j)
    }
}

棋盤類:包含一個棋子內部類,棋盤類實現了繪製棋盤,更新棋盤等功能,棋子類實現了繪製棋子,移動棋子等功能

Map:

package com.zcx.liu_zi_qi

object Map {
    var map= Array<Array<Int>>(4,{it -> Array<Int>(4,{it -> 0})})//4X4數組.0:空,1:白子,2:黑子
    var list = arrayListOf<ChessBoard.ChessMan>()//list集合存儲棋子數據
    val gameModel=GameModel.RENJI//遊戲模式
    var whoChess=ChessManType.WHITE//輪到哪方下子
    var history= arrayListOf<Pair<ArrayList<ChessBoard.ChessMan>,Array<Array<Int>>>>()//記錄歷史信息

    fun copyMap(m: Array<Array<Int>> = map):Array<Array<Int>>{
        val temp=Array<Array<Int>>(4,{it -> Array<Int>(4,{it -> 0})})
        for (i in 0..3){
            for (j in 0..3) {
                temp[i][j]=m[i][j]
            }
        }
        return temp
    }

    fun copyList(l: ArrayList<ChessBoard.ChessMan> = list):ArrayList<ChessBoard.ChessMan>{
        val temp=ArrayList<ChessBoard.ChessMan>()
        l.forEach {
            temp.add(it.copy())
        }
        return temp
    }
    /**
     * 打印Map信息
     */
    fun printMap(m: Array<Array<Int>> = map){
        print("[")
        for (i in 0..3){
            print("[")
            for (j in 0..3) {
                if (j == 3) {
                    print("${m[i][j]}")
                } else {
                    print("${m[i][j]},")
                }
            }
            print("]")

        }
        println("]")
    }

    /**
     * 打印List信息
     */
    fun printList(l: ArrayList<ChessBoard.ChessMan> = list){
        print("[")
        l.forEach {
            print(it.toString())
        }
        println("]")
    }
}

一個object類(相當於單例,只有一個實例):1.map屬性:4X4數組,記錄棋盤信息(0:空,1:白子,2:黑子),2.屬性list:一個list集合,存儲所有棋子信息(座標信息,棋子類型,是否被選中)

ChessManType:

package com.zcx.liu_zi_qi

/**
 * 棋子類型
 */
enum class ChessManType {
    WHITE,BLACK
}

棋子類型:一個枚舉類,WHITE:表示白子,BLACK:表示黑子

GameModel:

package com.zcx.liu_zi_qi

/**
 * 對戰模式
 */
enum class GameModel {
    RENJI,TWO,LANYA
}

遊戲模式:一個枚舉類,RENJI:人機模式,TWO:雙人對戰,LANYA:藍牙對戰

8.注意:

所有的ui更改(比如Toast)必須在主線程中進行,可以通過發送Message給主線程處理.
特別注意類的深拷貝和淺拷貝問題(Map類裏的map和list有涉及)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章