博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 悬浮窗口
阅读量:4637 次
发布时间:2019-06-09

本文共 10903 字,大约阅读时间需要 36 分钟。

Android 悬浮窗口

一.创建悬浮窗口步骤

    1.实现一个ViewGroup类,作为悬浮窗口的界面类,以便在里面重写onInterceptTouchEvent和onTouchEvent方法,实现移动界面的目的.
      在本例中实现了一个FloatLayer类,可以作为通用的类,使用时需要传入WindowManager对象以实现移动窗口.

// FloatLayer ~package com.example.hellofloatingwnd;import static com.ahai.util.DebugMessage.d;import android.content.Context;import android.graphics.PixelFormat;import android.util.AttributeSet;import android.view.Gravity;import android.view.MotionEvent;import android.view.WindowManager;import android.widget.RelativeLayout;public class FloatLayer extends RelativeLayout {    // flags: 须设置成 FLAG_NOT_FOCUSABLE, 否则悬浮窗口下面的窗口不能取得焦点, 无法响应触摸事件    // type: 值低的窗口在值高的下层,相同的 type值,后创建的窗口显示在先创建的窗口上面.    // 对应的type需要相应的权限,否则会报异常 BadTokenException.    // 对于权限 android.permission.SYSTEM_ALERT_WINDOW 可使用以下几个值:    // TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY    // 其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能响应触摸事件    public static class FloatLayoutParams extends WindowManager.LayoutParams {        public FloatLayoutParams() {            super(TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);            gravity = Gravity.LEFT | Gravity.TOP;        }        public FloatLayoutParams(int type) {            super(type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);            gravity = Gravity.LEFT | Gravity.TOP;        }        public FloatLayoutParams(int xpos, int ypos) {            super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,                    ypos, TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);            gravity = Gravity.LEFT | Gravity.TOP;        }        public FloatLayoutParams(int xpos, int ypos, int type) {            super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,                    ypos, type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);            gravity = Gravity.LEFT | Gravity.TOP;        }    }    private WindowManager mWindowManager;    private int mStatusBarHeight;    private int mMoveX;    private int mMoveY;    public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr,            int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        mStatusBarHeight = getStatusBarHeight();    }    public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mStatusBarHeight = getStatusBarHeight();    }    public FloatLayer(Context context, AttributeSet attrs) {        super(context, attrs);        mStatusBarHeight = getStatusBarHeight();    }    public FloatLayer(Context context) {        super(context);        mStatusBarHeight = getStatusBarHeight();    }    public void setWindowManager(WindowManager windowManager) {        mWindowManager = windowManager;    }    /** 取得系统状态栏的高度 */    private int getStatusBarHeight() {        int statusBarHeight = 0;        int resourceId = getResources().getIdentifier("status_bar_height",                "dimen", "android");        if (resourceId > 0) {            statusBarHeight = getResources().getDimensionPixelSize(resourceId);        }        // d("statusBarHeight=" + statusBarHeight);        return statusBarHeight;    }    @Override    public boolean onInterceptTouchEvent(MotionEvent event) {        final int action = event.getAction();        if (action == MotionEvent.ACTION_MOVE) {            if (handleMoveEvent(event))                return true;        } else if (action == MotionEvent.ACTION_DOWN) {            mMoveX = (int) event.getRawX();            mMoveY = (int) event.getRawY();        }        return super.onInterceptTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        final int action = event.getAction();        if (action == MotionEvent.ACTION_MOVE) {            if (handleMoveEvent(event))                return true;        } else if (action == MotionEvent.ACTION_DOWN) {            mMoveX = (int) event.getRawX();            mMoveY = (int) event.getRawY();        }        return super.onTouchEvent(event);    }    private boolean handleMoveEvent(MotionEvent event) {        try {            if (mWindowManager != null) {                // 通过以下消息可知getLayoutParams得到的对象即为 addView 传入的 LayoutParams 对象                // d("class:" + getLayoutParams().getClass());                final int x = (int) event.getRawX();                final int y = (int) event.getRawY();                int[] location = new int[2];                getLocationOnScreen(location);                FloatLayoutParams layoutParams = (FloatLayoutParams) getLayoutParams();                layoutParams.x = location[0] + (x - mMoveX);                layoutParams.y = location[1] + (y - mMoveY) - mStatusBarHeight;                mWindowManager.updateViewLayout(this, layoutParams);                mMoveX = x;                mMoveY = y;                return true;            }        } catch (Exception e) {            d("", e);        }        return false;    }}

    2.在res/layout中创建一个布局文件,实现界面布局.如float_layout.xml

    

    3.取得WindowManager对象.
        在 Activity 中可以通过以下方法取得, 其中前面的3个方法实际取得的是当前Activity的应用程序窗口对象,在Activity销毁等情况下,
    WindowManager对象也就不存在了,需要将悬浮窗口移除,否则会报错.
        WindowManager windowManager;
        windowManager = getWindow().getWindowManager();
        windowManager = getWindowManager();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
        在 Service 中, 以下两个方法均可, 在 onDestory 中将悬浮窗口移除即可.
        windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
   
    4.创建View并显示. 通过以下两个行代码完成:
        mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);
        windowManager.addView(mFloatView, layoutParams);
        其中layoutParams是实现悬浮窗口的关键,窗口的配置及移动都通过其指定.
        LayoutParams需使用WindowManager.LayoutParams对象或继承自该类重写.
        移动窗口通过下面的代码实现:
            mWindowManager.updateViewLayout(mFloatView, layoutParams);
        layoutParams最重要的两个参数是flags和type:
        flags: 须设置成 FLAG_NOT_FOCUSABLE, 否则悬浮窗口下面的窗口不能取得焦点,不能响应触摸事件.
        type: 值低的窗口在值高的下层,相同的 type值,后创建的窗口显示在先创建的窗口上面.
              对应的type需要相应的权限,否则会报异常 BadTokenException.
        对于权限 android.permission.SYSTEM_ALERT_WINDOW, type可使用以下几个值:
            TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY
        其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能响应触摸事件

  在Service中实现

  

