本次要練習的是自定義控件,並不是數學邏輯 ,所以主要的目的就是繪製棋盤。
養的貓咪跑丟了 鬱悶ing….
首先看看效果圖,然後分析一下。
首先,背景圖 這個交給父容器
然後棋盤,可以分爲兩部分,第一是線、 第二是棋子。
首先畫線的時候,肯定要求畫的是矩形,所以必須要以長和寬當中最小的那一方來作爲棋盤的邊。
可以注意到有個細節是 在棋盤的左右離手機屏幕都有一定距離的。
棋盤是10*10的。
棋子落在線的交點上。
分析:
想要知道長和寬當中最小的那一方,前提是知道長和寬,獲取長和寬的方法
那麼獲取寬高可以這樣:
onMeasure方法中:
int width_size = MeasureSpec.getSize(widthMeasureSpec);
int height_size = MeasureSpec.getSize(heightMeasureSpec);
Log.e("ASD", getMeasuredWidth() + "****" + getMeasuredHeight());
Log.e("ASD", getWidth() + "++++" + getHeight());
Log.e("ASD", width_size + "----" + height_size);
可是,這三個哪個是設備寬高、哪個是獲取控件寬高呢?於是我測了一下:
<sunshine.myview2.MyView
android:layout_width="100dp"
android:layout_height="100px" />
06-28 20:57:55.872 13720-13720/sunshine.myview2 E/ASD: 0,0
06-28 20:57:55.872 13720-13720/sunshine.myview2 E/ASD: 0,0
06-28 20:57:55.912 13720-13720/sunshine.myview2 E/ASD: 0****0
06-28 20:57:55.912 13720-13720/sunshine.myview2 E/ASD: 0++++0
06-28 20:57:55.912 13720-13720/sunshine.myview2 E/ASD: 200----1118
06-28 20:57:55.912 13720-13720/sunshine.myview2 E/ASD: 200****1118
06-28 20:57:55.912 13720-13720/sunshine.myview2 E/ASD: 0++++0
06-28 20:57:55.912 13720-13720/sunshine.myview2 E/ASD: 200----100
06-28 20:57:56.116 13720-13720/sunshine.myview2 E/ASD: 200****100
06-28 20:57:56.116 13720-13720/sunshine.myview2 E/ASD: 200++++100
06-28 20:57:56.116 13720-13720/sunshine.myview2 E/ASD: 200----1118
06-28 20:57:56.116 13720-13720/sunshine.myview2 E/ASD: 200****1118
06-28 20:57:56.116 13720-13720/sunshine.myview2 E/ASD: 200++++100
06-28 20:57:56.116 13720-13720/sunshine.myview2 E/ASD: 200----100
打開程序時候控件是會不斷測量的(都這麼說我也就當成定理了),應該看最後測量的結果。
getMeasuredWidth()獲取的是200,getMeasuredHeight()獲取的是1118.
getWidth()獲取的是200,getHeight()獲取的是100.
width_size是200,height_size是100.
獲取到100的肯定都是像素了,獲取到200應該是由於屏幕密度的原因,由dp轉化爲px時候是1:2的關係了,也就都是獲取本控件的大小(px)。
1118是什麼鬼呢,爲啥前面是200,後面是1118!!!???
上網查了一下資料 覺得這裏說的很好這裏就不費時間了,主要內容如下:
①getMeasuredWidth方法獲得的值是setMeasuredDimension方法設置的值,它的值在measure方法運行後就會確定
②getWidth方法獲得是layout方法中傳遞的四個參數中的mRight-mLeft,它的值是在layout方法運行後確定的
③一般情況下在onLayout方法中使用getMeasuredWidth方法,而在除onLayout方法之外的地方用getWidth方法。
取兩者中最小的作爲矩形的邊後(minWidth),我們發現還有個距離設備邊框那邊距的問題(相對於控件是margin),我們將邊距預留爲一份棋盤格子的minWidth,左右各1/2minWidth,也就是說我們畫線的時候要預留1/2minWidth的邊距。
畫線代碼(注意線的起點和重點,重複了就畫不出來了 =.= 由於不熟練,折騰了一番纔出來):
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < 10; i++) {
//橫線
canvas.drawLine(
lineWidth / 2, (float) ((0.5 + i) * lineWidth),
minWidth - lineWidth / 2, (float) ((0.5 + i) * lineWidth),
paint_line);
//縱線
canvas.drawLine(
(float) ((0.5 + i) * lineWidth), lineWidth / 2,
(float) ((0.5 + i) * lineWidth), minWidth - lineWidth / 2,
paint_line);
}
}
費了一番力氣的效果圖(畫筆設置如下):
setBackgroundColor(0x99ff0000);
paint_line = new Paint();
paint_line.setColor(0xff00ff00);
paint_line.setStyle(Paint.Style.STROKE);
paint_line.setAntiAlias(true);
paint_line.setDither(true);
OK,下一步就是棋子了,要想顯示棋子首先得先加載棋子,加載時候要先設定棋子大小,棋子大小要在onSizeChanged中設置(爲什麼呢,看方法名就能理解差不多了吧 - . - ),設置大小是用Bitmap.createScaledBitmap方法,一開始我寫錯成Bitmap.createBitmap了
createScaledBitmap(Bitmap src, int dstWidth, int dstHeight,boolean filter)
createBitmap(Bitmap source, int x, int y, int width, int height,Matrix m, boolean filter)
後來去看了下才知道Bitmap.createScaledBitmap也是調用的Bitmap.createBitmap方法。
加載完之後就要顯示出來啊,那怎麼顯示出來呢,肯定是觸發了之後顯示,那怎麼觸發呢,肯定找我們onTouchEvent(MotionEvent event)方法啊…..
@Override
public boolean onTouchEvent(MotionEvent event) {
//如果沒有ACTION_DOWN返回true 就不會執行ACTION_UP,不知道爲什麼
if (event.getAction() == MotionEvent.ACTION_DOWN) {
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
Point point = new Point(
(int) (event.getX() / lineWidth), (int) (event.getY() / lineWidth)
);
points.add(point);
invalidate();//重繪
return true;
}
return super.onTouchEvent(event);
}
重繪後再onDraw中畫出棋子就Ok了
for (int i = 0; i < points.size(); i++) {
Log.e("asd", points.get(i).x + "---" + points.get(i).y);
canvas.drawBitmap(stone_b,
(int) (lineWidth / 2 - lineWidth * ratio / 2) + points.get(i).x * lineWidth,//x
(int) (lineWidth / 2 - lineWidth * ratio / 2) + points.get(i).y * lineWidth,//y
null);
}
看下最後的“成果”