本开篇是整个自定义 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 的高度。