关于android 浮动窗口的使用浅解

最近项目在搞音频的小窗口播放(不是类似于音乐播放器的那种高端大气上档次的货),经过一系列的挣扎和努力算是搞出和设计师要求的效果了


效果如下(不要在意图片的名字)


搜狗截图_看图王.png

就是这么个小东西,在app内外来回跑的那种(原谅我不能用gift,不知道公司让不让,相信这么浅白的东西,应该都能懂吧...)


看也看了,那就开撸吧

1,写一个小窗口的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/ll_layout"
              xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
    >

    <LinearLayout
        android:id="@+id/ll_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/float_window_back"//.9 图片
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <FrameLayout
            android:layout_width="40dp"
            android:layout_height="40dp"
            >


            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:scaleType="centerCrop"
                android:src="@drawable/float_window_img"/>

            <ProgressBar
                android:layout_width="25dp"
                android:layout_height="25dp"
                android:layout_gravity="center"
                android:indeterminateDrawable="@drawable/float_window_anim"/>//动画
        </FrameLayout>


        <TextView
            android:id="@+id/text_room_id"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="8dp"
            android:layout_weight="1"
            android:ellipsize="end"
            android:gravity="center"
            android:singleLine="true"
            android:textColor="#E6E6E6"
            android:textSize="14sp"/>

        <ImageButton
            android:id="@+id/bt_close"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:background="@color/transparent"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"
            android:src="@drawable/float_window_close"
            />
    </LinearLayout>


</LinearLayout>
float_window_anim

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" >
    <item android:drawable="@drawable/float_window_animate_01" android:duration="300"></item>
    <item android:drawable="@drawable/float_window_animate_02" android:duration="300"></item>
    <item android:drawable="@drawable/float_window_animate_03" android:duration="300"></item>
</animation-list>

2,撸一个小窗口的自定义View

public class FloatWindowView extends LinearLayout implements View.OnClickListener {
    private final TextView text_room_id;
    public final ImageButton bt_close;
    private WindowManager mManager;
    private Context mContext;
    private WindowManager.LayoutParams mParams;

    public FloatWindowView(Context context) {
        super(context);
        mContext = context;
        mManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater.from(context).inflate(R.layout.dialog_video_play, this);
        text_room_id = (TextView) findViewById(R.id.text_room_id);
        bt_close = (ImageButton) findViewById(R.id.bt_close);
        bt_close.setOnClickListener(this);
    }
    
//外部传入LayoutParams 
    public void setParams(WindowManager.LayoutParams params) {
        this.mParams = params;
    }

    int lastX, lastY;
    int paramX, paramY;
    boolean isMove;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
                paramX = mParams.x;
                paramY = mParams.y;
                break;
            case MotionEvent.ACTION_MOVE:

