写这篇文章的原因是因为坐标遇到的坑太多了,所以要记录一下。
一.基本的坐标与获取
先来看看基本的一些内容。
假如一个屏幕的区域是这样,蓝色的那条叫状态栏(statusBar),下面橙色的那条叫导航(NavigationBar),中间绿色的是应用区域。标题栏是应用区域里面的,因为现在很多都是用到自定义的标题栏而不是系统的标题栏,所以这里不介绍。
1.获取整个屏幕的宽高
整个屏幕的宽高指的是蓝色、绿色加起来的部分。(注意不算橙色部分)
WindowManager windowManager = getWindowManager();
Display display = windowManager.getDefaultDisplay();
screenWidth = display.getWidth();
screenHeight = display.getHeight();
2.获取状态栏高度
蓝色那栏状态栏
public float getStatusBarHeight() {
int height = 0;
try {
Resources resources = applicationContext.getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen","android");
height = resources.getDimensionPixelSize(resourceId);
} catch (Exception e) {
e.printStackTrace();
}
return height;
}
3.获取导航栏的高度
private int getNavigationBarHeight() {
int height = 0;
try {
Resources resources = applicationContext.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
height = resources.getDimensionPixelSize(resourceId);
}catch (Exception e){
e.printStackTrace();
}
return height;
}
4.获取应用区域宽高
这里先讲一个误区,有的人获取应用区域的高度,为了图方便用整体的高度(也就是1中的高度)减去状态栏的高度(也就是2中的高度)
这样做的话普通的手机是没问题的,但是对于前段时间新出的刘海屏手机,就会出问题,举个栗子,比如下面这款手机,我随便找张图
它的状态栏是刘海的那一栏,也就是红框的上边。但是你用1中的方法获取到的并不会是整个屏幕的高度,而是红框中的区域,所以你用 小标题1获取的高度减去 小标题2的高度的话,肯定不准确的。
所以在获取高度这个操作上,最好不要使用运算操作,因为android机型很多,谁知道有什么机型会不会出问题。所以我们应该直接获取应用区域的宽高
Rect outRect = new Rect();
getActivity().getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect);
int height = outRect.height()
int width= outRect.width()
5.测试高度
我们来测试下这4种方法获取到的高度,可以自己试试然后打印出来
我的手机是1920 x 1080 结果如下
(1)小标题1获取高度为1794
(2)小标题2获取高度为72
(3)小标题3获取高度为126
(4)小标题4获取高度为1722
可以看出 屏幕高度1794 + 底部导航栏高度126 = 1920
屏幕高度1794 - 状态栏高度72 = 应用区域高度 1722
二.获取高度时的坑
你以为能正常获取到这些高度就能正常使用?那还是太天真,这些东西的坑填都填不完,我也只能总结一些我碰到的,然而这肯定也仅仅是冰山一角。
1.状态栏的坑
这个就是我上面说的,普通手机获取整个屏幕的高度是正常的,但是在有刘海的手机上,获取的高度是不包括刘海部分的高度。
注意:这点我只是根据我用过的刘海机测试得出的,但是我不排除会有其它某些刘海机也会把刘海那部分的高度算进去。
2.底部导航栏的坑
在小标题4中的获取应用区域高度中,可以看出我计算得到内容区域的结果是1722,它是整个屏幕减去状态栏再减去导航栏得到的结果。
但是有些情况下,你会在没有显示导航栏的时候获取应用区域高度,比如旋转手机,如果在onConfigurationChanged方法中获取高度,对于某些手机,比如华为手机,会先计算outRect.height()再显示导航栏,这时outRect.height()的高度就是1722+126 而不是 1722 ,所以显示就会有问题。
对于这个问题,我的解决办法是在activity中outRect.height()方法只获取一次,因为activity是有生命周期的,第一次获取肯定会是在导航栏显示后获取,所以肯定是准确的,但这个解决方案的前提是一定会显示导航栏。
3.横屏情况下导航栏的坑
对于某些手机,当你切换至横屏的时候,它的导航栏会一直显示在右侧,比如我的华为机,而有些手机导航栏可能会在左侧也可能会在右侧,因为它一直固定在底部,比如小米的某些手机。那这种情况会出现什么问题呢,下面的内容我会涉及到,这里先把它引出来。
三.屏幕中各种坐标
这个问题其实很简单,你随便百度一下就能得到一张很可观的图,我这随便拿一张来说
这样看一眼就知道这些方法获取到的是哪段距离,然后我也只重点讲下期中一些坐标
1.getRawX与getRawY
这里我要说下这两个方法。这两个方法获取到的是距离屏幕边缘的距离,而不是应用区域的距离,如图
2.窗口WindowManager
如果你要自定义一个有宽高的窗口,你可以设置它的属性使用WindowManager.LayoutParams
而这个windowMangerlp可以设置两个参数windowMangerlp.x和windowMangerlp.y来表示这个窗口左上角的坐标。
注意,关键的地方来了,这个坐标的参考系不是整个屏幕,而是应用区域,也就是下图中红框的区域,而(0,0)则是我标出来的那个点。
这里我就要补上上面说的第三个坑了,在做悬浮窗的时候,悬浮窗要随手指移动,动态设置悬浮窗的位置的方法是
windowMangerlp.x = event.getRawX() - event.getX()
windowMangerlp.y = event.getRawY() - event.getY()
那么问题来了,getRawX和windowMangerlp.x的坐标系并不是同一个坐标系,简单来说,在底部导航栏横屏时在左边的情况,你点我上图中画圈的那点,getRawX和getRawY以我的手机坐标不是(0,0)而是(126,72),那悬浮窗设置的坐标是(126,72),那显示的位置就不是画圈的那个地方了。
这里不光是这个问题,我只是举例栗子,我想引出的问题是要做坐标计算的操作,无论你处于什么状况,都必须要让这两个坐标的计算方法处于同一个坐标系中。可能说得不太好理解,拿我这个来说windowMangerlp.x和event.getRawX()进行了计算操作,但它们不处于同个坐标系,所以可能会出问题。
那我这里就想办法把这两个坐标放在同个坐标系中,要怎么做,无非两种方法,第一种把windowMangerlp.x 的坐标系扩展到getRawX的坐标系中,第二种方法是把getRawX的坐标系缩小到windowMangerlp.x 的坐标系中,我这里选择第二种方法。
其实我觉得这个变坐标系才是最难的操作,说白的就是把一个坐标系的(0,0)变成另一个坐标系的(0,0),这个不理解的话说明数学没学好
比如这里把event.getRawX()的0变成windowMangerlp.x的0,把event.getRawY()的0变成windowMangerlp.y的0,。看我的图第一眼的想法就是event.getRawX() - 导航栏高度,event.getRawY() - 状态栏高度。但是这样是不可行的,为什么,因为这两个高度是死的,就算我状态栏不显示或者我导航栏在右边等等,这样减的话肯定是错误的,那我们必须要动态获取画圈的地方距屏幕(0,0)的距离。
这里我想到了一个骚操作,其实这个距离,就是我内容区域view距屏幕的距离
// 获取屏幕view
View decorView = ((Activity)mContext).getWindow().getDecorView();
view screenView = decorView.findViewById(android.R.id.content);
screenView 就是红框的区域
int[] position = new int[2];
screenView.getLocationInWindow(position);
int paddingLeft = position[0];
int paddingTop = position[1];
然后修改坐标系
windowMangerlp.x = event.getRawX() - event.getX() - position[0];
windowMangerlp.y= event.getRawY() - event.getY() - position[1];
用到坐标的地方坑比较多,只能碰到或怎样再补充,如果可以,建议App固定竖屏,因为很多问题其实都是横屏或者转屏引起的,一直设置为竖屏就没这么多逼事。