请看下面截图,例子来自android学习手册,360手机助手中下载,排到第4个,里面有108个例子、源码还有文档
我们在网上看得最多的应该是以下这个方法:
WindowManager wm = getWindowManager();
Display display = wmgetDefaultDisplay();
int screenWidth = displaygetWidth();
int screenHeight = displaygetHeight();
但studio提示它已经过时了,建议不再使用。
再去查看android源码,发现可使用getSize替代,代码如下:
public static Point getSize(Activity act) {
Display display = actgetWindowManager()getDefaultDisplay();
Point size = new Point();
displaygetSize(size); //sizex就是宽度,sizey就是高度
return size;
}
一段时间后发现getWindowManager方法只能在activity中使用或者被activity实例调用,那像我们在Fragment或者Adapter中想获得屏幕分辨率,又有困难了(因为Fragment和Adapter一般只有Context),虽然可以通过强制类型转换来处理,可是毕竟劳民伤财需要时时记得去转换。所以又改了从Context获取系统服务,然后再去取分辨率,改后的代码如下:
public static Point getSize(Context ctx) {
WindowManager wm = (WindowManager) ctxgetSystemService(ContextWINDOW_SERVICE);
Display display = wmgetDefaultDisplay();
Point size = new Point();
displaygetSize(size);
return size;
}
再后来发现通过DisplayMetrics也能获取分辨率
public static Point getSizeNew(Context ctx) {
WindowManager wm = (WindowManager) ctxgetSystemService(ContextWINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wmgetDefaultDisplay()getMetrics(dm);
Point size = new Point();
sizex = dmwidthPixelsdmdensity;
sizey = dmheightPixelsdmdensity;
return size;
}
不久便发现这个新方法时常量错了,结果确认widthPixels和heightPixels就是宽和高,无需再乘上density。因为widthPixels和heightPixels的单位都是像素,而density指的是像素密度,即一个单位内有几个像素,所以在我这边乘上density没有意义,只有除以density才有意义。dmwidthPixels/dmdensity指的是宽度上有多少单位,dmheightPixels/dmdensity指的是高度上有多少单位。网上乘以density的做法,可能只是他们恰好遇上部分特殊机型罢了。最后的代码如下:
public static Point getSizeNew(Context ctx) {
WindowManager wm = (WindowManager) ctxgetSystemService(ContextWINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wmgetDefaultDisplay()getMetrics(dm);
Point size = new Point();
sizex = dmwidthPixels;
sizey = dmheightPixels;
return size;
}
摘自: >
LinearLayout - 线形布局。
orientation - 容器内元素的排列方式。vertical: 子元素们垂直排列;horizontal: 子元素们水平排列
gravity - 内容的排列形式。常用的有 top, bottom, left, right, center 等
FrameLayout - 层叠式布局。以左上角为起点,将 FrameLayout 内的元素一层覆盖一层地显示
TableLayout - 表格式布局。
TableRow - 表格内的行,行内每一个元素算作一列
collapseColumns - 设置 TableLayout 内的 TableRow 中需要隐藏的列的列索引,多个用“,”隔开
stretchColumns - 设置 TableLayout 内的 TableRow 中需要拉伸(该列会拉伸到所有可用空间)的列的列索引,多个用“,”隔开
shrinkColumns - 设置 TableLayout 内的 TableRow 中需要收缩(为了使其他列不会被挤到屏幕外,此列会自动收缩)的列的列索引,多个用“,”隔开
RelativeLayout - 相对定位布局。
layout_centerInParent - 将当前元素放置到其容器内的水平方向和垂直方向的中央位置(类似的属性有 :layout_centerHorizontal, layout_alignParentLeft 等)
layout_marginLeft - 设置当前元素相对于其容器的左侧边缘的距离
layout_below - 放置当前元素到指定的元素的下面
layout_alignRight - 当前元素与指定的元素右对齐
layout_width - 宽。
fill_parent: 宽度跟着父元素走;
wrap_content: 宽度跟着本身的内容走;
直接指定一个 px 值来设置宽
layout_height - 高。
fill_parent: 高度跟着父元素走;
wrap_content: 高度跟着本身的内容走;
直接指定一个 px 值来设置高
<xml version="10" encoding="utf-8">
<LinearLayout xmlns:android=" >
通过启动窗口为例子,大致上明白了WMS是如何添加,更新,移除窗口的工作原理。本文将会重点聊一聊窗口的大小计算逻辑。
下面的源码都是来自Android 90
计算窗口的大小和Android 44相比变化很大。花了一点心思去重新学习了。在Android 44中,窗体的计算在onResume中调用了ViewRootImpl调用relayoutWindow对整个Window重新测量窗口大小以及边距。
relayoutWindow这个方法是做什么的呢?当我们在Activity的生命周期到达了onResume的阶段,此时ViewRootImpl的setView,开始走渲染的View的流程,并且调用requestLayout开始测量渲染。其中有一个核心的逻辑就是调用WMS的relayoutWindow,重新测量Window。
在Android 90中把这个流程和DisplayContent绑定起来。让我们稍微解剖一下这个方法。
relayout大致上要做了以下的事情:
relayout的方法有点长,本次我们将关注这一部分核心的逻辑。分别是两个方法:
能看到在这里面对performSurfacePlacementLoop做最多为6次的循环,这六次循环做什么呢?
能看到这里面的核心逻辑,首先会检查WMS下mForceRemoves集合中是否还有对象。有则调用removeImmediately清空WindowState的中SurfaceControl和WindowContainer之间的绑定和Surface对象,以及销毁WindowAnimator中的Surface。
做这个得到目的很简单,因为下一个步骤将会申请一个Surface对象,而此时如果Android系统内存过大了(OOM),mForceRemoves就存在对象,就可以销毁不需要的Surface。这一点的设计和Davlik虚拟机申请对象时候的思路倒是一致的。
销毁需要一点时间,因此就需要做一个250毫秒的的等待。接着会调用RootWindowContainer的performSurfacePlacement做真正的执行。最后会通过handler通过ViewServer通知事件给DebugBridge调试类中。
每一次loop的最后,如果发现RootWindowContainer需要重新测量,就会把当前这个方法,放入Handler中,等待下次的调用,也是调用6次。这样就能最大限度的保证在这段时间内Window能够测量每一次的窗体参数。
下面这个方法十分长,我们只看核心;
我在上面划分了9个部分:
这里只给总览,之后有机会再进去里面抓细节。
我们能够看到无论是在哪里,如果窗口发生了变化,都会调用updateFocusedWindowLocked方法。实际上这个方法才是真正的核心测量窗口大小逻辑。
这里注意一下isWindowChange是判断输入法焦点是否一致,而窗体焦点则是通过不同的WindowState来判断。
实际上核心测量的真正动作是DisplayContentperformLayout。我们仔细一想也就知道,在Android 90的时候,DisplayContent象征着逻辑屏幕,我们讨论无分屏的情况,实际上就是指我们当前窗体铺满逻辑显示屏各个边距的大小。
在正式开始聊窗体大小的测量之前,实际上,在Android系统中,为了把Window各个边界标记出来,实际上随着时代和审美潮流的演进,诞生越来越多的边距类型,我们往往可以通过这些边距来测定窗体的大小。
在DisplayFrame中有了大致的分区,如下:
可以看到,这些窗体的边距实际上是跟着这些年潮流走的。如Android 70的自由窗体模式,嵌套窗体模式,刘海屏等等,这些边距的共同作用,才会诞生一个真正的Window大小。有了这些基础知识之后,我们去看看测量大小的逻辑。
我们这里把这个方法拆成如下几个部分:
能看到,此时会设置当前显示屏幕的大小,以及获取过扫描区域,还会判断当前手机屏幕是否支持刘海屏。这一切实际上都是由硬件回馈到DisplayService,我们再从中获取的信息。
实际上如果有读者注意到我写的WMS第一篇就会看到实际上WMS初始化的时候,我们能够看到WMS会初始化一个WindowManagerPolicy的策略,而这个策略就是PhoneWindowManager。实际上这也支持了系统开发自定义策略,从而办到自己想要的窗体计算结果。
首先初始化几个参数,父窗体,屏幕,过扫描,可见区域,输入法区域为当前逻辑显示屏的大小,等到后面做裁剪。
能看到所有的事情实际上是关注的是系统UI上的判断,检测NavBar,StatusBar大小。最后再判断当前刘海屏的不允许交叉的区域顶部和显示屏顶部哪个大。如果mDisplayCutoutSafe的top大于mUnrestricted的top,说明mDisplayCutoutSafe在mUnrestricted下面,也就是我上面那个包含一段黑色的区域。此时会拿稳定的应用区域和刘海区域顶部的最大值,作为刘海屏幕的区域。这样就能保证刘海屏的顶部就是状态栏。
提一句如果NavigationBar隐藏,则会创建一个虚假的区域把输入事件都捕捉起来。
里面有四个关键函数:
可以看到所有的所有的间距将会设置为mUnrestricted的初始宽高,也就是不包含OverScan区域。如果是遇到刘海屏,则会根据设置的SafeInset区域来设置mDisplayCutoutSafe的安全区域。也就是我上面那种情况。比如设置了LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT这种情况,显示区域将不会超过刘海屏的底部。
我们关注到mTmpNavigationFrame这个对象的赋值,在正常的情况下的范围是如下:
此时mStable和mStableFullscreen区域的底部都是对应着top,也就是对应着Navigation顶部。System系统元素的底部也是Navigation顶部。
最后经过computeFrameLw重新计算这个区域的值。这个方法稍后会聊到,但是在正常手机开发中,其实是没有变化的。也就说,实际上对于mNavigationBar来说:
同理对于statusBar来说:
注意,此时如果statusBar可见,则做如下计算:
这种情况挺常见的,我们从一个隐藏状态栏的页面跳转到有状态栏的页面,国有有个PopupWindow,你能看到这个popwindow会明显向下移动。
在这个方法中mScreenDecorWindows这个集合实际上是在adjustWindowParamsLw以及prepareAddWindowLw这两个方法中加入。加入的条件是,每当有新的Window加入(WMS的addView)或者Window需要重新调整(WMS的relayoutWindow),当前新增得到Window或者需要重新relayout的Window有StatusBar有权限,且显示则会添加到mScreenDecorWindows集合。
mScreenDecorWindows从上面的描述,能得知实际上这个步骤还没有根据层级作区分。但是没关系,此时仅仅只是初步的测量。
明白了mScreenDecorWindows之后,我们阅读上面这个方法就很简单了。
layoutScreenDecorWindows做的事情就如名字一样,要测量Window上装饰部分,如StatusBar,如输入法。此时经过循环,自尾部往头部调用所有的WindowState的computeFrameLw计算每一个WindowState的对应Window的窗体大小。
当计算出每一个窗体大小之后,将会把事件分成两个情况,当计算出来的当前的Window的left和top都小于等于0,也就是说,当前的Window的顶部边缘并且左边缘超过了当前的屏幕。
说明了有什么东西在右下侧把整个Window定上去了。因此dockFrame的计算就很简单了:
如果计算出来的bottom大于等于屏幕高度且right大于等于屏幕宽度。说明有什么东西在左上方把整个Window顶下去了。
最后再设置这个把displayFrames的可见等区域都设置为dockFrame。联合上下文,实际上这里就是把整个区域的顶部移动到了statusBar之下。
获取屏幕分辨率的方式如下:
一、在activity中
1、DisplayMetrics dm = getResources()getDisplayMetrics();
int screenWidth = dmwidthPixels;
int screenHeight = dmheightPixels;
2、DisplayMetrics dm = new DisplayMetrics();
thisgetWindowManager()getDefaultDisplay()getMetrics(dm);
int screenWidth = dmwidthPixels;
int screenHeight = dmheightPixels;
二、在service中
DisplayMetrics dm = new DisplayMetrics();
dm = getResources()getDisplayMetrics();
int screenWidth = dmwidthPixels;
int screenHeight = dmheightPixels;
三、在非activity中,传一个Context进来调用
1、public ImageGalleryAdapter(Context c) {
myContext = c;
DisplayMetrics dm = cgetResources()getDisplayMetrics();
int screenWidth = dmwidthPixels;
int screenHeight = dmheightPixels;
}
2、public class BaseTools {
public static int getWindowWidth(Context context){
// 获取屏幕分辨率
WindowManager wm = (WindowManager) (contextgetSystemService(ContextWINDOW_SERVICE));
DisplayMetrics dm = new DisplayMetrics();
wmgetDefaultDisplay()getMetrics(dm);
int mScreenWidth = dmwidthPixels;
return mScreenWidth;
}
public static int getWindowHeigh(Context context){
// 获取屏幕分辨率
WindowManager wm = (WindowManager) (contextgetSystemService(ContextWINDOW_SERVICE));
DisplayMetrics dm = new DisplayMetrics();
wmgetDefaultDisplay()getMetrics(dm);
int mScreenHeigh = dmheightPixels;
return mScreenHeigh;
}
}
以上就是关于android 怎么获取当前使用的屏幕分辨率全部的内容,包括:android 怎么获取当前使用的屏幕分辨率、Android 屏幕适配、android activity 各种布局方式以及相关参数等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)