一、游戏内显示悬浮球

手游SDK的悬浮球和一般的悬浮窗有点不一样,它只需要在游戏内显示即可,不需要也不能在桌面中显示出来。
所以如果使用WindowManager创建悬浮窗,需要监听App是否在前台,如果在,则显示。如果不在则隐藏悬浮窗。
而App不在前台了,可能有三种情况:1、正常退出。2、home键回到主界面了。3、点击任务键切换到别的程序。
正常退出的情况,那只要正常销毁悬浮窗就好。
home键和多任务键的情况,需要对按键进行监听。然后判断App是否在前台,如果不是,则是隐藏/销毁悬浮窗。

home键和多任务键的监听,示例代码:

InnerRecevier innerReceiver = new InnerRecevier();        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);        registerReceiver(innerReceiver, intentFilter);    class InnerRecevier extends BroadcastReceiver {        final String SYSTEM_DIALOG_REASON_KEY = "reason";        final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";        final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {                String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);                if (reason != null) {                    if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {                        Toast.makeText(MainActivity.this, "Home键被监听", Toast.LENGTH_SHORT).show();                    } else if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {                        Toast.makeText(MainActivity.this, "多任务键被监听", Toast.LENGTH_SHORT).show();                    }                }            }        }    }

判断当前App是否在前台,示例代码:

public static boolean isAppOnForeground(Context context) {
        ActivityManager activityManager = (ActivityManager) context.getApplicationContext()
                .getSystemService(Context.ACTIVITY_SERVICE);
        String packageName = context.getApplicationContext().getPackageName();
        //获取Android设备中所有正在运行的App
        List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
        if (appProcesses == null)
            return false;
        for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
            if (appProcess.processName.equals(packageName)
                    && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                return true;
            }
        }
        return false;
    }

但要使用WindowManager还需要用户手动打开悬浮窗权限。
弹出索要权限提示框,然后待用户点击确定后跳转到权限设置界面,寻找悬浮窗权限,并打开。这一系列操作对用户不太友好,因为操作比较过于繁琐。

在SDK中创建悬浮球还可以使用ViewGroup。
ViewGroup最大优势是不需要权限申请。使用ViewGroup作为悬浮球适用于游戏或者单Activity APP。
示例代码:

ViewGroup view = (ViewGroup )AppActivity.getWindow().getDecorView();//获取最顶层的view
    ViewGroup floatViewGroup=(ViewGroup) View.inflate(mActivity, Res.getLayout("circle_frame"), view);
    floatView = View.inflate(mContext, Res.getLayout("circlemenu"), null);
    floatViewGroup.addView(floatView);//把悬浮窗的view加进来

如果是多Activity就需要使用到Application.ActivityLifecycleCallbacks方法。
ActivityLifecycleCallbacks为Activity生命周期监控接口的方法,它包含了一整套Activity的生命周期回调方法,只要有一个Activity触发了声明周期,这个接口的回调就会触发,并且传回触发生命周期方法的Activity对象。
在拿到当前显示的Activity之后,通过activity.getWindow().getDecorView() 来获取Activity的视图组。然后把悬浮窗的view加进去。切换Activity时,把悬浮窗的view销毁,在新显示的Activity同样把悬浮窗的view加进去。

二、悬浮球UI 以及功能

Demo示例:

android 返回悬浮窗 手机返回悬浮窗_悬浮窗


这个圆形悬浮球UI一共分四部分:

1、最开始的 悬浮球 icon。可以拖动,并且靠边半隐藏。

拖动功能是悬浮窗中很常见的功能,实现拖动功能需要用到触摸事件的处理View.OnTouchListener。监听滑动所造成xy的变化,传去新的坐标,刷新悬浮球即可。

示例代码:

//悬浮窗的滑动监听
floatView.setOnTouchListener(new FloatingOnTouchListener());
    private class FloatingOnTouchListener implements View.OnTouchListener {
        private int x;
        private int y;
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    x = (int) event.getRawX();
                    y = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int nowX = (int) event.getRawX();
                    int nowY = (int) event.getRawY();
                    x = nowX-floatView.getWidth()/2;
                    y = nowY-floatView.getHeight()/2;
                    floatView.setX(x);
                    floatView.setY(y);
                    floatView.postInvalidate();
                    break;
                default:
                    break;
            }
            return false;
        }
    }

2、圆形菜单,可以转动。主要是参考Android 打造炫目的圆形菜单这篇博客修改。
3、账号页面,一个简单的ListView。ListView的简单使用,可以点击这里
4、加载WebView页面,分两个。
一个用于显示“新游”,“专区”等。
需要监听网页进度,出现错误时,使用默认界面代替。
示例代码:

webView.setWebChromeClient(new MyWebChromeClient());//监听网页进度

class MyWebChromeClient extends WebChromeClient {
    @Override 
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);

    }
    @Override
    public void onReceivedTitle(WebView view, String title) { //接受网页标题
        super.onReceivedTitle(view, title);
        Log.e("test",title+"");
        Web_Title.setText(titleName);//设定显示的标题
        if (title.contains("404") || title.contains("500") || title.contains("Error") || title.contains("找不到网页") || title.contains("网页无法打开")||title.contains("about:blank")) {
            view.loadUrl("about:blank");// 避免出现默认的错误界面
            view.setVisibility(View.GONE); //隐藏WebView
            //显示加载失败页面
            WebNull.setVisibility(View.VISIBLE);
            WebNullPhoto.setBackgroundResource(Res.getDrawable("box_gift_null"));
            WebNullTitle.setText("暂无"+titleName);
        }
    }
}

另一个用于客服。
客服页面需要有导航栏,需要保存聊天记录。
导航:

webview.canGoBack();//判断是否能后退
mWebView.goBack();//后退
mWebView.canGoForward();//判断是否能前进
mWebView.goForward();//前进
mWebView.reload();//刷新

Demo示例:

android 返回悬浮窗 手机返回悬浮窗_手机游戏_02