自定义控件要想彻底的把握,掌握Android各种坐标系及一些API的坐标含义毫无疑问是不可忽视的技能,对于控件的摆放位置、触摸点、控件绘制等都离不开坐标系,所以学习自定义控件之前我们就先来谈一下Android坐标系。

一、Android屏幕坐标系和数学坐标系的区别

(1)、在数学坐标系中以xy轴的交点为坐标原点,x轴向右为正方向,y轴向上为正方向,这对于童鞋们来说已经再熟悉不过了,如图:

android 定点坐标 纹理坐标 android 控件坐标_android 定点坐标 纹理坐标

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

android 定点坐标 纹理坐标 android 控件坐标_android 定点坐标 纹理坐标_02

二、Android屏幕区域的划分

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

android 定点坐标 纹理坐标 android 控件坐标_android 定点坐标 纹理坐标_03

下面我们来看看各个区域高度的获取:

(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都会被调用。此时界面已渲染结束
   @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        int width = view.getMeasuredWidth();//获得宽度
        int height = view.getMeasuredHeight();//获得高度
    }
  }
  
  //第三种方式
  view.post(new Runnable() {
         @Override
         public void run() {
             int width=view.getMeasuredWidth();
             int height=view.getMeasuredHeight();
         }
     })
     
  //第四种方式
    view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        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轴向下为正方向,如图所示:

android 定点坐标 纹理坐标 android 控件坐标_自定义控件_04

(2)、视图坐标系

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

android 定点坐标 纹理坐标 android 控件坐标_android studio_05

下面我们来看看常用的坐标方法,注意这些方法是相对父容器而言的:

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()变。

为了解释清楚这些方法,准备了张图,如图所示:

android 定点坐标 纹理坐标 android 控件坐标_坐标系_06

此时我们可以获取视图(View)宽高的方法:

View宽高方法 : 方法说明
getWidth(): 当前View的宽度,即getRight()-getLeft()
getHeight(): 当前View宽度,即getBottom()-getTop()

需要注意的是使用以上方法的过程中要在View测量结束即渲染完成后,不然获取到的值为0。

我们再来看看手指触摸屏幕时MotionEvent提供的一些方法解释:

MotionEvent坐标方法: 方法说明
getX(): 触摸中心点与该View左边缘的距离(相对坐标)
getY(): 触摸中心点与该View上边缘的距离(相对坐标)
getRawX(): 触摸中心点与屏幕左边缘的距离(绝对坐标)
getRawY(): 触摸中心点与屏幕上边缘的距离(绝对坐标)

为了解释清楚这些方法,准备了张图,如图所示:

android 定点坐标 纹理坐标 android 控件坐标_坐标系_07

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