安卓开发中如何获得Activity的界面的大小

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓开发中如何获得Activity的界面的大小相关的知识,希望对你有一定的参考价值。

注意,这里说的界面大小不是指整个屏幕,因为在非全屏的时候程序界面上方是有状态栏和标题栏的,如果要获得除了状态栏和标题栏以外的界面的尺寸,要用什么方法呢?(Java)

android系统中,Activity窗口的大小是由WindowManagerService服务来计算的。WindowManagerService服务会根据屏幕及其装饰区的大小来决定Activity窗口的大小。一个Activity窗口只有知道自己的大小之后,才能对它里面的UI元素进行测量、布局以及绘制。本文将详细分析WindowManagerService服务计算Activity窗口大小的过程。
  一般来说,Activity窗口的大小等于整个屏幕的大小,但是它并不占据着整块屏幕。为了理解这一点,我们首先分析一下Activity窗口的区域是如何划分的。
  我们知道,Activity窗口的上方一般会有一个状态栏,用来显示3G信号、电量使用等图标,如图1所示。
  

  图1 Activity窗口的Content区域示意图
  从Activity窗口剔除掉状态栏所占用的区域之后,所得到的区域就称为内容区域(Content Region)。顾名思义,内容区域就是用来显示Activity窗口的内容的。我们再抽象一下,假设Activity窗口的四周都有一块类似状态栏的区域,那么将这些区域剔除之后,得到中间的那一块区域就称为内容区域,而被剔除出来的区域所组成的区域就称为内容边衬区域(Content Insets)。Activity窗口的内容边衬区域可以用一个四元组(content-left, content-top, content-right, content-bottom)来描述,其中,content-left、content-right、content-top、content-bottom分别用来描述内容区域与窗口区域的左右上下边界距离。
  我们还知道,Activity窗口有时候需要显示输入法窗口,如图2所示。
  

  图2 Activity窗口的Visible区域示意图
  这时候Activity窗口的内容区域的大小有可能没有发生变化,这取决于它的Soft Input Mode。我们假设Activity窗口的内容区域没有发生变化,但是它在底部的一些区域被输入法窗口遮挡了,即它在底部的一些内容是不可见的。从Activity窗口剔除掉状态栏和输入法窗口所占用的区域之后,所得到的区域就称为可见区域(Visible Region)。同样,我们再抽象一下,假设Activity窗口的四周都有一块类似状态栏和输入法窗口的区域,那么将这些区域剔除之后,得到中间的那一块区域就称为可见区域,而被剔除出来的区域所组成的区域就称为可见边衬区域(Visible Insets)。Activity窗口的可见边衬区域可以用一个四元组(visible-left, visible-top, visible-right, visible-bottom)来描述,其中,visible-left、visible-right、visible-top、visible-bottom分别用来描述可见区域与窗口区域的左右上下边界距离。
  在大多数情况下,Activity窗口的内容区域和可见区域的大小是一致的,而状态栏和输入法窗口所占用的区域又称为屏幕装饰区。理解了这些概念之后,我们就可以推断,WindowManagerService服务实际上就是需要根据屏幕以及可能出现的状态栏和输入法窗口的大小来计算出Activity窗口的整体大小及其内容区域边衬和可见区域边衬的大小。有了这三个数据之后,Activity窗口就可以对它里面的UI元素进行测量、布局以及绘制等操作了。
  从前面Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析一文可以知道,应用程序进程是从ViewRoot类的成员函数performTraversals开始,向WindowManagerService服务请求计算一个Activity窗口的大小的,因此,接下来我们就从ViewRoot类的成员函数performTraversals开始分析一个Activity窗口大小的计算过程,如图3所示。
  

  图3 Activity窗口大小的计算过程
  这个过程可以分为11个步骤,接下来我们就详细分析每一个步骤。
  Step 1. ViewRoot.performTraversals
  这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中,它的实现很复杂,一共有600-行,不过大部分代码都是用来计算Activity窗口的大小的,我们分段来阅读:

  [java] view plaincopypublic final class ViewRoot extends Handler implements

ViewParent,

  View.AttachInfo.Callbacks

  ......

  private void performTraversals()

  ......

  final View host = mView;

  ......

  int desiredWindowWidth;

  int desiredWindowHeight;

  int childWidthMeasureSpec;

  int childHeightMeasureSpec;

  ......

  Rect frame = mWinFrame;

  if (mFirst)

  ......

  DisplayMetrics packageMetrics =

  mView.getContext().getResources().getDisplayMetrics();

  desiredWindowWidth = packageMetrics.widthPixels;

  desiredWindowHeight = packageMetrics.heightPixels;

   else

  desiredWindowWidth = frame.width();

  desiredWindowHeight = frame.height();

  if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight)

  ......

  windowResizesToFitContent = true;

  

  
