安卓自定義 view 開篇

本開篇是整個自定義 view 系列的開始,主要探討安卓屏幕座標系,顏色以及 MotionEvent 的 getX, getY 和 getRawX, getRawY 的區別。java

屏幕座標系

回想初中數學創建的直角座標系,如圖1-1 android

圖1-1

那在安卓中的屏幕會是什麼樣子呢? 它應該是這個樣子的。如圖 1-2 git

圖1-2

根據本身的理解去驗證是否符合猜測,就在屏幕中去繪製這樣的一個座標系。在自定義控件過程當中,有些座標想不清楚其實也是和沒有深入理解手機座標系的而致使的。如圖1-3 是我在畫布中繪製而成。經過對比能夠看出,手機屏幕的 y 軸正半軸向下,這就是和數學座標系的區別。想要看代碼預覽代碼的點擊此處 ,註釋很清楚的,固然不懂也是不要緊的,本篇只是介紹座標系,若是是小白可能對代碼中使用的 api 不瞭解,別慌,問題不大。日後看會懂的😊, 你只須要理解屏幕座標系和數學中的座標系的區別便可。 github

圖1-3

顏色系統

將顏色以前先說說光的三原色,也就是紅、綠、藍,光學三原色組成顯示屏顯示顏色。在安卓系開發中顏色無處再也不,在自定義 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

MotionEvent 的 getX,getY 和 getRawX, getRawY

講這個區別經過例子來說解,在屏幕上畫一個圓,而後當手指按下後進行移動。分析這個自定義 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 點爲手勢按下的座標。經過公式就能夠輕鬆求出距離。

圖3-1

因此 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 的高度。

相關文章
相關標籤/搜索