本篇文章主要介绍了"android控件大全 Android 应用内悬浮控件实践总结",主要涉及到android控件大全方面的内容,对于Android开发感兴趣的同学可以参考一下:
在工作中遇到一个需求,需要在整个应用的上层悬浮显示控件,目标效果如下图:首先想到的是申请悬浮窗权限,OK~ 打开搜索引擎,映入眼帘的并不是如何申请,而是“And...
OK~ 放弃共享元素方案, 真的绕不过申请权限了吗? 再考虑一下 TYPE_TOAST 方案, 为什么它失效了呢? 应该是系统对此类型的控件加了限制, 对待 TYPE_TOAST 不再跳过检查权限步骤, 而是像 TYPE_PHONE 之类一视同仁, 那为什么我们的 toast 却可以跳过呢? toast 不就是 TYPE_TOAST 类型的视图吗? 不管如何, 反正 toast 是不需要权限的, 那就尝试从 toast 入手. OK~ ,现在的关键词是 自定义 toast .
查看 Toast 类源码, 有一个方法眼前一亮:
/**
* Set the view to show.
* @see #getView
*/
public void setView(View view) {
mNextView = view;
}
Toast 是可以自定义视图的, 这为自定义 toast 提供了可能性, 但是显示时长只能设置为 LENGTH_SHORT 或 LENGTH_LONG ,我们需要的是无限时长, 没有方法实现, 除非反射之类的怪招了~ 嗯~ 下面奉上通过反射实现无限时长 toast 的完整代码 :
/**
* 自定义 toast , 无限时长
* 可设置显示位置 尺寸
*/
class AlwaysShowToast {
private Toast toast;
private Object mTN;
private Method show;
private Method hide;
private int mWidth = WindowManager.LayoutParams.WRAP_CONTENT;
private int mHeight = WindowManager.LayoutParams.WRAP_CONTENT;
public FixedFloatToast(Context applicationContext) {
toast = new Toast(applicationContext);
}
public void setView(View view, int width, int height) {
mWidth = width;
mHeight = height;
setView(view);
}
public void setView(View view) {
toast.setView(view);
initTN();
}
public void setGravity(int gravity, int xOffset, int yOffset) {
toast.setGravity(gravity, xOffset, yOffset);
}
public void show() {
try {
show.invoke(mTN);
} catch (Exception e) {
e.printStackTrace();
}
}
public void hide() {
try {
hide.invoke(mTN);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 利用反射设置 toast 参数
*/
private void initTN() {
try {
Field tnField = toast.getClass().getDeclaredField("mTN");
tnField.setAccessible(true);
mTN = tnField.get(toast);
show = mTN.getClass().getMethod("show");
hide = mTN.getClass().getMethod("hide");
Field tnParamsField = mTN.getClass().getDeclaredField("mParams");
tnParamsField.setAccessible(true);
WindowManager.LayoutParams params = (WindowManager.LayoutParams) tnParamsField.get(mTN);
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = mWidth;
params.height = mHeight;
Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView");
tnNextViewField.setAccessible(true);
tnNextViewField.set(mTN, toast.getView());
} catch (Exception e) {
e.printStackTrace();
}
}
}
有了这个自定义 toast , 跳过权限显示悬浮窗就非常容易了, 理论上可以兼容任意版本,任意机型, 因为这只是一个普通的 toast , 系统没理由不允许一个 toast 显示的~ 然而… 亲测在 Nexus7.1.1 及以上不显示 , 在 Android 4.4 以下无法接受触摸事件, 在小米部分机型上无法改变位置.
OK~ 对比一下这些方案 :
方案1: 申请权限
优点:实现简单,只要正确引导用户打开权限即可
缺点:部分机型默认禁用; 需权限不友好
方案2: 每个界面添加,共享元素过渡
优点:不需权限
缺点:较复杂,只适用于5.0以上,且悬浮控件不可隐藏(共享元素会闪显控件)
方案3: TYPE_TOAST
优点:实现简单
缺点:小米(MIUI8)、7.1.1需要权限,4.4以下无法接受点击事件
方案4:自定义 toast
优点:大部分机型不需权限,实现简单
缺点:Nexus7.1.1及以上不显示,4.4以下无法接受点击事件,小米(MIUI8)及部分机型不可改变位置
结合我的需求, 我的悬浮控件并不需要改变位置, 所以最终选择方案为: