Android自定义控件坐标系解析
自定义控件要想彻底的把握,掌握Android各种坐标系及一些API的坐标含义毫无疑问是不可忽视的技能,对于控件的摆放位置、触摸点、控件绘制等都离不开坐标系,所以学习自定义控件之前我们就先来谈一下Android坐标系。
一、Android屏幕坐标系和数学坐标系的区别
(1)、在数学坐标系中以xy轴的交点为坐标原点,x轴向右为正方向,y轴向上为正方向,这对于童鞋们来说已经再熟悉不过了,如图:

(2)、而在手机屏幕上的坐标系与数学坐标系还是有差别的,移动设备一般定义屏幕左上角为坐标原点,x轴向右为正方向,y轴向下为正方向,如图:

二、Android屏幕区域的划分
Android屏幕区域主要划分为五个区域分别为:状态栏区域、ActionBar区域、View布局区域、应用程序App区域、屏幕区域,相互之间又存在嵌套关系。如图所示:

下面我们来看看各个区域高度的获取:
(1)、状态栏区域高度获取:
//第一种方式,使用此方法一定要等界面渲染结束Rect rect = new Rect();getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);int statusBarHeight = rect.top;//第二种方式,获取状态栏高度Resources resources = this.getResources();int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");int height = resources.getDimensionPixelSize(resourceId);//此次获取状态栏高度//第三种方式,通过反射方式获取状态栏高度int statusHeight = -1;try {Class clazz = Class.forName("com.android.internal.R$dimen");Object object = clazz.newInstance();int intheight = Integer.parseInt(clazz.getField("status_bar_height").get(object).toString());//获取状态栏高度statusHeight = this.getResources().getDimensionPixelSize(intheight);} catch (Exception e) {e.printStackTrace();}
(2)、ActionBar区域高度获取:
//第一种方式,此方法要等界面渲染结束int actionBarHeight = getSupportActionBar().getHeight();//第二种方式TypedValue tv = new TypedValue();if (this.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {int actionBarHeightOther = TypedValue.complexToDimensionPixelSize(tv.data, this.getResources().getDisplayMetrics());}
(3)、View布局区域高度获取:
//第一种方式Rect rect = new Rect();getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(rect);//第二种方式//可见当执行onResume和onPause时,onWindowFocusChanged都会被调用。此时界面已渲染结束public void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);if (hasFocus) {int width = view.getMeasuredWidth();//获得宽度int height = view.getMeasuredHeight();//获得高度}}//第三种方式view.post(new Runnable() {public void run() {int width=view.getMeasuredWidth();int height=view.getMeasuredHeight();}})//第四种方式view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {public void onGlobalLayout() {view.getViewTreeObserver().removeOnGlobalLayoutListener(this);int width=view.getMeasuredWidth();int height=view.getMeasuredHeight();}});
(4)、应用程序App区域高度获取:
Rect rect = new Rect();getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
(5)、屏幕区域高度获取:
//第一种方式,该方式在4.1版本后已过时。Display display = getWindowManager().getDefaultDisplay();int width = display.getWidth();int height = display.getHeight();//第二种方式Display defaultDisplay = getWindowManager().getDefaultDisplay();Point point = new Point();defaultDisplay.getSize(point);int x = point.x;int y = point.y;//第三种方式Rect outSize = new Rect();getWindowManager().getDefaultDisplay().getRectSize(outSize);int left = outSize.left;int top = outSize.top;int right = outSize.right;int bottom = outSize.bottom;//第四种方式DisplayMetrics outMetrics = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(outMetrics);int widthPixels = outMetrics.widthPixels;int heightPixels = outMetrics.heightPixels;//第五种方式Point outSizeOther = new Point();getWindowManager().getDefaultDisplay().getRealSize(outSizeOther);int x1 = outSizeOther.x;int y1 = outSizeOther.y;//第六种方式DisplayMetrics outMetrics1 = new DisplayMetrics();getWindowManager().getDefaultDisplay().getRealMetrics(outMetrics1);int widthPixel = outMetrics1.widthPixels;int heightPixel = outMetrics1.heightPixels;
特别注意:上面这些方法最好在Activity的onWindowFocusChanged()方法或者之后调用,因为只有这时候才是真正的显示完全。
三、Android坐标系的分类
上面我们分析了Android屏幕区域的划分,接着我们分析一下与区域相关的Android坐标系,在Android坐标系中可以分为:屏幕坐标系,视图坐标系。
(1)、屏幕坐标系
屏幕坐标系我们前面在和数学坐标系的区别已经介绍过了,以屏幕左上角为坐标原点,x轴向右为正方向,y轴向下为正方向,如图所示:

(2)、视图坐标系
视图坐标系在View绘制过程中,绘制的内容将以坐标系作为参考,最后确定绘制内容在View里面的位置。

下面我们来看看常用的坐标方法,注意这些方法是相对父容器而言的:
| View相关方法 | 方法说明 | 
| view.getLeft() | 当前View的左边缘与它父View的左边缘的距离(视图坐标); | 
| view.getRight() | 当前View的右边缘与它父View的左边缘的距离(视图坐标); | 
| view.getTop() | 当前View的上边缘与它父View的上边缘(顶部)的距离(视图坐标); | 
| view.getBottom() | 当前View的下边缘与它父View的上边缘(顶部)的距离(视图坐标); | 
| View.getTranslationX() | 当前View在X轴的偏移量。初始值为0,向左偏移值为负,向右偏移值为正;(常见于属性动画中) | 
| View.getTranslationY() | 当前View在Y轴的偏移量。初始值为0,向上偏移为负,向下偏移为正;(常见于属性动画中) | 
| View.getX | 当前View在X轴的偏移量。初始值为0,向左偏移值为负,向右偏移值为正;返回值为getLeft()+getTranslationX(),当setTranslationX()变getLeft()不变时,getX()变。 | 
| View.getY | 当前View在Y轴的偏移量。初始值为0,向上偏移为负,向下偏移为正;返回值为getTop()+getTranslationY(),当setTranslationY()变getTop()不变时,getY()变。 | 
为了解释清楚这些方法,准备了张图,如图所示:

此时我们可以获取视图(View)宽高的方法:
| View宽高方法 | 方法说明 | 
| getWidth() | 当前View的宽度,即getRight()-getLeft() | 
| getHeight() | 当前View宽度,即getBottom()-getTop() | 
需要注意的是使用以上方法的过程中要在View测量结束即渲染完成后,不然获取到的值为0。
我们再来看看手指触摸屏幕时MotionEvent提供的一些方法解释:
| MotionEvent坐标方法 | 方法说明 | 
| getX() | 触摸中心点与该View左边缘的距离(相对坐标) | 
| getY() | 触摸中心点与该View上边缘的距离(相对坐标) | 
| getRawX() | 触摸中心点与屏幕左边缘的距离(绝对坐标) | 
| getRawY() | 触摸中心点与屏幕上边缘的距离(绝对坐标) | 
为了解释清楚这些方法,准备了张图,如图所示:

今天的内容就到这啦,本文主要就是阐述View里常用方法及坐标相关的概念,也是为后期的内容做铺垫。