                int dx = (int) event.getRawX() - lastX;
                int dy = (int) event.getRawY() - lastY;
                if (Math.abs(dx) >= 50 || Math.abs(dy) >= 50) {
                    isMove = true;
                }
                mParams.x = paramX + dx;
                mParams.y = paramY + dy;
                mManager.updateViewLayout(this, mParams);
                break;
            case MotionEvent.ACTION_UP:
                if (!isMove) {
                    int x = (int) event.getRawX() - lastX;
                    int y = (int) event.getRawY() - lastY;
                    if (Math.abs(x) <= 50 || Math.abs(y) <= 50) {
                        //模拟点击事件
                      doClick();
                    }
                }
                isMove = false;
                break;
        }
        return true;

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_close:
               //关闭窗口,服务之类的东东
                break;
        }
    }
    
}
这里有个插曲,我们不能给整个小窗口的根布局设置点击事件,这样的话 mManager.updateViewLayout(this, mParams);更新小窗口位置的事件就会被拦截,导致小窗口不能跟着手指滑动,因此用了个粗鄙的办法模拟点击事件了
```基于我们项目的实现方式,我就直接说我们的方式了

3,在Application中创建,显示并记录小窗口的显示状态

//显示状态表示
 public boolean inWindowShowing = false;
 private FloatWindowView mFloatWindowView;

    public void createFloatView(Context context) {
        if (null == mFloatWindowView) {
            mFloatWindowView = new FloatWindowView(context);
//获取窗体服务
            WindowManager wm = (WindowManager) getSystemService(
                    Context.WINDOW_SERVICE);
//创建一个窗体布局参数
            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//保证背景无色
            params.format = PixelFormat.TRANSPARENT;
//设置窗体初始化的位置(这个很纠结,下文再讲)
            params.gravity = Gravity.CENTER_VERTICAL;
            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            params.width =WindowManager.LayoutParams.WRAP_CONTENT;
            params.height =WindowManager.LayoutParams.WRAP_CONTENT;
//x坐标的位置(这个是在gravity的基础上的)
            params.x= wm.getDefaultDisplay().getWidth()/2;
//y坐标位置
            params.y=wm.getDefaultDisplay().getHeight()/2- UIUtils.dip2px(100);
//这个标识的优先级低于下拉通知栏
                params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
//这个就高于了  不推荐使用
//                params.type = WindowManager.LayoutParams.TYPE_PHONE;


        /*
         * 下面的flags属性的效果形同“锁定”。 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
         * wmParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL |
         * LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE;
         */
//先给浮窗view设置参数,方便滑动时动态改变小窗口位置
            mFloatWindowView.setParams(params);
           //添加到窗体上
            wm.addView(mFloatWindowView, params);
//显示标识
            inWindowShowing = true;
        }

    }
//这个很简单,从窗体上移除
    public void reMoveFloatView() {
        if (null != mFloatWindowView) {
            WindowManager wm = (WindowManager) getSystemService(
                    Context.WINDOW_SERVICE);
            wm.removeView(mFloatWindowView);
            mFloatWindowView = null;
            inWindowShowing = false;
        }
    }
#然而然而然而,这个小窗口的初始化位置可把我整的够呛,只要把gravity设置成TOP和LEFT,
#好的可以就是在屏幕的左上角了(当然设置成CENTER就会居中),但是呢  我们的设计说要在界面的右下角
#并且距离底部导航栏高个20px,那么问题就来了  我把gravity设置成RIGHT|BOTTOM不就行了?NONONO,
#TOO羊TOO新坡,设置成Bottom  只能在底部移动了就,  设置成Right和Bottom那么就直接不能动了,
#所以才有了设置成CENTER_VERTICAL,这个位置的的X和Y都是相对于屏幕的中间的,
#所以让浮窗显示在右边就把X设置成屏幕的一半,下边就把Y设置成高度的一半加上导航栏的高度再加个20px,好吧 位置终于OK 了

这样的话就差不多实现了小窗口了

关于那些个FLOG的什么意思的直接看这个 传送门--TP


到此我们可以安安静静的写写文章泡泡X了,可是.....................

TMD的一些国产ROM默认是把浮窗的权限是关着的...........我曹.....

不过不要慌 还好前辈们帮我们解决了这个问题(至少90%吧)

4,浮窗权限的适配

具体做法传送门--TP

再次向前辈们致敬...........

这个实现方式目前是有缺陷的,最好能在service中创建,这样不容易被系统回收,反正问题还是有得,欢迎大家在评论区点评!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,413评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,351评论 0 17
  • 繁华过谦,都迷途在了金钱利益堆积的虚荣里。在这个你骗我我骗你的社会,哪还有什么最纯的爱,哪还有最真挚的感情。这些...
    地窖里的白菜阅读 240评论 0 1
  • 每个人的一生都如同一次秋天,都会经历落花,败叶的过程。但有的人蜕变成为了天鹅;而有的人却一败涂地;在我们的年华中,...
    崔伊萌阅读 451评论 1 0
  • 你总觉得人有压力才会进步,人有压力是会进步没错,不过也是因人而已的,压力对于心理承受能力强的人,可能会是进步的动力...
    Dhchfuc阅读 583评论 0 0