package com.example.hellofloatingwnd;import android.app.Application;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.WindowManager;import android.widget.ImageButton;import android.widget.Toast;public class FloatingService extends Service {    private FloatLayer mFloatView;    private WindowManager mWindowManager;    private ImageButton mBtnHide;    @Override    public void onCreate() {        super.onCreate();        Application app = getApplication();        mWindowManager = (WindowManager) app.getSystemService(WINDOW_SERVICE);        LayoutInflater inflater = LayoutInflater.from(app);        mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);        mFloatView.setWindowManager(mWindowManager);        mBtnHide = (ImageButton) mFloatView.findViewById(R.id.mBtnHide);        mBtnHide.setOnClickListener(mClickListener);        FloatLayer.FloatLayoutParams layoutParams;        layoutParams = new FloatLayer.FloatLayoutParams(10, 100);        mWindowManager.addView(mFloatView, layoutParams);    }    @Override    public void onDestroy() {        super.onDestroy();        if (mFloatView != null) {            mWindowManager.removeView(mFloatView);            mFloatView = null;        }    }    @Override    public IBinder onBind(Intent arg0) {        return null;    }    private OnClickListener mClickListener = new OnClickListener() {        @Override        public void onClick(View view) {            if (view.getId() == R.id.mBtnHide) {                Toast.makeText(getApplicationContext(),                        "on float button clicked.", Toast.LENGTH_SHORT).show();                FloatingService.this.stopSelf();            }        }    };}

  在Activity中实现

package com.example.hellofloatingwnd;import android.app.Activity;import android.app.Application;import android.content.Intent;import android.os.Bundle;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.WindowManager;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity {    private Button mBtnStart;    private Button mBtnStop;    private FloatLayer mFloatView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mBtnStart = (Button) findViewById(R.id.mBtnStart);        mBtnStop = (Button) findViewById(R.id.mBtnStop);        mBtnStart.setOnClickListener(mClickListener);        mBtnStop.setOnClickListener(mClickListener);        WindowManager windowManager;        // windowManager = getWindow().getWindowManager();        windowManager = getWindowManager();        // windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);        // windowManager = (WindowManager)        // getApplication().getSystemService(WINDOW_SERVICE);        Application application = getApplication();        LayoutInflater inflater = LayoutInflater.from(application);        mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);        mFloatView.setWindowManager(windowManager);        TextView textView = (TextView) mFloatView.findViewById(R.id.mTvHello);        textView.setText("This create by activity.");        FloatLayer.FloatLayoutParams layoutParams;        layoutParams = new FloatLayer.FloatLayoutParams(50, 200);        windowManager.addView(mFloatView, layoutParams);    }    @Override    protected void onDestroy() {        // TODO Auto-generated method stub        super.onDestroy();        if (mFloatView != null) {            WindowManager windowManager = getWindow().getWindowManager();            windowManager.removeView(mFloatView);            mFloatView = null;        }    }    private OnClickListener mClickListener = new OnClickListener() {        @Override        public void onClick(View view) {            if (view.getId() == R.id.mBtnStart) {                Intent intent = new Intent(MainActivity.this,                        FloatingService.class);                startService(intent);            } else if (view.getId() == R.id.mBtnStop) {                Intent intent = new Intent(MainActivity.this,                        FloatingService.class);                stopService(intent);            }        }    };}

       

    5.在AndroidManifest.xml文件中添加权限,在layoutParams中配置不同的type类型需要不同的权限.
        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
   

转载于:https://www.cnblogs.com/diysoul/p/5169234.html

你可能感兴趣的文章
ubuntu 重启命令,ubuntu 重启网卡方法
查看>>
Linux的学习:
查看>>
JavaScript中的原型继承原理
查看>>
Python logger模块
查看>>
jquery控制css的display(控制元素的显示与隐藏)
查看>>
关于python做人工智能的一个网页(很牛逼)
查看>>
判断控件的CGRect是否重合,获取控件的最大XY值
查看>>
POJ-1128 Frame Stacking
查看>>
异常体系
查看>>
String.format(转)
查看>>
解决 CS0006 未能找到元数据文件
查看>>
HDU 5131.Song Jiang's rank list (2014ACM/ICPC亚洲区广州站-重现赛)
查看>>
mysql搭建主从数据库
查看>>
新的一年,新的开始
查看>>
python模块struct
查看>>
图像的灰度级和动态范围(转)
查看>>
C# MODBUS协议 上位机(转)
查看>>
CSS box-shadow 属性
查看>>
vue:图片切换动态显示
查看>>
04 JDK并发包相关类
查看>>