Window

  • Window表示窗口,比如可以实现桌面或者锁屏上的类似悬浮窗的效果,Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,实际上他们都是附加在Window上的,WindowManager提供了对这些Window的统一管理功能

Window与WindowManager的联系

  • 为了分析Window的工作机制,需要先了解如何使用WindowManager来添加一个Window
WindowManager manager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(width, height, type, flags, format);
manager.addView(btn, layoutParams);
  • WindowManager.LayoutParams的属性中type和flags参数比较重要
  • flags的常用选项:

flag选项

意义

FLAG_NOT_FOCUSABLE = 0x00000008

表示此Window不需要获取焦点,不接受各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层具有焦点的Window

FLAG_NOT_TOUCH_MODAL = 0x00000020

自己Window区域内的事件自己处理;自己Window区域外的事件传递给底层Window处理,一般这个选项会默认开启,否则其他Window无法接受事件

FLAG_SHOW_WHEN_LOCKED

让Window无法收到事件

  • Type参数是int类型的,表示Window的类型,Window有三种类型:应用Window、子Window和系统Window
  • 应用Window对应着一个Activity,子Window不能独立存在(要依附在特定的父Window中),系统Window需要权限才能进行创建(Toast和系统状态栏)

Window层级:

  • 应用Window的层级范围:1~99
  • 子Window的层级范围:1000~1999
  • 系统Window的层级范围:2000~2999
  • 注意:
  • 层级大的Window会覆盖在层级小的Window上
  • 使用系统级Window,需要在AndroidMenifest.xml中配置

WindowManager的功能:

  • 常用功能(定义在ViewManager中):
public interface WindowManager ectends ViewManager
public interface ViewMananger{
//添加View
public void addView(View view, ViewGroup.LayoutParams params);
//更新View
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
//删除View
public void remoteView(View view);
}

eg:桌面悬浮窗:

public class WindowDemoActivity extends AppCompatActivity implements View.OnTouchListener {


private Button add_btn;
private Button rem_btn;
private CircleImageView imageView;
private WindowManager.LayoutParams layoutParams;
private WindowManager windowManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_window_demo);


if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivityForResult(intent, 100);
}
add_btn = (Button)findViewById(R.id.add_btn);
rem_btn = (Button)findViewById(R.id.rem_btn);
windowManager = (WindowManager)getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
add_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
imageView = new CircleImageView(getApplicationContext());
imageView.setImageResource(R.drawable.androidfirst);
layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, 2099, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED, PixelFormat.TRANSPARENT
);
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
layoutParams.x = 0;
layoutParams.y = 300;
imageView.setOnTouchListener(WindowDemoActivity.this);
windowManager.addView(imageView, layoutParams);
}
});
rem_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(imageView != null) {
windowManager.removeView(imageView);
}
}
});
}




@Override
public boolean onTouch(View v, MotionEvent event) {


int rawX = (int) event.getRawX();
int rawY = (int) event.getRawY();
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:{
layoutParams.x = rawX;
layoutParams.y = rawY;
windowManager.updateViewLayout(imageView, layoutParams);
break;
}
default:
break;
}
return false;
}
}