zoukankan      html  css  js  c++  java
  • Android 悬浮窗口

    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

    <?xml version="1.0" encoding="utf-8"?>
    <com.example.hellofloatingwnd.FloatLayer xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >
    
        <ImageButton
            android:id="@+id/mBtnHide"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/hide_button_selector" />
    
        <TextView
            android:id="@+id/mTvHello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/mBtnHide"
            android:layout_centerHorizontal="true"
            android:text="@string/hello"
            android:textColor="#21d" />
    
    </com.example.hellofloatingwnd.FloatLayer>

        
        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" />
       

  • 相关阅读:
    【CSS 第五天】背景,边框
    CSS Sprite雪碧图
    【ASP】session实现购物车
    【ASP】response和sever对象实现用户登录
    【操作系统】银行家算法
    【操作系统】先来先服务
    【操作系统】多级反馈队列算法
    【页面置换算法】LRC算法和FIFS算法
    Alpha版(内部测试版)发布
    项目结束--事后诸葛亮会议总结
  • 原文地址:https://www.cnblogs.com/diysoul/p/5169234.html
Copyright © 2011-2022 走看看