安卓自定义 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 的高度。

相关文章
相关标签/搜索