安卓第二趴
今天是對昨天知識的補充
加上新的學(xué)習(xí)內(nèi)容
1.?昨天的內(nèi)容里,有的地方?jīng)]有說的很清楚,今天加以補充。
在說到背景圖的添加的時候,只是簡單的說了放在drawable文件下。但是沒有說明怎么在代碼中實現(xiàn)。
我們知道,我們所有的操作實際上就是在一個畫布上搞來搞去,那么背景圖,既然要作為大背景,理應(yīng)放在一個大的布局范圍。
我們放在RelativeLayout中:
?
這樣我們就可以看到效果圖了:
(和昨天的圖不一樣,因為我換了啊,我還會換的,看心情。。。)
?
2. ????View是我們自定義的。
這里的view相當(dāng)于是在整個大的畫布上圈出了一個范圍,畫過畫的人都知道,所有的畫都不要從一張紙的邊緣處就下筆,要有留白。
我們接下來的操作,比如說繪制網(wǎng)格等等就在這個圈出來的部分進行。
昨天我們只是簡單提了一下view定義了基本的繪圖操作,這里我們詳細說明一下:
基本操作由三個函數(shù)完成:draw()、measure()、layout()。其內(nèi)部又包含了三個子方法:onDraw()、onMeasure()、onLayout()。
具體操作如下:
measure操作
? ? ?measure操作主要用于計算視圖的大小,即視圖的寬度和長度。在view中定義為final類型,要求子類不能修改。measure()函數(shù)中又會調(diào)用下面的函數(shù):onMeasure(),視圖大小的將在這里最終確定,也就是說measure只是對onMeasure的一個包裝,子類可以覆寫onMeasure()方法實現(xiàn)自己的計算視圖大小的方式,并通過setMeasuredDimension(width, height)保存計算結(jié)果。
layout操作
? ???layout操作用于設(shè)置視圖在屏幕中顯示的位置。在view中定義為final類型,要求子類不能修改。layout()函數(shù)中有兩個基本操作:
? ? ?(1)setFrame(l,t,r,b),l,t,r,b即子視圖在父視圖中的具體位置,該函數(shù)用于將這些參數(shù)保存起來;
? ? ?(2)onLayout(),在View中這個函數(shù)什么都不會做,提供該函數(shù)主要是為viewGroup類型布局子視圖用的;
draw操作
? ? ?draw操作利用前兩步得到的參數(shù),將視圖顯示在屏幕上,到這里也就完成了整個的視圖繪制工作。子類也不應(yīng)該修改該方法,因為其內(nèi)部定義了繪圖的基本操作:
? ? ?(1)繪制背景;
? ? ?(2)如果要視圖顯示漸變框,這里會做一些準備工作;
? ? ?(3)繪制視圖本身,即調(diào)用onDraw()函數(shù)。在view中onDraw()是個空函數(shù),也就是說具體的視圖都要覆寫該函數(shù)來實現(xiàn)自己的顯示(比如TextView在這里實現(xiàn)了繪制文字的過程)。而對于ViewGroup則不需要實現(xiàn)該函數(shù),因為作為容器是“沒有內(nèi)容“的,其包含了多個子view,而子View已經(jīng)實現(xiàn)了自己的繪制方法,因此只需要告訴子view繪制自己就可以了,也就是下面的dispatchDraw()方法;
? ? ?(4)繪制子視圖,即dispatchDraw()函數(shù)。在view中這是個空函數(shù),具體的視圖不需要實現(xiàn)該方法,它是專門為容器類準備的,也就是容器類必須實現(xiàn)該方法;
? ? ?(5)如果需要(應(yīng)用程序調(diào)用了setVerticalFadingEdge或者setHorizontalFadingEdge),開始繪制漸變框;
? ? ?(6)繪制滾動條;
? ? ? 從上面可以看出自定義View需要最少覆寫onMeasure()和onDraw()兩個方法。
????還記得我們在之前講JAVA的時候說過,抽象父類的所有方法在子類中都要實現(xiàn),這里有異曲同工之妙。
下面我們再聊一聊onMeasure():
onMeasure(int widthMeasureSpec, int heightMeasureSpec)中,兩個參數(shù)的作用: ??? ? ?widthMeasureSpec和heightMeasureSpec這兩個int類型的參數(shù),看名字應(yīng)該知道是跟寬和高有關(guān)系,但它們其實不是寬和高,而是由寬、高和各自方向上對應(yīng)的模式來合成的一個值:其中,在int類型的32位二進制位中,31-30這兩位表示模式,0~29這三十位表示寬和高的實際值.
其中模式一共有三種,被定義在Android中的View類的一個內(nèi)部類中:View.MeasureSpec:
①UNSPECIFIED:表示默認值,父控件沒有給子view任何限制。------二進制表示:00
②EXACTLY:表示父控件給子view一個具體的值,子view要設(shè)置成這些值的大小。
----二進制表示:01
③AT_MOST:表示父控件個子view一個最大的特定值,而子view不能超過這個值的大小。------二進制表示:10
?
那么問題來了,MeasureSpec是什么?
MeasureSpe描述了父View對子View大小的期望.里面包含了測量模式和大小.我們可以通過以下方式從MeasureSpec中提取模式和大小,該方法內(nèi)部是采用位移計算.
int specMode = MeasureSpec.getMode(measureSpec);//得到模式
int specSize = MeasureSpec.getSize(measureSpec);//得到大小
?
也可以通過MeasureSpec的靜態(tài)方法把大小和模式合成,該方法內(nèi)部只是簡單的相加.
MeasureSpec.makeMeasureSpec(specSize,specMode);
?
????每個View都包含一個ViewGroup.LayoutParams類或者其派生類,LayoutParams中包 含了View和它的父View之間的關(guān)系,而View大小正是View和它的父View共同決定的。
我們平常使用類似于RelativeLayout和LinearLayout的時候,在其內(nèi)部添加view的時候,不管是布局文件中加入還是在代碼中使用addView方法添加,實際上都會調(diào)用這個onMeasure方法,而measure和onMeasure中的兩個參數(shù),是由各級父控件往子控件/子view進行一層層傳遞的。我們可以在xml中定義Layout的寬和高的具體的值或?qū)捀叩奶畛浞绞?#xff1a;matchparent/wrapcontent,也可以在代碼中使用LayoutParams設(shè)置,而實際上這里設(shè)置的值就會對應(yīng)到上面的measure和onMeasure方法中的兩個參數(shù)的模式,對應(yīng)關(guān)系如下:
具體的值(如width=200dp)和matchparent/fillparent,對應(yīng)模式中的MeasureSpec.EXACTLY
包裹內(nèi)容(width=wrapcontent)則對應(yīng)模式中的MeasureSpec.AT_MOST。
一個view的寬高尺寸,只有在測量之后才能得到,也就是measure方法被調(diào)用之后。具體實現(xiàn)代碼如下:
第一個紅框框就是我們上面說的模式和大小組合才能知道這個view的大小。第二個紅框框是進行計算并保存計算結(jié)果。這里我們想要繪制的是一個正方形,所以取了寬和高中最小值作為width。中間的if和else if是判斷模式的,因為模式可能在設(shè)置的時候出現(xiàn)問題,所以這里進行簡單處理。
.????現(xiàn)在我們繪制棋盤,那我們需要知道棋盤的高度和寬度,以及每行格子數(shù)。那這些數(shù)據(jù)在哪里進行初始化呢?我們選擇在onSizeChanged()中進行設(shè)置。使用這個是因為當(dāng)布局發(fā)生變化時,可以回調(diào)onMeasure重新布局。具體代碼如下:
至于繪制操作我們肯定是放在onDraw()里面,我們使用了一個drawBoard()方法。昨天說過,繪制的時候是需要paint的,所以定義一下paint,此外還要在init中進行初始化(大小,風(fēng)格,抗鋸齒等等)。具體的代碼我們昨天已經(jīng)看過,這里就不再看了。這里就是要注意一下棋盤畫的時候應(yīng)該注意線的初始坐標和結(jié)束坐標,還有縱坐標的變化。
????繪制完棋盤我們就應(yīng)該繪制棋子了,但是棋子的位置我們并不確定,因為這是和用戶交互的,所以我們肯定要在onTouchEvent()函數(shù)中寫交互。但是在這之前,我們要在代碼中引入這兩個棋子的圖片(注意,這里是代碼中引入,和昨天的是不一樣的)。
????·首先得聲明一下兩個棋子:
mWhitePiece是我們棋子的名字。
然后進行棋子的初始化(在init中):
至于書寫格式,大家模仿就可以。
????接下來就是棋子的繪制了。這里要注意的:第一點,要注意棋子不能比我們繪制的格子還要大,大小得適宜;第二點,在繪制棋子的時候要考慮到兩個棋子之間的空隙,不能交叉,也不要緊貼,美觀一些。這就要求我們在繪制的時候一定多注意坐標的設(shè)置。
????為了滿足上面兩點,我們定義一個比例變量,讓棋子等于格子寬的3/4:
那么這里我們就又涉及到棋子尺寸大小的改變了。上面提到過了,布局改變放在onSizeChanged()中:
棋子的寬度我們是行高乘以3/4比例。紅框下面兩行是對白棋和黑棋的繪制,我們使用的是BitMap里的createScaledBitmap方法。
下面講棋子與用戶的交互:
先介紹一下運動事件:
再詳細介紹一下onTouchEvent()方法:
-
public boolean onTouchEvent (MotionEvent event)?
? ?? ??參數(shù)event:參數(shù)event為手機屏幕觸摸事件封裝類的對象,其中封裝了該事件的所有信息,例如觸摸的位置、觸摸的類型以及觸摸的時間等。該對象會在用戶觸摸手機屏幕時被創(chuàng)建。
? ?? ??返回值:該方法的返回值機理與鍵盤響應(yīng)事件的相同,同樣是當(dāng)已經(jīng)完整地處理了該事件且不希望其他回調(diào)方法再次處理時返回true,否則返回false。
? ?? ? 該方法并不像之前介紹過的方法只處理一種事件,一般情況下以下三種情況的事件全部由onTouchEvent方法處理,只是三種情況中的動作值不同。
? ?? ??屏幕被按下:當(dāng)屏幕被按下時,會自動調(diào)用該方法來處理事件,此時MotionEvent.getAction()的值為MotionEvent.ACTION_DOWN,如果在應(yīng)用程序中需要處理屏幕被按下的事件,只需重新該回調(diào)方法,然后在方法中進行動作的判斷即可。
? ?? ??屏幕被抬起:當(dāng)觸控筆離開屏幕時觸發(fā)的事件,該事件同樣需要onTouchEvent方法來捕捉,然后在方法中進行動作判斷。當(dāng)MotionEvent.getAction()的值為MotionEvent.ACTION_UP時,表示是屏幕被抬起的事件。
? ?? ??在屏幕中拖動:該方法還負責(zé)處理觸控筆在屏幕上滑動的事件,同樣是調(diào)用MotionEvent.getAction()方法來判斷動作值是否為MotionEvent.ACTION_MOVE再進行處理。
看上面的介紹我們知道了,在寫五子棋的時候,如果Action是Down形式的,那么手滑點歪的情況不容易解決,當(dāng)然,用戶更不可能在滑動的過程產(chǎn)生落子的行為(噗,,,突然污了,咳咳咳),所以我們使用的是UP形式。要注意,當(dāng)判斷可以處理該行為的時候,返回值應(yīng)該設(shè)為true,上面對于返回值的介紹很清楚了。
????那么這個事件怎么處理呢?用戶落子(我不行了,這個我可以笑好久),我們索要的有效信息是點擊的那個坐標。所以我們在設(shè)一個全局變量來記錄棋子坐標:
這種定義形式需要學(xué)習(xí)。
此外,我們還需要有一個變量來告訴我們到底是黑子在下還是白棋在下:
用布爾形式即可。那這句話的含義就是黑子先手或者是黑子正在下。
接下來我們就該接受用戶點擊的坐標值并進行是否落子的判斷了。
-
第一點我們比較容易想到,就是簡單粗暴的直接獲取橫縱坐標;
-
第二點是說寫一個方法,把這個點封裝起來,放到Point類的p中,而且大家看這個方法是獲得有效的坐標值,那有效的意思是什么呢?我們知道,有的人手粗,有的人手細,有的人就是喜歡用腳或者胳膊肘下棋,還有的人眼睛就是不大好使,那問題就來了,我怎么著都點不到那個行與列的交叉處,接近的位置難道就不認了?為了滿足用戶的這個需求,我們寫一個方法,獲得有效的坐標值:
代碼很簡單,就是取整來減少誤差。只要不是手太粗、胳膊太粗、腳太粗、眼睛太不好使都不會有什么問題的。
-
第四點我們也比較容易想到,得到了坐標值,就把棋子添加進去啊,這里就是判斷一下是黑子下還是白子下。與之有關(guān)的是,當(dāng)我們下完一個子之后,要把棋子的狀態(tài)恢復(fù)到原來的狀態(tài)去,也就是第五點的第二行的含義。
-
在最后不要忘記了一定要加上第五點的invalidate(),用這個函數(shù)進行重繪;
-
那么第三點是什么呢?實際上就是考慮是否全面的問題:用戶不可以在同一個點處重復(fù)落子。
?
那這里與用戶的交互就完成了,下來我們進行棋子的繪制:
這里代碼看起來多,但是重復(fù)性很大,黑子白子邏輯一樣的,繪制棋子這一塊比較麻煩的是坐標值得確定,要考慮間隙,考慮與格子的關(guān)系,大家可以自行畫圖對照代碼進行理解,這里我就不贅述了,不好說啊。。。
給大家看一點效果:(背景圖我很喜歡,但是當(dāng)五子棋的背景圖真是丑啊,棋子也沒有進行摳圖,湊活一下吧先)
視頻地址:https://v.qq.com/x/page/u132224hol2.html
006
原創(chuàng)不易,請多多支持與交流~
總結(jié)
- 上一篇: 《疯狂Java讲义》11
- 下一篇: 安卓第三趴