多加了一点功能的可拖动的浮动按钮,点击可弹出包含按钮的控制框

很小的一个Demo,希望能有点用处。Demo代码在文末。

已经有不少关于浮动按钮的例子,在本例中加入了一点功能,并对一些小问题进行了修正。

Demo中共有三个类文件:FloatingView、FloatingService和MainActivity,其中FloatingView是浮动控制按钮的实现,其他两个类主要为能够长时间显示浮动按钮所做。

MainActivity是启动界面,点击该Activity中间的按钮就会启动FloatingService。

FloatingService的onStartCommand(Intent, int, int)方法中会调用new FloatingView(this).showFloatingBtn() 用于显示浮动控制按钮。

FloatingView继承自View,它的内部类OnCtrlViewTouchListener继承自OnTouchListener,用于处理对控制按钮的触控事件。

之前所看的例子中有一个问题是:根据浮动按钮的位置来判断用户的操作是否是点击事件,这会造成即使是浮动按钮发生了微小的移动,也不会触发点击事件,所以需要点击很多次,才会触发一次点击。所以本例中设定了一个用于计时的变量(OnCtrlViewTouchListener类中的mTouchDur),在用户点击按钮时开始计时,抬起时停止,如果小于100毫秒(例子中定义为MAX_MILLI_TREAT_AS_CLICK常量),就认为这次操作用户是想触发点击事件,而非移动浮动按钮。

另外,FloatingView类的removeCtrlViewByTopActivityChag会通过ActivityManager定时检测当前正运行的应用程序是否发生了变化,如果发生了变化就会移除浮动控制按钮,这主要用于放置在实际项目中使用时,浮动按钮不会自我移除的问题。

View Code
private class OnCtrlViewTouchListener implements OnTouchListener {
        private static final long MAX_MILLI_TREAT_AS_CLICK = 100;  //当用户触控控制按钮的时间小于该常量毫秒时,就算控制按钮的位置发生了变化,也认为这是一次点击事件
        
        private WindowManager mWindowManager;
        private WindowManager.LayoutParams mLayoutParams;
        // 触屏监听
        float mLastX, mLastY;

        int mOldOffsetX, mOldOffsetY;
        int mRecordFlag = 0; // 用于重新记录CtrlView位置的标志
        long mTouchDur;  //记录用户触控控制按钮的时间
        
        boolean hasShowedDetail = false;

        public OnCtrlViewTouchListener(WindowManager windowManager,
                WindowManager.LayoutParams layoutParams) {
            mWindowManager = windowManager;
            mLayoutParams = layoutParams;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            final int action = event.getAction();

            float x = event.getX();
            float y = event.getY();

            if (mRecordFlag == 0) {
                mOldOffsetX = mLayoutParams.x; // 偏移量
                mOldOffsetY = mLayoutParams.y; // 偏移量
            }

            if (action == MotionEvent.ACTION_DOWN) {
                mLastX = x;
                mLastY = y;
                mTouchDur = System.currentTimeMillis();

            } else if (action == MotionEvent.ACTION_MOVE) {
                mLayoutParams.x += (int) (x - mLastX); // 偏移量
                mLayoutParams.y += (int) (y - mLastY); // 偏移量

                mRecordFlag = 1;
                mWindowManager.updateViewLayout(mCtrlView, mLayoutParams);
            }

            else if (action == MotionEvent.ACTION_UP) {
                mTouchDur =  System.currentTimeMillis() - mTouchDur;
                int newOffsetX = mLayoutParams.x;
                int newOffsetY = mLayoutParams.y;
                if (mTouchDur < MAX_MILLI_TREAT_AS_CLICK || (mOldOffsetX == newOffsetX && mOldOffsetY == newOffsetY)) {
                    if (hasShowedDetail == false) {
                        if (mDetailView == null) {
                            showDetailView(mWindowManager);
                        } else {
                            mDetailView.setVisibility(VISIBLE);
                        }
                        hasShowedDetail = true;
                    } else {
                        mDetailView.setVisibility(INVISIBLE);
                        hasShowedDetail = false;
                    }
                } else {
                    mRecordFlag = 0;
                }
            }
            return true;
        }

        /**
         * 该方法会显示详情视图
         * 
         * @param windowManager
         *            用于控制通话状态标示出现的初始位置(默认居中)、大小以及属性
         * @return 会返回所创建的控制按钮的WindowManager.LayoutParams型对象。
         */
        private WindowManager.LayoutParams showDetailView(
                WindowManager windowManager) {
            mDetailView = LayoutInflater.from(mContext).inflate(
                    R.layout.detail_window, null);
            mDetailView.setBackgroundColor(Color.TRANSPARENT);

            setDetailBtnsListener();
                        
            WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
            layoutParams.type = 2002; // type是关键,这里的2002表示系统级窗口,你也可以试试2003。
            layoutParams.flags = 40;// 这句设置桌面可控
            layoutParams.format = -3; // 透明
            layoutParams.width = 400;
            layoutParams.height = 230;
            windowManager.addView(mDetailView, layoutParams);
            return layoutParams;
        }

        private void setDetailBtnsListener() {
            Button chgBtn = (Button) mDetailView
                    .findViewById(R.id.btn_chg_stat); // 设置改变通话状态按钮的监听器
            chgBtn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    changCallingStat((Button) v);
                }
            });
            Button hideBtn = (Button) mDetailView.findViewById(R.id.btn_hide); // 设置隐藏按钮的监听器
            hideBtn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    mDetailView.setVisibility(INVISIBLE);
                    hasShowedDetail = false;
                }
            });
            Button removeBtn = (Button) mDetailView.findViewById(R.id.btn_remove);
            removeBtn.setOnClickListener(new OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    mWindowManager.removeView(mDetailView);
                    mWindowManager.removeView(mCtrlView);
                }
            });
        }
        
        private void changCallingStat(Button button) {
            TextView currStat = (TextView) mDetailView
                    .findViewById(R.id.tv_curr_stat);

            if (mContext.getString(R.string.stat_gen).equals(
                    currStat.getText().toString())) {
                button.setEnabled(false);
                sendUpdateMsg(mUpdateStatusHandler, STAT_OPER_BUILD, 0);
                sendUpdateMsg(mUpdateStatusHandler, STAT_OPER_CHECK, 2);
                sendUpdateMsg(mUpdateStatusHandler, STAT_READY_SECURE, 3);
            } else {
                button.setEnabled(false);
                sendUpdateMsg(mUpdateStatusHandler, STAT_OPER_DES, 0);
                sendUpdateMsg(mUpdateStatusHandler, STAT_READY_GENERAL, 4);
            }
        }

        /**
         * 该方法用于向更新通话状态的Handler发送消息
         * 
         * @param handler
         * @param status
                       所发送的状态信息
         * @param seconds
         *            几秒后向handler发送消息
         */
        private void sendUpdateMsg(Handler handler, int status, int seconds) {
            final Message msg = Message.obtain(handler);
            msg.what = status;
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    msg.sendToTarget();
                }
            }, seconds * 1000);

        }
    }

Download Demo

 

 

posted on 2012-10-17 21:01  GloriousOnion  阅读(979)  评论(0编辑  收藏  举报