自定义View基础——Android中的坐标

一、基础

很多人一想到学习自定义UI就开始去研究Measure、Draw、Layout、画笔、画布、动画等这些知识,但是学着学着却发现越来越难。出现这样的现象,我觉得最重要的一个原因就是我们还没学会爬就开始想着跑了。既然要学习自定义View,却不了解Android各种坐标系以及它的各个API的坐标含义绝对是在误入歧途。你都不知道你当前View在整个坐标系中相对坐标,那你绘制出来又有什么意义呢?所以学习Android中的坐标系是一个不可忽视的技能。

说到Android坐标系其实就是一个三维坐标系,但是这个三维坐标系和我们平时见到的三维坐标系却又有一些不同。在Android中,将屏幕的左上角的顶点作为Android坐标系的原点,这个原点向右是X轴正方向,原点向下是Y轴正方向。原点正上方是Z轴正方向。

二、Android屏幕区域划分

我们先看一副图来了解一下Android屏幕的区域划分如下:

 通过上图我们可以很直观的看到Android对于屏幕的划分。下面我们就给出这些区域里常用区域的一些坐标或者度量方式。如下:

  • 获取屏幕区域的宽高等尺寸获取
    //第一种方式,该方式在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 outSize = new Point();
    getWindowManager().getDefaultDisplay().getRealSize(outSize);
    int x = outSize.x;
    int y = outSize.y;

	//第六种方式
    DisplayMetrics outMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getRealMetrics(outMetrics);
    int widthPixel = outMetrics.widthPixels;
    int heightPixel = outMetrics.heightPixels;
  •  应用程序区域宽高等尺寸获取
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
  •  获取状态栏高度
    //第一种方式
	//该方法依赖于WMS(窗口管理服务的回调),使用此方法一定要等界面渲染结束
	Rect rect= new Rect();
	getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
	int statusBarHeight = rectangle.top;
    //第二种方式
    /**
     * 获取状态栏高度
     * @param context
     */
    Resources resources = context.getResources();
    int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
    int height = resources.getDimensionPixelSize(resourceId);//此次获取状态栏高度

    //第三种方式
    /**
     * 通过反射方式获取状态栏高度
     * @param context
     * @return
     */
    int statusHeight = -1;
    try{
        Class<!--?--> clazz = Class.forName("com.android.internal.R$dimen");
        Object object = clazz.newInstance();
        intheight = Integer.parseInt(clazz.getField("status_bar_height")
                .get(object).toString());
        statusHeight = context.getResources().getDimensionPixelSize(height);//此次获取状态栏高度
    }catch(Exception e) {
        e.printStackTrace();
    }
  •  ActionBar高度获取
//第一种方式
	int actionBarHeight = getActionBar().getHeight();
	
//第二种方式
	TypedValue tv = new TypedValue();
	if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
	int actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,context.getResources().getDisplayMetrics());
	}
  •  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();
        }
    });

 特别注意:在Activity的onCreate()或者onResume()中去获得View的高度的时候不能正确获得宽度和高度信息,这是因为 View的measure过程和Activity的生命周期不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时是否已经完成了界面测量,如果还没有测量完,那么获得的宽高就是0。所以上面这些方法最好在Activity的onWindowFocusChanged ()方法或者之后调运,因为只有这时候才是真正的显示OK。

三、Android中的坐标系

Android中的坐标系可分为三类:屏幕坐标系、布局坐标系和视图坐标系

1、屏幕坐标系

该坐标系是以屏幕的左上角为原点(0, 0), 水平向右代表 x 方向的正方向, 垂直向下代表 y方向的正方向。

 2、布局坐标系

该坐标系是以View布局区域的左上角为坐标原点, 水平向右代表 x 方向的正方向,垂直向下代表 y 方向的正方向。

 3、视图坐标系

该坐标系是在View绘制过程中,绘制的内容将该坐标系作为参考,来绘制View也就是内容在View里面的位置。

 上面我们分析了Android坐标系和屏幕区域的划分,可以发现我们平时开发的重点其实都在关注View布局区域,那么下面我们就先来细说一下View区域相关的各种坐标及其获取方法。先看下面这幅图:

 上图我们可以很直观的给出试图View一些坐标相关的方法解释,不过必须要明确的是上面这些方法必须要在layout之后才有效,如下:

 同时View还有关键的点击事件MotionEvent相关坐标,如下图:

 可以看见上图中给出了手指触摸屏幕时MotionEvent提供的一些方法解释,如下:

 这其中只有getRawX()和getRawY()是相对屏幕的。

上面都是View相对静止的Android坐标,但是除了这些坐标,View还有很多与这些坐标相关的尺寸需要我们去了解,如下:

发布了29 篇原创文章 · 获赞 3 · 访问量 886

猜你喜欢

转载自blog.csdn.net/LVEfrist/article/details/103509384