本開篇是整個自定義 view 系列的開始,主要探討安卓屏幕座標系,顏色以及 MotionEvent 的 getX, getY 和 getRawX, getRawY 的區別。java
回想初中數學創建的直角座標系,如圖1-1 android
那在安卓中的屏幕會是什麼樣子呢? 它應該是這個樣子的。如圖 1-2 git
根據本身的理解去驗證是否符合猜測,就在屏幕中去繪製這樣的一個座標系。在自定義控件過程當中,有些座標想不清楚其實也是和沒有深入理解手機座標系的而致使的。如圖1-3 是我在畫布中繪製而成。經過對比能夠看出,手機屏幕的 y 軸正半軸向下,這就是和數學座標系的區別。想要看代碼預覽代碼的點擊此處 ,註釋很清楚的,固然不懂也是不要緊的,本篇只是介紹座標系,若是是小白可能對代碼中使用的 api 不瞭解,別慌,問題不大。日後看會懂的😊, 你只須要理解屏幕座標系和數學中的座標系的區別便可。 github
將顏色以前先說說光的三原色,也就是紅、綠、藍,光學三原色組成顯示屏顯示顏色。在安卓系開發中顏色無處再也不,在自定義 view 中更是常常用到。經常使用的顏色設置方法以下:canvas
// 經過十六進制的方式設置
mPaint.setColor(Color.parseColor("#E2C0D6"));
// 沒有透明度設置通道
mPaint.setColor(Color.rgb(100, 100,100));
// 有透明度, 其中 a 表示 alpha , r 表示red, g 表示 green, b 表示 b
mPaint.setColor(Color.argb(200,100,100, 100));
複製代碼
值得注意的是,安卓手機屏幕是不可能有透明度的變化的,咱們設置的透明度實際上是將 R、G、B 與畫布的顏色進行混合而的到的。這樣達到透明度的效果。有不少這樣的圖形混合方式,將在後續講解。api
講這個區別經過例子來說解,在屏幕上畫一個圓,而後當手指按下後進行移動。分析這個自定義 view 的實現,無非就是須要處理按下的手指是否在圓上,以及怎樣記錄移動的座標,根據記錄的座標不斷更新圓的位置, 效果以下:bash
首先新建 MotionEventView 繼承自 View 重寫帶有兩個參數的構造方法,這是爲了在佈局中能使用,佈局中使用的自定義控件會調用帶有兩個參數的構造方法。接着初始化畫筆。app
private void initPaint() {
// 初始化畫筆並設置抗拒址以及抗抖動.
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
// 設置填充.
mPaint.setStyle(Paint.Style.FILL);
// 設置畫筆的顏色
//mPaint.setColor(Color.parseColor("#E2C0D6"));
mPaint.setColor(Color.parseColor("#880000"));
}
複製代碼
以及記錄移動的座標,初始化時爲 view 的中心點座標,當前的 view 和屏幕寬高一致。ide
public MotionEventView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
// 記錄手指移動的座標
mPoint = new PointF();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
// 開始初始化爲 view 的中心點座標
mPoint.set((float) mWidth / 2, (float) mHeight / 2);
}
複製代碼
注意的是 onSizeChanged 是 view 通過測量後的到確切的大小。其中 oldw,oldh 是當 view 發生該變致使從新繪製,記錄的是上一次 view 測量的大小。而 w,h 是 view 最終通過測量肯定的大小。佈局
接下來,在 onDraw 方法中繪製圓。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 繪製一個圓, 肯定一個圓須要一個圓心座標和半徑,而後以半徑旋轉一週獲得一個圓,
// 這裏第一個和第二個參數爲圓心座標,
// 第三個參數爲 半徑,
// 第四個參數是畫筆
canvas.drawCircle(mPoint.x, mPoint.y, radius, mPaint);
}
複製代碼
最後就是處理手勢移動過程啦!在安卓中要處理手勢移動,只需實現 onTouchEvent 方法或者經過 view 的 setOnTouchListener 實現 onTouch 方法也可。處理其中手勢按下,移動,擡起 或意外事件終結各個狀態。
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN: // 根據名字能夠看出,這是手勢按下
// 手指按下時須要檢查是否在圓上
mHasPressed = hasPressed(event);
if (mHasPressed) {
Toast.makeText(getContext(), "點擊到圓上", Toast.LENGTH_SHORT).show();
}
break;
case MotionEvent.ACTION_MOVE:
if (mHasPressed) {
// 手勢移動的時,將新的座標賦值給 mPoint
mPoint.set(event.getX(), event.getY());
}
// 調用這個方法會從新繪製,調用 onDraw 方法。
invalidate();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// 手勢擡起或意外終結將圓還原到初始位置,以及重置標誌位。
mPoint.set((float) mWidth / 2, (float) mHeight / 2);
mHasPressed = false;
invalidate();
break;
}
複製代碼
上面檢查手勢按下的座標是否在圓上,這也是在初等數學中學到的。也就是按下的點與圓心的距離和半徑的差值,若是大於0,在圓外,小於等於0 在圓內。其本質就是勾股定理,看一張圖。 只要想象 O 點爲圓心座標, B 點爲手勢按下的座標。經過公式就能夠輕鬆求出距離。
因此 hasPressed 的計算方法以下:
/**
* 檢查是否 down 事件在圓上
* @param event
* @return
*/
public boolean hasPressed(MotionEvent event) {
// 當前按下的位置距離 view 的左上角位置.
float x = event.getX();
float y = event.getY();
// 要判斷一個點是否在圓上,根據公式 按下的點和圓心的距離小於等於半徑, 實際上就是初中學的勾股定理
// Math.pow 爲求一個數的幾回方, 這裏是 2 次方
double distance = Math.sqrt(Math.pow(x - mPoint.x, 2) + Math.pow(y - mPoint.y, 2));
if (distance <= radius) {
return true;
}
return false;
}
複製代碼
若是留心看代碼的話,能夠看到咱們使用的是 event.getX(), event.getY() 來獲取的按下的座標。這個點的含義是對於當前的視圖, 好比這個就是針對的是咱們自定義的 MotionEventView, 而 getRawX(), getRawY() 是以屏幕的座標系爲參考。讀者能夠自行將 event.getY() 改成 getRawY() 試試看,看看是否是會發現按下的點要在圓上方一點。這是由於 getRawY 的座標多了一個 ActionBar 的高度。