需求:用户触发了一定的条件会弹出一个悬浮框,用户在关闭APP或把APP退到后台时,点击悬浮框可以打开应用;
很简单的一个悬浮框很快就创建出来了,退出APP后点击悬浮框可以通过包名启动APP,但是把APP退到后台后怎么都启动不了,原因是:从后台启动 Activity 的限制 ,于是有了骚操作:从ActivityManager中根据本应用正运行的Task唤醒到前台(不是每次都能成功) , 然后就有了下面对悬浮框和画中画的研究;
创建一个简单的悬浮框:
申请悬浮框权限:
if (Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(this)) {
new AlertDialog().bulid(this)
.setTitle("提示")
.setMsg("请打开悬浮框权限或\n可在其它应用上层显示")
.setBtnR("确定", new OnDialogButtonClickListener() {
@Override
public void onClick(@NotNull BaseDialog dialog, @NotNull View v) {
startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()))
, ANDROID_ACTION_MANAGE_OVERLAY_PERMISSION);
dialog.dismiss();
}
}).setBtnL("取消",null)
.show();
return;
}
创建悬浮框,添加到窗口
ImageView imageView = new ImageView(this);
imageView.setImageResource(R.mipmap.ic_launcher);
WindowManager mWindowManager;
WindowManager.LayoutParams mParams;
mWindowManager = getWindowManager();
mParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}else {
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// 效果为背景透明
mParams.format = PixelFormat.TRANSLUCENT;
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mParams.gravity = Gravity.START | Gravity.TOP; // 调整悬浮窗口至右下角
// 设置悬浮窗口长宽数据
int width = dp2px(this, 150);
mParams.width = width;
mParams.height = width * 9 / 16;
mWindowManager.addView(imageView,mParams);
这样悬浮框就可以显示出来了,(结尾有悬浮框工具类,可拖拽的悬浮框)
Android8.0 画中画:
https://developer.android.google.cn/guide/topics/ui/picture-in-picture.html#handling_ui
Android 8.0(API 级别 26)允许以画中画模式启动 Activity。画中画是一种特殊类型的多窗口模式,最常用于视频播放。使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容。
1、清单中注册 Activity , 将 android:supportsPictureInPicture
和 android:resizeableActivity
设置为 true
,
<activity android:name=".MainActivity"
android:excludeFromRecents="true"
android:supportsPictureInPicture="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
</activity>
2、主动调用 enterPictureInPictureMode 方法即可启动画中画功能:
private final PictureInPictureParams.Builder mPictureInPictureParamsBuilder =
new PictureInPictureParams.Builder();
@Override
public void onBackPressed() {
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)){
Rational aspectRatio = new Rational(16, 9);
mPictureInPictureParamsBuilder.setAspectRatio(aspectRatio).build();
enterPictureInPictureMode(mPictureInPictureParamsBuilder.build());
}else {
super .onBackPressed();
}
}
从上面可以了解到,画中画主要时针对于视频播放的,代替了分屏功能,对本需求并不是很适用;
悬浮框工具类:
public class FloatView extends FrameLayout{
private WindowManager mWindowManager;
private WindowManager.LayoutParams mParams;
private int mDownRawX, mDownRawY;//手指按下时相对于屏幕的坐标
private int mDownX, mDownY;//手指按下时相对于悬浮窗的坐标
public FloatView(@NonNull Context context, int x, int y) {
super(context);
mDownX = x;
mDownY = y;
init();
}
private void init() {
// setBackgroundResource(R.color.dark);
int padding = dp2px(App.getInstance() , 2);
setPadding(padding, padding, padding, padding);
initWindow();
}
private void initWindow() {
mWindowManager = getWindowManager(getContext().getApplicationContext());
mParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}else {
mParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// 设置图片格式,效果为背景透明
mParams.format = PixelFormat.TRANSLUCENT;
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// mParams.windowAnimations = R.style.FloatWindowAnimation;
mParams.gravity = Gravity.START | Gravity.TOP; // 调整悬浮窗口至右下角
// 设置悬浮窗口长宽数据
int width = dp2px(getContext(), 150);
mParams.width = width;
mParams.height = width * 9 / 16;
// mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
// mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mParams.x = mDownX;
mParams.y = mDownY;
}
/**
* 添加至窗口
*/
public boolean addToWindow() {
if (mWindowManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (!isAttachedToWindow()) {
mWindowManager.addView(this, mParams);
return true;
} else {
return false;
}
} else {
try {
if (getParent() == null) {
mWindowManager.addView(this, mParams);
}
return true;
} catch (Exception e) {
return false;
}
}
} else {
return false;
}
}
/**
* 从窗口移除
*/
public boolean removeFromWindow() {
if (mWindowManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isAttachedToWindow()) {
mWindowManager.removeViewImmediate(this);
return true;
} else {
return false;
}
} else {
try {
if (getParent() != null) {
mWindowManager.removeViewImmediate(this);
}
return true;
} catch (Exception e) {
return false;
}
}
} else {
return false;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercepted = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercepted = false;
mDownRawX = (int) ev.getRawX();
mDownRawY = (int) ev.getRawY();
mDownX = (int) ev.getX();
mDownY = (int) (ev.getY() + getStatusBarHeight(getContext()));
break;
case MotionEvent.ACTION_MOVE:
float absDeltaX = Math.abs(ev.getRawX() - mDownRawX);
float absDeltaY = Math.abs(ev.getRawY() - mDownRawY);
intercepted = absDeltaX > ViewConfiguration.get(getContext()).getScaledTouchSlop() ||
absDeltaY > ViewConfiguration.get(getContext()).getScaledTouchSlop();
break;
}
return intercepted;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
int x = (int) event.getRawX();
int y = (int) event.getRawY();
mParams.x = x - mDownX;
mParams.y = y - mDownY;
mWindowManager.updateViewLayout(this, mParams);
break;
}
return super.onTouchEvent(event);
}
/**
* 如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。
*/
public WindowManager getWindowManager(Context context) {
return (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
/**
* dp转为px
*/
public int dp2px(Context context, float dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
}
/**
* 获取状态栏高度
*/
public double getStatusBarHeight(Context context) {
int statusBarHeight = 0;
//获取status_bar_height资源的ID
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
//根据资源ID获取响应的尺寸值
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
}
FloatView floatView = new FloatView(App.getInstance(), 0, 0);
ImageView imageView = new ImageView(this);
imageView.setImageResource(R.mipmap.ic_launcher);
floatView.addView(imageView);
floatView.addToWindow();
悬浮框工具类抽取的DK播放器,感谢;