WindowManager解析

WindowManager解析_第1张图片

 WindowManager是什么

首先来看一张Android架构图,我们会发现WindowManager是属于Application Framework层的,即应用程序框架层,这一层的定义是:这一层是编写Google发布的核心应用时所使用的API框架,开发人员同样可以使用这些框架来开发自己的应用,这样便简化了程序开发的结构设计,但是必须要遵守其框架的开发原则。WindowManager就是其中一个系统提供的API框架。 
WindowManager Service 是全局的,是唯一的。Window Manager 将用户的操作,翻译成为指令,发送给呈现在界面上的各个Window。Activity会将顶级的控件注册到 Window Manager 中,当用户真是触碰屏幕或键盘的时候,Window Manager就会通知到,而当控件有一些请求产生,也会经由ViewParent送回到Window Manager中。从而完成整个通信流程。
整个Android的窗口机制是基于WindowManager,这个接口可以添加view到屏幕,也可以从屏幕删除view。Android系统中的“窗口”类型虽然很多,但只有两大类是经常使用的:一是由系统进程管理的,称之为“系统窗口”;第二个就是由应用程序产生的,用于显示UI界面的“应用窗口”。
WindowManager 是一个负责统筹管理所有窗口的一个服务,从始到终一直在运作。之所以扯上WMS,因为它才是大Boss,所有的窗口变化都要通知到它。而WindowManager虽然与它没有之间的关系,但是对它负责,所有信息会经过一定的途径传回到WMS中。其实我们的Activity或者Diolog底层的实现也是通过WindowManager,这个 WindowManager是全局的,整个系统就是这个唯一的东东。它是显示View的最底层了。所以如果我们想显示一个界面,可以直接通过WindowManager来进行操作。

WindowManager可以干什么

创建系统顶级窗口,实现悬浮窗口效果

WIndowManager具体操作

先看如何用WindowManager创建一个窗口:
Button bb = new Button(getApplicationContext());

//在Activity和Service中都可以直接使用这个方法来获得WindowManager。其getSystemService返回的是一个WindowManagerImpl对象,这是一个存在于本地进程中的一个对象。而事实是 WindowManagerImpl 继承了WindowManager,而WindowManger继承了ViewManager。
WindowManager wmManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

//LayoutParams里面存放着的是窗口的属性,通过这个变量,可以为窗口赋予各式的属性。也可以改变它的属性值,来进行各种各样的操作,像悬浮窗口的拖动,拉伸等操作。
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();

//类型: 应用程序窗口
wmParams.type = WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW ;

//不允许屏幕截图(即使使用手机助手的预览功能也不能看)
wmParams.flags = WindowManager.LayoutParams.FLAG_SECURE;

//设置坐标值,默认中心点是屏幕中央
wmParams.x=0;
wmParams.y=-200;

//宽度
wmParams.width = 400;

//高度
wmParams.height = 400;

//透明度,1.0表示不透明,0.0表示全透明
wmParams.alpha = 0.5f;

//创建View
wmManager.addView(bb, wmParams);

重点是addView(View view, ViewGroup.LayoutParams params);
第一个参数定义是窗口的界面,第二个参数是LayoutParams,定义窗口属性
LayoutParams属性介绍:
http://www.cnblogs.com/shitianzeng/articles/2814050.html
http://blog.csdn.net/chenyafei617/article/details/6577940

需要添加权限,注意Android6.0以上的系统对这个权限是动态控制的,如果不动态控制需要将应用的targetSdkVersion设置为6.0以下

WindowManager可以进行以下操作
(1)窗口添加
public void addView(View view, ViewGroup.LayoutParams params);
(2)窗口更新
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
(3)窗口删除
public void removeView(View view);
以上的三个方法都存在于ViewManager中。

其实系统的一些提示框也是WindowManager来实现的,比如说Toast,我们看一下它的源码:
public void handleShow(IBinder windowToken) {
  if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
  + " mNextView=" + mNextView);
  if (mView != mNextView) {
  // remove the old view if necessary
  handleHide();
  mView = mNextView;
  Context context = mView.getContext().getApplicationContext();
  String packageName = mView.getContext().getOpPackageName();
  if (context == null) {
  context = mView.getContext();
  }
  mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
  // We can resolve the Gravity here by using the Locale for getting
  // the layout direction
  final Configuration config = mView.getContext().getResources().getConfiguration();
  final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
  mParams.gravity = gravity;
  if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
  mParams.horizontalWeight = 1.0f;
  }
  if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
  mParams.verticalWeight = 1.0f;
  }
  mParams.x = mX;
  mParams.y = mY;
  mParams.verticalMargin = mVerticalMargin;
  mParams.horizontalMargin = mHorizontalMargin;
  mParams.packageName = packageName;
  mParams.hideTimeoutMilliseconds = mDuration ==
  Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
  mParams.token = windowToken;
  if (mView.getParent() != null) {
  if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
  mWM.removeView(mView);
  }
  if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
  mWM.addView(mView, mParams);
  trySendAccessibilityEvent();
  }
}

重点是  mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);和  mWM.addView(mView, mParams);这两句话,所以我们完全可以仿照源码写一个自己的Toast
Toast的属性
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
  | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

final Configuration config = mView.getContext().getResources().getConfiguration();
final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
  mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
  mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = mDuration ==
  Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
mParams.token = windowToken;

PopWindow的属性
private WindowManager.LayoutParams createPopupLayoutParams(IBinder token) {
  final WindowManager.LayoutParams p = new WindowManager.LayoutParams();

  // These gravity settings put the view at the top left corner of the
  // screen. The view is then positioned to the appropriate location by
  // setting the x and y offsets to match the anchor's bottom-left
  // corner.
  p.gravity = computeGravity();
  p.flags = computeFlags(p.flags);
  p.type = mWindowLayoutType;
  p.token = token;
  p.softInputMode = mSoftInputMode;
  p.windowAnimations = computeAnimationResource();

  if (mBackground != null) {
  p.format = mBackground.getOpacity();
  } else {
  p.format = PixelFormat.TRANSLUCENT;
  }

  if (mHeightMode < 0) {
  p.height = mLastHeight = mHeightMode;
  } else {
  p.height = mLastHeight = mHeight;
  }

  if (mWidthMode < 0) {
  p.width = mLastWidth = mWidthMode;
  } else {
  p.width = mLastWidth = mWidth;
  }

  p.privateFlags = PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH
  | PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;

  // Used for debugging.
  p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));

  return p;
}

参考文章:

decorView和window之间的层级及关系    

Android DecorView浅析      


你可能感兴趣的:(Android基础)