复制代码

  这段代码用来获得Activity窗口的当前宽度desiredWindowWidth和当前高度desiredWindowHeight。
参考技术A 获取android屏幕大小 int width = this.getWindowManager().getDefaultDisplay().getWidth(); int hight = this.getWindowManager().getDefaultDisplay().getHeigh();本回答被提问者采纳 参考技术B Window.getHitht();
Window.getWidth();追问

呃……Window类好像没有getHeight和getWidth的函数啊……

学习安卓开发[2] - 在Activity中托管Fragment

在上一篇学习安卓开发[1]-程序结构、Activity生命周期及页面通信中,学习了Activity的一些基础应用,基于这些知识,可以构建一些简单的APP了,但这还远远不够,本节会学习如何使用Activity托管Fragment的方式来进行开发

为什么需要Fragment

单纯使用Activity的局限

为什么需要Fragment呢,这要从Activity的局限说起。在前面使用Activity的过程中已经发现,Activity很容易被销毁重建,甚至是在设备旋转的时候也会被销毁,为了返回之前的状态需要保存各种界面相关的信息。
再来假设一种比较常见的场景,一个列表界面+明细界面构成的应用,如果用两个Activity来实现也可以,但如果用户在平板设备上运行应用,则最好能同时显示列表和明细记录,类似网易云、QQ那样在屏幕左侧约1/3的区域显示列表,右侧剩余的区域展示详细信息,这是使用两个Activity无法满足的;另外,查看能否在用户想查看下一条明细时不必回退、再点击进入明细界面,而是采用在屏幕横向滑动切换到下一条这样的快捷手势呢,这也是两个Activity无法满足的。

Fragment介绍

接下来该是Fragment隆重登场的时候了,可以说Fragment就是为了应对UI的灵活需求而生的,Fragment是在API 11中开始引入的,当时Google发布了第一台平板设备。
那么什么是Fragment呢,Fragment是一种控制器对象,可以在Activity的托管下进行用户界面的管理,受其管理的界面可以是整个屏幕区域,也可以是一小部分,Fragment(碎片)就是这个意思。
要让Activity能够托管Fragment,则需要activity视图预留fragment插入其中的位置。一个activity视图中可以插入过个fragment视图。Fragment本身没有在屏幕上显示视图的能力,所以它必须放置在Activity的视图层级中。

如何使用Fragment

代码实现
容器视图和Activity

在文件activity_fragment.xml中定义容器视图:

<FrameLayout android:id="@+id/fragment_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

在Activity中定义了一个用于放置Fragment的FrameLayout,这个容器视图可以托管任意的Fragment。
对应Activity的代码在CrimeActivity.java为:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_fragment);

	FragmentManager fm = getSupportFragmentManager();
	Fragment fragment = fm.findFragmentById(R.id.fragment_container);
	if (fragment == null) {
		fragment = new CrimeFragment();
		fm.beginTransaction()
				.add(R.id.fragment_container, fragment)
				.commit();
	}
}
FragmentManager

这段代码的作用是:在资源ID为R.id.fragment_container的FrameLayout容器中,找到fragment,然后判断获取的fragment是否为空,如果为空则创建新的名为CrimeFragment的Fragment实例,将其添加到FragmentManager所维护的队列中,并在容器R.id.fragment_container中显示。
除了这种用代码将fragment交给Activity托管的方式,还可以在xml中直接将fragment签入activity,但为了能够动态地更换fragment,唯一能采用的便是前面采用的代码的方式。
在设备旋转或回收内存时,Android系统会销毁Activity,但FragmentManager会将fragment队列保存下来。Activity被重建时,新的FragmentManager会首先获取保存的队列(这就是使用了Fragment后,不会有像之前那样旋转就会设备导致状态丢失的现象的原因)。所以代码里会先判断fragment是否为null,只有为null的时候才会重新向队列中添加fragment。

Fragment的生命周期

Fragment的生命周期如下图所示:

可见Fragment的生命周期与Activity的生命周期非常类似,实际上Fragment的许多方法对应着activity的生命周期方法。
Fragment的onCreate方法:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
}

Activity的onCreate方法:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
}

两者的区别在于Fragment.OnCreate()是公共方法,而Activity.OnCreate()是受保护方法,Activity的生命周期方法由操作系统调用,而Fragment的生命周期方法则是由托管它的Activity调用的。

以上是关于安卓开发中如何获得Activity的界面的大小的主要内容,如果未能解决你的问题,请参考以下文章

【安卓开发】activity不能完全关闭

部分安卓手机上出现切换界面时软键盘无法收回的情况,请问该如何解决?

安卓activity劫持测试工具开发

安卓应用开发中Activity之间怎么用按钮进行跳转?

安卓开发学习04

在安卓低版本窗口activity点击空白处如何消失