实现像九游游戏一样的悬浮窗、在按下HOME键的时候不会显示悬浮窗、并随着Activity的销毁而销毁,如果想在桌面也显示悬浮窗的话可以稍微的修改一下,如果不点击的话3秒后变淡
效果图
这个悬浮窗共有两种状态:
1、悬浮窗没有操作几秒钟后会隐藏一半的悬浮窗,点击之后显示完整悬浮窗。
2、悬浮窗在完整显示之后点击会显示菜单,点击屏幕其他地方则隐藏菜单。
接下来就看看怎么实现这个悬浮窗。
首先是创建一个悬浮窗:
/**
* 显示悬浮窗口 把登录成功的信息返回 创建悬浮窗服务
*/
void showFloatBtn(Activity activity){
mActivity = activity;
if (floatServiceIntent == null) {
floatServiceIntent = new Intent(activity,FloatService.class);
floatSConnection = new ServiceConnection() { //悬浮窗服务连服务连接
@Override
public void onServiceDisconnected(ComponentName name) {
if (mFloatBinder != null) {
mFloatBinder.removeFloat();
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mFloatBinder = (FloatService.FloatBinder) service;
mFloatBinder.createFloat(mActivity);
}
};
}
activity.getApplicationContext().bindService(floatServiceIntent, floatSConnection, Activity.BIND_AUTO_CREATE);
isCreateFloat = true; //创建了悬浮窗
}
在FloatPresnetImpl类的showFloat方法中,通过一个service来创建并管理悬浮窗。
在FloatService服务类当中的Binder来对悬浮窗做具体的创建,删除。
class FloatBinder extends Binder{
/**
* 创建float
*/
void createFloat(Activity activity){
mFloatView = new FloatView(activity);
}
/**
* 销毁float
*/
void removeFloat(){
if (mFloatView != null) {
mFloatView.removeAllWindow();
mFloatView = null;
}
}
/**
* 重新创建一个float
*/
public void reCreateFloat(Activity activity) {
mFloatView = new FloatView(activity);
}
}
接下来看主要的悬浮窗类都干了什么事情。
如何把悬浮窗添加到屏幕上面?
我们可以通过WindowManager把悬浮窗添加到屏幕上,通过WindowManager对悬浮窗进行操作,比如更新悬浮窗的位置。先对windowmanager还有WindowManager。LayoutParam参数进行设置:
private void initFloatWindowManage() {
wManager = mActivity.getWindowManager();
screenWidth = wManager.getDefaultDisplay().getWidth();
int screenHeigth = wManager.getDefaultDisplay().getHeight();
wmParams = new WindowManager.LayoutParams();
wmParams.format = PixelFormat.RGBA_8888;
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
wmParams.x = screenWidth;
wmParams.y = screenHeigth /2;
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
}
设置完WindowManager之后把通过WindowManager的addView函数把悬浮窗布局添加到屏幕上,完成这一步的话就可以在屏幕上看到悬浮窗啦。
private void initFloat() {
viewHeight = XYGameSDKUtil.dip2px(mActivity, viewHeight);//转化成px
mParentView = LayoutInflater.from(mActivity).inflate(R.layout.xygame_float_layout, null);
floatIV = (ImageView) mParentView.findViewById(R.id.xygame_float_iv);
floatDot = (TextView) mParentView.findViewById(R.id.xygame_float_dot_tv);
//判断状态栏是否显示 如果不显示则statusBarHeight为0
WindowManager.LayoutParams attrs = mActivity.getWindow().getAttributes();
if((attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN){
statusBarHeight = 0;
}else{
statusBarHeight = XYGameSDKUtil.getStatusBarHeight(mActivity);
}
wManager.addView(mParentView, wmParams);
}
光有悬浮窗,那怎么对悬浮窗进行操作呢?比如怎么对悬浮窗进行拖拽还有对点击事件的处理呢?那就要对悬浮窗设置TouchListener事件了。
touchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
floatEventDown(event);
break;
case MotionEvent.ACTION_MOVE:
floatEventMove(event);
break;
case MotionEvent.ACTION_UP:
floatEventUp();
break;
}
return true;
}
};
floatIV.setOnTouchListener(touchListener);
Touch事件有三种状态,分别是Down,Move,Up。在Down事件中首先要记录手指点击的位置,然后更改悬浮窗的按钮:
private void floatEventDown(MotionEvent event) {
xInView = event.getX();
yInView = event.getY();
xDownInScreen = event.getRawX();
yDownInScreen = event.getRawY() - statusBarHeight;
xInScreen = event.getRawX();
yInScreen = event.getRawY() - statusBarHeight;
floatIV.setBackgroundResource(R.mipmap.float_btn_show_icon);
mHideTimer.cancel();
}
在Move事件中,要对当前手指在屏幕上的坐标跟Down记录下的坐标进行计算,得出需要移动的x跟y的坐标,然后更新悬浮窗的位置:
private void floatEventMove(MotionEvent event) {
xInScreen = event.getRawX();
yInScreen = event.getRawY() - statusBarHeight;
if (xInScreen != xDownInScreen && yInScreen != yDownInScreen ) {
removeBigWindow();
}
wmParams.x = (int) (xInScreen - xInView);
wmParams.y = (int) (yInScreen - yInView);
updateViewPosition(); // 手指移动的时候更新小悬浮窗的位置
}
在up事件中要做的事情:
1、更新悬浮窗的状态
2、判断手指松开后是在屏幕的左半边还是右半边,然后贴壁处理。
3、开始一个计时器。用来记录悬浮窗在多长时间之后如果没有操作的话进行隐藏。
4、判断悬浮窗的touch事件是否是点击事件。
private void floatEventUp() {
if (xInScreen < screenWidth / 2) { //在左边
wmParams.x = 0;
mHintLocation = LEFT;
} else { //在右边
wmParams.x = screenWidth+100;
mHintLocation = RIGHT;
}
floatIV.setBackgroundResource(R.mipmap.float_btn_hide_icon);
mHideTimer.start();
updateViewPosition();
if (xInScreen == xDownInScreen && yInScreen == yDownInScreen) { //点击
openBigWindow();
}
}
完成这些的话还是不够的,需要在悬浮窗被点击之后弹出一个菜单出来,并且菜单弹出之后悬浮窗是不能隐藏的,必须要没有菜单的时候悬浮窗才能隐藏。当菜单隐藏之后要重新开启计时器来对悬浮窗进行隐藏。因为悬浮窗在没有拖动的时候是会显示在屏幕的最左边或者最右边的,那么在弹出去菜单的时候要对悬浮窗的位置进行判断,这样才能决定是显示哪一边的菜单。首先先看一下弹出悬浮窗的操作:
private void openMenu() {
initMenu();
if (mHintLocation == RIGHT ){
openRightMenuw();
}else openLeftMenu();
mHideTimer.cancel();
}
菜单其实就是一个PopupWindow控件。根据悬浮窗的位置来显示:
private void initMenu() {
if (mPopupWindow == null && floatIV != null) {
mPopupWindow = new PopupWindow(popMainView, LayoutParams.WRAP_CONTENT ,
viewHeight, true);
mPopupWindow.setFocusable(true);
mPopupWindow.setOutsideTouchable(true);
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
mPopupWindow.setOnDismissListener(new OnDismissListener() { //大悬浮窗的隐藏监听
@Override
public void onDismiss() { //隐藏时候的操作
mHideTimer.start();
}
});
}
}
菜单的布局如下:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/xygame_float_menu_main_ll" android:layout_width="wrap_content" android:layout_height="50dp" android:background="@drawable/xygame_float_menu_bg" android:gravity="center" android:minHeight="45.0dip" android:orientation="horizontal" > <ImageView android:id="@+id/xygame_float_menu_left_iv" android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/float_btn_show_icon" android:visibility="gone" /> <TextView android:id="@+id/xygame_float_menu_personal_tv" style="@style/xygame_float_item_style" android:layout_width="match_parent" android:layout_marginLeft="5.0dip" android:background="@drawable/xygame_float_menu_item_bg" android:drawableTop="@mipmap/float_menu_personal_icon" android:text="用户" /> <TextView android:id="@+id/xygame_float_menu_gifts_tv" style="@style/xygame_float_item_style" android:background="@drawable/xygame_float_menu_item_bg" android:drawableTop="@mipmap/float_menu_gift_icon" android:text="礼包" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" > <TextView android:id="@+id/xygame_float_menu_msg_tv" style="@style/xygame_float_item_style" android:background="@drawable/xygame_float_menu_item_bg" android:drawableTop="@mipmap/float_menu_notice_icon" android:text="消息" /> <TextView android:id="@+id/xygame_float_menu_dot_tv" android:visibility="gone" android:layout_width="18dp" android:layout_height="18dp" android:layout_marginLeft="20dp" android:background="@mipmap/float_dot_icon" /> </RelativeLayout> <TextView android:id="@+id/xygame_float_menu_help_tv" style="@style/xygame_float_item_style" android:background="@drawable/xygame_float_menu_item_bg" android:drawableTop="@mipmap/float_menu_help_icon" android:text="帮助" /> <ImageView android:id="@+id/xygame_float_menu_right_iv" android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/float_btn_show_icon" android:visibility="visible" /> </LinearLayout>
下面的是完整的悬浮窗类,这里是demo想玩玩的话也可以下载一下运行看看:import android.annotation.SuppressLint; import android.app.Activity; import android.graphics.PixelFormat; import android.graphics.drawable.BitmapDrawable; import android.os.CountDownTimer; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnTouchListener; import android.view.WindowManager; import android.view.animation.TranslateAnimation; import android.widget.ImageView; import android.widget.PopupWindow; import android.widget.PopupWindow.OnDismissListener; import android.widget.TextView; import android.widget.LinearLayout.LayoutParams; class FloatView implements IFloatUI, OnClickListener{ /** * 记录系统状态栏的高度 */ private static int statusBarHeight; /** * 记录当前手指位置在屏幕上的横坐标值 */ private float xInScreen; /** * 记录当前手指位置在屏幕上的纵坐标值 */ private float yInScreen; /** * 记录手指按下时在屏幕上的横坐标的值 */ private float xDownInScreen; /** * 记录手指按下时在屏幕上的纵坐标的值 */ private float yDownInScreen; /** * 记录手指按下时在小悬浮窗的View上的横坐标的值 */ private float xInView; /** * 记录手指按下时在小悬浮窗的View上的纵坐标的值 */ private float yInView; private static WindowManager wManager; private Activity mActivity; private WindowManager.LayoutParams wmParams; /**悬浮窗*/ private ImageView floatIV; /**悬浮窗提示的小点*/ private TextView floatDot; /**消息提示的小点*/ private TextView notiftyDot; private int screenWidth; /**float menu*/ private PopupWindow mPopupWindow; /**放置按钮的View*/ private CountDownTimer mHideTimer; /**悬浮窗的父控件*/ private View mParentView; private OnTouchListener touchListener; private int LEFT = 0; private int RIGHT = 1; /**悬浮窗要隐藏的位置*/ private int mHintLocation = LEFT; /**右边的图标*/ private ImageView rightIconIV; /**左边的图标*/ private ImageView leftIconIV; /**悬浮窗隐藏的距离*/ private int length; /**悬浮窗菜单栏的父控件*/ private View popMainView; /**小红点的状态*/ private boolean dotStatus; /**float menu的高度*/ private int viewHeight = 50; FloatView(Activity activity) { this.mActivity = activity; initView(); } private void initView() { initFloatWindowManage(); initFloat(); floatBtnEvent(); initTimer(); initFloatLaout(); } @SuppressWarnings("deprecation") private void initFloatWindowManage() { wManager = mActivity.getWindowManager(); screenWidth = wManager.getDefaultDisplay().getWidth(); int screenHeigth = wManager.getDefaultDisplay().getHeight(); wmParams = new WindowManager.LayoutParams(); wmParams.format = PixelFormat.RGBA_8888; wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; wmParams.gravity = Gravity.LEFT | Gravity.TOP; wmParams.x = screenWidth; wmParams.y = screenHeigth /2; wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; } private void initFloat() { viewHeight = XYGameSDKUtil.dip2px(mActivity, viewHeight);//转化成px mParentView = LayoutInflater.from(mActivity).inflate(R.layout.xygame_float_layout, null); floatIV = (ImageView) mParentView.findViewById(R.id.xygame_float_iv); floatDot = (TextView) mParentView.findViewById(R.id.xygame_float_dot_tv); //判断状态栏是否显示 如果不显示则statusBarHeight为0 WindowManager.LayoutParams attrs = mActivity.getWindow().getAttributes(); if((attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN){ statusBarHeight = 0; }else{ statusBarHeight = XYGameSDKUtil.getStatusBarHeight(mActivity); } wManager.addView(mParentView, wmParams); } private void initTimer() { length = viewHeight/2; //隐藏一半 mHideTimer = new CountDownTimer(5000,5000) { //悬浮窗超过5秒没有操作的话会自动隐藏 @Override public void onTick(long millisUntilFinished) {} @Override public void onFinish() { //隐藏 if (!dotStatus) { //如果有显示小红点的话则不隐藏 if (mHintLocation == LEFT) { TranslateAnimation translateAnimation = new TranslateAnimation(-length,-length,0,0); translateAnimation.setDuration(10000); translateAnimation.setFillAfter(true); floatIV.startAnimation(translateAnimation); floatIV.setOnTouchListener(null);//隐藏touch事件 wManager.updateViewLayout(mParentView, wmParams); }else { TranslateAnimation translateAnimation = new TranslateAnimation(length,length,0,0); translateAnimation.setDuration(10000); translateAnimation.setFillAfter(true); floatIV.startAnimation(translateAnimation); floatIV.setOnTouchListener(null);//隐藏touch事件 wManager.updateViewLayout(mParentView, wmParams); } } } }; } /**悬浮窗的点击事件*/ @SuppressLint("ClickableViewAccessibility") private void floatBtnEvent() { touchListener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: floatEventDown(event); break; case MotionEvent.ACTION_MOVE: floatEventMove(event); break; case MotionEvent.ACTION_UP: floatEventUp(); break; } return true; } }; floatIV.setOnTouchListener(touchListener); floatIV.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mHintLocation == LEFT) { TranslateAnimation translateAnimation = new TranslateAnimation(0,0,0,0); translateAnimation.setDuration(10000); translateAnimation.setFillAfter(true); floatIV.startAnimation(translateAnimation); floatIV.setOnTouchListener(touchListener);//隐藏touch事件 }else { TranslateAnimation translateAnimation = new TranslateAnimation(0,0,0,0); translateAnimation.setDuration(10000); translateAnimation.setFillAfter(true); floatIV.startAnimation(translateAnimation); floatIV.setOnTouchListener(touchListener);//隐藏touch事件 } wManager.updateViewLayout(mParentView, wmParams); floatIV.setOnTouchListener(touchListener);//隐藏touch事件 } }); } private void floatEventDown(MotionEvent event) { xInView = event.getX(); yInView = event.getY(); xDownInScreen = event.getRawX(); yDownInScreen = event.getRawY() - statusBarHeight; xInScreen = event.getRawX(); yInScreen = event.getRawY() - statusBarHeight; floatIV.setBackgroundResource(R.mipmap.float_btn_show_icon); mHideTimer.cancel(); } private void floatEventMove(MotionEvent event) { xInScreen = event.getRawX(); yInScreen = event.getRawY() - statusBarHeight; if (xInScreen != xDownInScreen && yInScreen != yDownInScreen ) { removeBigWindow(); } wmParams.x = (int) (xInScreen - xInView); wmParams.y = (int) (yInScreen - yInView); updateViewPosition(); // 手指移动的时候更新小悬浮窗的位置 } private void floatEventUp() { if (xInScreen < screenWidth / 2) { //在左边 wmParams.x = 0; mHintLocation = LEFT; } else { //在右边 wmParams.x = screenWidth+100; mHintLocation = RIGHT; } floatIV.setBackgroundResource(R.mipmap.float_btn_hide_icon); mHideTimer.start(); updateViewPosition(); if (xInScreen == xDownInScreen && yInScreen == yDownInScreen) { //点击 openMenu(); } } /**初始化float menu*/ private void initFloatLaout() { popMainView = LayoutInflater.from(mActivity).inflate(R.layout.xygame_float_menu_layout, null); notiftyDot = (TextView) popMainView.findViewById(R.id.xygame_float_menu_dot_tv); rightIconIV = (ImageView) popMainView.findViewById(R.id.xygame_float_menu_right_iv); leftIconIV = (ImageView) popMainView.findViewById(R.id.xygame_float_menu_left_iv); /*个人中心*/ TextView personalCenterTV = (TextView) popMainView.findViewById(R.id.xygame_float_menu_personal_tv); /*礼包*/ TextView giftTV = (TextView) popMainView.findViewById(R.id.xygame_float_menu_gifts_tv); /*消息*/ TextView noticeTV = (TextView) popMainView.findViewById(R.id.xygame_float_menu_msg_tv); /*帮助*/ TextView helpTV = (TextView) popMainView.findViewById(R.id.xygame_float_menu_help_tv); rightIconIV.setOnClickListener(this); leftIconIV.setOnClickListener(this); personalCenterTV.setOnClickListener(this); giftTV.setOnClickListener(this); noticeTV.setOnClickListener(this); helpTV.setOnClickListener(this); } @Override public void onClick(View v) { } /** * 打开大悬浮窗 */ private void openMenu() { initMenu(); if (mHintLocation == RIGHT ){ openRightMenuw(); }else openLeftMenu(); mHideTimer.cancel(); } /**初始化大窗口*/ @SuppressWarnings("deprecation") private void initMenu() { if (mPopupWindow == null && floatIV != null) { mPopupWindow = new PopupWindow(popMainView, LayoutParams.WRAP_CONTENT , viewHeight, true); mPopupWindow.setFocusable(true); mPopupWindow.setOutsideTouchable(true); mPopupWindow.setBackgroundDrawable(new BitmapDrawable()); mPopupWindow.setOnDismissListener(new OnDismissListener() { //大悬浮窗的隐藏监听 @Override public void onDismiss() { //隐藏时候的操作 mHideTimer.start(); } }); } } /**显示左大悬浮窗*/ private void openLeftMenu(){ mPopupWindow.showAtLocation(floatIV, Gravity.NO_GRAVITY, 0, 0); rightIconIV.setVisibility(View.GONE); leftIconIV.setVisibility(View.VISIBLE); } /**显示右大悬浮窗*/ private void openRightMenuw(){ mPopupWindow.showAtLocation(floatIV, Gravity.NO_GRAVITY, 0, 0); rightIconIV.setVisibility(View.VISIBLE); leftIconIV.setVisibility(View.GONE); } /** * 更新小悬浮窗在屏幕中的位置。 */ private void updateViewPosition() { wManager.updateViewLayout(mParentView, wmParams); } /** * 移除大悬浮窗 */ private void removeBigWindow() { if (mPopupWindow != null) { mPopupWindow.dismiss(); mPopupWindow = null; } } /** * 移除所有悬浮窗 */ public void removeAllWindow() { try { mHideTimer.cancel(); removeBigWindow(); mPopupWindow = null; // mParentView.removeAllViews(); wManager.removeViewImmediate(mParentView); } catch (Exception e) { } } @Override public void destoryFloat() { removeAllWindow(); } /** * 设置小红点的状态 * @param isShow 是否有新消息 */ public void setDotStatus(boolean isShow){ invalidataDot(isShow); } /** * 刷新红点状态 * @param isShow 是否显示 */ private void invalidataDot(boolean isShow){ if (isShow) { floatDot.setVisibility(View.VISIBLE); notiftyDot.setVisibility(View.VISIBLE); }else { floatDot.setVisibility(View.GONE); notiftyDot.setVisibility(View.GONE); } dotStatus = isShow; } }