目标
本篇博客的目标主要有三个
- 不使用安卓原生控件,比如PopupWindow,Dialog等控件,实现我们的目标
- PopupWindow用于在Activity中全屏展示,模态,PopupWindow下面的全部内容不可操作,当Activity隐藏时,PopupWindow也随之隐藏
- SystemalertDialog用于在整个系统中悬浮展示,非模态,SystemalertDialog下面的内容仍然可以操作,当Activity隐藏时,SystemalertDialog并不隐藏,它可以在系统桌面之上显示
注意,桌面悬浮框是需要申请额外权限的,需要在清单里注册"SYSTEM_alert_WINDOW"权限,并在应用设置里开启"允许在应用上层显示"功能
另外,默认情况下,Window中的ContentView,是不具备处理按键事件的功能的,如果我们需要监听返回键时关闭Window,需要自己重写一个ContentView
实现代码
package com.android.architecture;
import android.view.KeyEvent;
public interface IKeyEventDispatcher {
boolean dispatchKeyEvent(KeyEvent event);
void setKeyEventListener(KeyEventListener listener);
interface KeyEventListener {
boolean dispatchKeyEvent(KeyEvent event);
}
}
package com.android.architecture;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.frameLayout;
public class KeyEventframeLayout extends frameLayout implements IKeyEventDispatcher {
KeyEventListener listener;
public KeyEventframeLayout(Context context) {
super(context);
}
public KeyEventframeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public KeyEventframeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setKeyEventListener(KeyEventListener listener) {
this.listener = listener;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (listener == null)
return super.dispatchKeyEvent(event);
return listener.dispatchKeyEvent(event);
}
}
//显示应用内弹窗
protected void showPopupWindow() {
WindowManager windowManager = StartActivity.this.getSystemService(WindowManager.class);
//设置window大小
WindowManager.LayoutParams lpContent = new WindowManager.LayoutParams();
lpContent.height = ViewGroup.LayoutParams.MATCH_PARENT;
lpContent.width = ViewGroup.LayoutParams.MATCH_PARENT;
lpContent.format = PixelFormat.RGBA_8888;
lpContent.gravity = Gravity.CENTER;
//设置window类型,这个同时也决定了window显示的优先级
lpContent.type = WindowManager.LayoutParams.TYPE_base_APPLICATION;
//设置窗体特性
lpContent.flags = 0;
lpContent.flags = lpContent.flags | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
lpContent.flags = lpContent.flags | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
//设置背景变暗程度
lpContent.dimAmount = 0.6F;
//创建ContentView
KeyEventframeLayout contentView = new KeyEventframeLayout(StartActivity.this);
//添加内容
TextView textView = new TextView(StartActivity.this);
textView.setText("Popup Window");
textView.setTextColor(Color.WHITE);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 30);
frameLayout.LayoutParams lpChild = new frameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lpChild.gravity = Gravity.CENTER;
textView.setLayoutParams(lpChild);
contentView.addView(textView);
//监听返回事件
contentView.setKeyEventListener(event -> {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
windowManager.removeView(contentView);
return true;
}
return false;
});
//监听点击事件
contentView.setOnClickListener(v -> {
windowManager.removeView(contentView);
});
//显示window
windowManager.addView(contentView, lpContent);
}
//显示系统悬浮框
//注意,这个操作需要注册"SYSTEM_alert_WINDOW"权限,并开启"允许在应用上层显示"功能
protected void showSystemalertDialog() {
WindowManager windowManager = StartActivity.this.getSystemService(WindowManager.class);
//设置window大小
WindowManager.LayoutParams lpContent = new WindowManager.LayoutParams();
lpContent.height = 400;
lpContent.width = 400;
lpContent.format = PixelFormat.RGBA_8888;
lpContent.gravity = Gravity.TOP | Gravity.RIGHT;
//设置window类型,这个同时也决定了window显示的优先级
lpContent.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
//设置窗体特性
lpContent.flags = 0;
lpContent.flags = lpContent.flags | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
//创建ContentView
//由于原生的frameLayout不能监听返回键,我们必须重写一个自己的frameLayout
KeyEventframeLayout contentView = new KeyEventframeLayout(StartActivity.this);
//添加内容
TextView textView = new TextView(StartActivity.this);
textView.setText("System alert Dialog");
textView.setTextColor(Color.WHITE);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 30);
frameLayout.LayoutParams lpChild = new frameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lpChild.gravity = Gravity.CENTER;
textView.setLayoutParams(lpChild);
contentView.addView(textView);
//监听返回事件
contentView.setKeyEventListener(event -> {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
windowManager.removeView(contentView);
return true;
}
return false;
});
//显示window
windowManager.addView(contentView, lpContent);
}
WindowManager中常见Flag的作用
//无法获得焦点,即无法接收到任何KeyEvent事件 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; //无法触摸,即无法接收到任何MotionEvent WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; //非模态,即不阻挡底部内容 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; //背景变暗 WindowManager.LayoutParams.FLAG_DIM_BEHIND; //全屏,即不显示状态栏和导航栏背景 WindowManager.LayoutParams.FLAG_FULLSCREEN; //绘制状态栏和导航栏,如果不绘制,可能会出现导航栏空白现象 WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;



