项目中需要实现一个状态显示的悬浮框,要求可以设置两种模式:拖动模式和不可拖动模式。

实现效果图如下:


实现步骤:

1.首先要设置该悬浮框的基本属性:

/**     * 显示弹出框     *     * @param context     */    @SuppressWarnings("WrongConstant")    public static void showPopupWindow(final Context context, String showtxt) {        if (isShown) {            return;        }        isShown = true;        // 获取WindowManager        mWindowManager = (WindowManager) context                .getSystemService(Context.WINDOW_SERVICE);        mView = setUpView(context, showtxt);        params = new WindowManager.LayoutParams();        // 类型,系统提示以及它总是出现在应用程序窗口之上。        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;        // 设置flag        int flags = canTouchFlags;        // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;        // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件        params.flags = flags;        // 不设置这个弹出框的透明遮罩显示为黑色        params.format = PixelFormat.TRANSLUCENT;        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口        // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按        // 不设置这个flag的话,home页的划屏会有问题        params.width = LayoutParams.WRAP_CONTENT;        params.height = LayoutParams.WRAP_CONTENT;        params.gravity = Gravity.TOP;        mWindowManager.addView(mView, params);    }
比较重要的点是要注意设置flags,我这里提供了两种flags以供切换:

  private static int canTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;    private static int notTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
第一种是可触摸不可聚焦模式,第二种是不可触摸不可聚焦模式。其他的flags可以从api中查阅。


2.设置悬浮框的拖动监听事件:

 private static View setUpView(final Context context, String showtxt) {        View view = LayoutInflater.from(context).inflate(R.layout.layout_popwindow,                null);        TextView showTv = (TextView) view.findViewById(R.id.tv_showinpop);        showTv.setText(showtxt);        rl_drag_showinpop = (RelativeLayout) view.findViewById(R.id.rl_drag_showinpop);        rl_drag_showinpop.setOnTouchListener(new View.OnTouchListener() {            private float lastX; //上一次位置的X.Y坐标            private float lastY;            private float nowX;  //当前移动位置的X.Y坐标            private float nowY;            private float tranX; //悬浮窗移动位置的相对值            private float tranY;            @Override            public boolean onTouch(View v, MotionEvent event) {                boolean ret = false;                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        // 获取按下时的X,Y坐标                        lastX = event.getRawX();                        lastY = event.getRawY();                        ret = true;                        break;                    case MotionEvent.ACTION_MOVE:                        // 获取移动时的X,Y坐标                        nowX = event.getRawX();                        nowY = event.getRawY();                        // 计算XY坐标偏移量                        tranX = nowX - lastX;                        tranY = nowY - lastY;                        params.x += tranX;                        params.y += tranY;                        //更新悬浮窗位置                        mWindowManager.updateViewLayout(mView, params);                        //记录当前坐标作为下一次计算的上一次移动的位置坐标                        lastX = nowX;                        lastY = nowY;                        break;                    case MotionEvent.ACTION_UP:                        break;                }                return ret;            }        });

这里要在down的时候记录坐标,move事件中使用修改params坐标进行移动。


3.设置悬浮框文字属性:

    public static void setShowTxt(String txt) {        try {            TextView showTv = (TextView) mView.findViewById(R.id.tv_showinpop);            showTv.setText(txt);            mWindowManager.updateViewLayout(mView, params);        }catch (Exception e){            Log.d(TAG, "setShowTxt: 更新悬浮框错误");            e.printStackTrace();            if(e.getMessage().contains("not attached to window manager")){                mWindowManager.addView(mView, params);            }        }    }
4.更新悬浮框图片显示:

 public static void setShowImg(Bitmap bitmap) {        try {            ImageView showImg = (ImageView) mView.findViewById(R.id.iv_showinpop);            showImg.setImageBitmap(bitmap);            mWindowManager.updateViewLayout(mView, params);        }catch (Exception e){            Log.d(TAG, "setShowTxt: 更新悬浮框错误");            e.printStackTrace();            if(e.getMessage().contains("not attached to window manager")){                mWindowManager.addView(mView, params);            }        }    }



介绍完毕,整个类都封装好了,代码如下:

/** * 悬浮窗工具类 * created by Pumpkin at 17/3/28 */public class WindowsUitlity {    private static String TAG = WindowsUitlity.class.getSimpleName();    private static WindowManager mWindowManager = null;    private static WindowManager.LayoutParams params;    public static Boolean isShown = false;    private static View mView = null;    /**     * 显示弹出框     *     * @param context     */    @SuppressWarnings("WrongConstant")    public static void showPopupWindow(final Context context, String showtxt) {        if (isShown) {            return;        }        isShown = true;        // 获取WindowManager        mWindowManager = (WindowManager) context                .getSystemService(Context.WINDOW_SERVICE);        mView = setUpView(context, showtxt);        params = new WindowManager.LayoutParams();        // 类型,系统提示以及它总是出现在应用程序窗口之上。        params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;        // 设置flag        int flags = canTouchFlags;        // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;        // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件        params.flags = flags;        // 不设置这个弹出框的透明遮罩显示为黑色        params.format = PixelFormat.TRANSLUCENT;        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口        // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按        // 不设置这个flag的话,home页的划屏会有问题        params.width = LayoutParams.WRAP_CONTENT;        params.height = LayoutParams.WRAP_CONTENT;        params.gravity = Gravity.TOP;        mWindowManager.addView(mView, params);    }    private static int canTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;    private static int notTouchFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;    /**     * 设置是否可响应点击事件     *     * @param isTouchable     */    public static void setTouchable(boolean isTouchable) {        if (isTouchable) {            params.flags = canTouchFlags;        } else {            params.flags = notTouchFlags;        }        mWindowManager.updateViewLayout(mView, params);    }    /**     * 隐藏弹出框     */    public static void hidePopupWindow() {        if (isShown && null != mView) {            mWindowManager.removeView(mView);            isShown = false;        }    }    public static void setShowTxt(String txt) {        try {            TextView showTv = (TextView) mView.findViewById(R.id.tv_showinpop);            showTv.setText(txt);            mWindowManager.updateViewLayout(mView, params);        }catch (Exception e){            Log.d(TAG, "setShowTxt: 更新悬浮框错误");            e.printStackTrace();            if(e.getMessage().contains("not attached to window manager")){                mWindowManager.addView(mView, params);            }        }    }    public static void setShowImg(Bitmap bitmap) {        try {            ImageView showImg = (ImageView) mView.findViewById(R.id.iv_showinpop);            showImg.setImageBitmap(bitmap);            mWindowManager.updateViewLayout(mView, params);        }catch (Exception e){            Log.d(TAG, "setShowTxt: 更新悬浮框错误");            e.printStackTrace();            if(e.getMessage().contains("not attached to window manager")){                mWindowManager.addView(mView, params);            }        }    }    static RelativeLayout rl_drag_showinpop;    private static View setUpView(final Context context, String showtxt) {        View view = LayoutInflater.from(context).inflate(R.layout.layout_popwindow,                null);        TextView showTv = (TextView) view.findViewById(R.id.tv_showinpop);        showTv.setText(showtxt);        rl_drag_showinpop = (RelativeLayout) view.findViewById(R.id.rl_drag_showinpop);        rl_drag_showinpop.setOnTouchListener(new View.OnTouchListener() {            private float lastX; //上一次位置的X.Y坐标            private float lastY;            private float nowX;  //当前移动位置的X.Y坐标            private float nowY;            private float tranX; //悬浮窗移动位置的相对值            private float tranY;            @Override            public boolean onTouch(View v, MotionEvent event) {                boolean ret = false;                switch (event.getAction()) {                    case MotionEvent.ACTION_DOWN:                        // 获取按下时的X,Y坐标                        lastX = event.getRawX();                        lastY = event.getRawY();                        ret = true;                        break;                    case MotionEvent.ACTION_MOVE:                        // 获取移动时的X,Y坐标                        nowX = event.getRawX();                        nowY = event.getRawY();                        // 计算XY坐标偏移量                        tranX = nowX - lastX;                        tranY = nowY - lastY;                        params.x += tranX;                        params.y += tranY;                        //更新悬浮窗位置                        mWindowManager.updateViewLayout(mView, params);                        //记录当前坐标作为下一次计算的上一次移动的位置坐标                        lastX = nowX;                        lastY = nowY;                        break;                    case MotionEvent.ACTION_UP:                        break;                }                return ret;            }        });        return view;    }}




更多相关文章

  1. 7.2 android volley超时重试设置
  2. compileSdkVersion, minSdkVersion 和 targetSdkVersion,坑爹的A
  3. Android(安卓)camera preview and take picture with V4l2
  4. 关于listview
  5. android客户端使用ssl连接mqtt服务器(单向认证)
  6. Android(安卓)SDK sample 之 SoftKeyboard 详解
  7. Android学习笔记(三十):弹出信息-Toast和告警
  8. Android(安卓)Dialog设置TYPE_SYSTEM_ALERT 小米,魅族手机不能显
  9. EditText属性和相关用法

随机推荐

  1. Unity3D 调用Android的提供的接口
  2. Android(安卓)HttpURLConnection及HttpCl
  3. Android(安卓)的系统架构
  4. android:layout_gravity和android:gravit
  5. android小说阅读源码、bilibili源码、MVP
  6. Android(安卓)SDK 2.0安装(配置图文教程)
  7. Android开发人员必须收藏的国外网站
  8. Android知识图谱:我们到底需要学习哪些And
  9. 如何学习android
  10. 跟着做 Android(安卓)NDK学习入门如此简