最近工(xian)作(de)需(dan)要(teng),封装了一个提示型的悬浮窗工具类,简化了悬浮窗的创建显示和隐藏等步骤,并预定义了上中下三种简单的进场退场动画,拓展了悬浮窗自定义消失时间的功能。
使用方法很简单:
1.实例化悬浮窗工具类FloatView
FloatView floatView = new FloatView(context);
2.传入需要显示在悬浮窗上的view,可以是xml也可以是自定义view
//inflate xml类型view
LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = inflate.inflate(R.layout.my_view, null);
floatView.setView(mView);
//或自定义类型view
VolumeBar volumeBar = new TVolumeBar(context);
floatView.setView(volumeBar);
3.可自定义悬浮窗的宽高, 默认是wrap_content
//可自定义宽高,不设置则默认均为wrap_content
//单位是px,建议使用资源文件定义宽高,使用resources.getDimensionPixelSize()获取,方便做不同分辨率适配
floatView.setWidth(250);
floatView.setHeight(40);
4.可自定义显示时间,工具类中预定义了三种方案,分别是短时间(2秒)显示,长时间(4秒)显示,一直显示(直至主动隐藏悬浮窗),也可以自定义显示时间(单位秒)
//短时间显示
floatView.setDuration(FloatView.LENGTH_SHORT);
//长时间显示
floatView.setDuration(FloatView.LENGTH_LONG);
//一直显示
floatView.setDuration(FloatView.LENGTH_ALWAYS);
//自定义显示时间,单位秒,10秒后消失
floatView.setDuration(10);
5.自定义布局位置,工具类预定义了3种布局方案
{@link #TOP} 置顶并水平铺满
{@link #CENTER} 居中自适应
{@link #BOTTOM} 置底并水平铺满
预定义方案可以通过 defaultAnim 决定是否使用默认动画
不使用默认动画可以通过 {@link #setWindowAnimations} 自定义
除了三种预定义方案,还可传入 Gravity 自定义参数进行布局
//使用预定义TOP布局,水平铺满并自适应高度
//使用预定义动画,自上而下淡入,自下而上淡出
floatView.setGravity(FloatView.TOP, true);
//使用预定义CENTER布局,宽度高度均自适应
//不实用预定义动画
floatView.setGravity(FloatView.CENTER, false);
//自定义Gravity,使悬浮窗布局在屏幕右侧
floatView.setGravity(Gravity.RIGHT, false);
6.可自定义进出场动画
<style name="my_anim">
<item name="@android:windowEnterAnimation">@anim/my_anim_in</item>
<item name="@android:windowExitAnimation">@anim/my_anim_out</item>
</style>
my_anim_in.xml
与my_anim_out.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:fromYDelta="-120%"
android:toXDelta="0%"
android:toYDelta="0%"
android:fillAfter="true"
android:duration="1000"
/>
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="-120%"
android:fillAfter="true"
android:duration="1000"
/>
</set>
自定义进出场动画详细写法请自行查找资料.
//参数传入R.style
floatView.setWindowAnimations(R.style.my_anim);
7.最后调用show()方法显示悬浮窗, 悬浮窗已显示的时候更新view的内容可以立即刷新view。已显示的时候再次调用show()方法会刷新悬浮窗的消失时间重新计时。
//调用show()前必须调用过setView(),否则不显示悬浮窗
floatView.show();
//在floatView显示时对显示的view进行内容修改可立刻刷新悬浮窗显示的内容
myView.setText("内容更新");
//再次调用show()方法可以刷新消失时间,重新倒计时
floatView.show();
//调用hide()播放退场动画并隐藏悬浮窗
floatView.hide();
//调用hideImmediate()立即隐藏悬浮窗,适合在悬浮窗父容器退出时调用,防止内存泄漏
floatView. hideImmediate();
8.在悬浮窗隐藏的时候会触发OnHideListener,可以在悬浮窗隐藏时做适当的处理
floatView.setOnHideListener(new FloatView.OnHideListener() {
@Override
public void onHide() {
Log.e(TAG, "onHide");
}
});
FloatView工具类源码
public class FloatView {
public static final int TOP = 0x9731;
public static final int CENTER = 0x9732;
public static final int BOTTOM = 0x9733;
public static final int LENGTH_ALWAYS = 0;
public static final int LENGTH_SHORT = 2;
public static final int LENGTH_LONG = 4;
protected Context mContext;
protected WindowManager.LayoutParams params;
protected WindowManager mWM;
protected View mView;
protected Handler mHandler;
protected int mDuration = LENGTH_SHORT;
public FloatView(Context context){
this.mContext = context;
mHandler = new Handler();
params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
params.setTitle("FloatView");
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
}
/**
* 为悬浮窗设置View
* @param view 自定义view
*/
public void setView(View view) {
if(mView != null && mView.getParent() != null) {
mWM.removeView(mView);
}
mView = view;
}
/**
* 设置悬浮窗宽度
* @param width 宽度
*/
public void setWidth(int width) {
params.width = width;
}
/**
* 设置悬浮窗高度
* @param height 高度
*/
public void setHeight(int height) {
params.height = height;
}
/**
* 自定义透明度,0.0f全透明,1.0f全不透明
* @param alpha 透明度
*/
public void setAlpha(float alpha) {
params.alpha = alpha;
}
/**
* 自定义窗口类型,默认 {@link WindowManager.LayoutParams#TYPE_SYSTEM_OVERLAY}
* @param type 窗口类型
*/
public void setType(int type) {
params.type = type;
}
/**
* 自定义窗口进出场动画
* @param windowAnimations 动画资源res
*/
public void setWindowAnimations(int windowAnimations) {
params.windowAnimations = windowAnimations;
}
/**
* 设置Gravity,已预定义三种Gravity分别是
* {@link #TOP} 置顶并水平铺满
* {@link #CENTER} 居中自适应
* {@link #BOTTOM} 置底并水平铺满
* 预定义方案可以通过 defaultAnim 决定是否使用默认动画
* 不使用默认动画可以通过 {@link #setWindowAnimations} 自定义
* 除了三种预定义方案,还可传入 Gravity 自定义参数进行布局
* @param gravity 预定义或自定义Gravity
* @param defaultAnim 是否使用默认动画
*/
public void setGravity(int gravity, boolean defaultAnim) {
switch (gravity) {
case TOP:
params.gravity = Gravity.FILL_HORIZONTAL | Gravity.TOP;
if (defaultAnim) {
params.windowAnimations = R.style.top_anim_view;
}
break;
case CENTER:
params.gravity = Gravity.CENTER;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
if (defaultAnim) {
params.windowAnimations = R.style.center_anim_view;
}
break;
case BOTTOM:
params.gravity = Gravity.FILL_HORIZONTAL | Gravity.BOTTOM;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
if (defaultAnim) {
params.windowAnimations = R.style.bottom_anim_view;
}
break;
default:
params.gravity = gravity;
}
}
private Runnable hideRunnable = new Runnable() {
@Override
public void run() {
hide();
}
};
/**
* 显示悬浮窗,如果mView还未初始化则不做处理
* 如果mView已经显示,则只刷新显示时间,未显示添加到mWM
*/
public void show() {
if(mView == null) {
return;
}
if (mView.getParent() == null) {
mWM.addView(mView, params);
}
refreshHideTime();
}
/**
* 隐藏悬浮窗,并触发onHide回调
* 该隐藏方法会触发window退场动画
* 如果mView的容器退出,建议调用{@link #hideImmediate()}
* 否则有可能会导致 Activity 出现内存泄漏
*/
public void hide(){
if (mView != null && mView.getParent() != null) {
mWM.removeView(mView);
if (onHideListener != null) {
onHideListener.onHide();
}
}
}
/**
* 立刻隐藏悬浮窗,触发onHide回调
* 不会触发window退场动画
*/
public void hideImmediate(){
if (mView != null && mView.getParent() != null) {
params.windowAnimations = 0;
mWM.updateViewLayout(mView, params);
mWM.removeView(mView);
if (onHideListener != null) {
onHideListener.onHide();
}
}
}
/**
* 设置悬浮窗持续显示时间,预定义三种方案
* {@link #LENGTH_SHORT} 2秒后自动消失
* {@link #LENGTH_LONG} 4秒后自动消失
* {@link #LENGTH_ALWAYS} 持续显示悬浮窗,直至调用 hide 或 hideImmediate
* 除了三种预定义方案,也可自定义显示时间,传入的参数值代表n秒后自动消失
* @param duration 持续时间
*/
public void setDuration(int duration) {
mDuration = duration;
}
/**
* 返回悬浮窗持续显示时间
* @return 持续时间
*/
public int getDuration() {
return mDuration;
}
// 刷新悬浮窗的消失时间
private void refreshHideTime() {
//判断duration,如果大于#LENGTH_ALWAYS 则设置消失时间
if (mDuration > LENGTH_ALWAYS) {
mHandler.removeCallbacks(hideRunnable);
mHandler.postDelayed(hideRunnable, mDuration * 1000);
}
}
private OnHideListener onHideListener;
public void setOnHideListener(OnHideListener onHideListener) {
this.onHideListener = onHideListener;
}
public interface OnHideListener {
void onHide();
}
}