1. 程式人生 > >Android自定義可移動懸浮窗,WindowManager.LayoutParams一些屬性介紹

Android自定義可移動懸浮窗,WindowManager.LayoutParams一些屬性介紹

效果圖

效果圖

thanks

首先介紹一下常見的WindowManager.LayoutParams常量屬性

layoutParams.flag

int型別 常量介紹
FLAGS_CHANGED 用於表示flags發生了變化
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 當該window對使用者可見的時候,允許鎖屏。
FLAG_BLUR_BEHIND 讓該window後所有東西都模糊(blur)
FLAG_DIM_BEHIND 讓該window後所有的東西都成暗淡(dim)
FLAG_DITHER 開啟抖動(dithering)
FLAG_FORCE_NOT_FULLSCREEN 恢復window非全屏顯示
FLAG_FULLSCREEN 讓window進行全屏顯示
FLAG_KEEP_SCREEN_ON 當該window對使用者可見時,讓裝置螢幕處於高亮(bright)狀態。
FLAG_LAYOUT_IN_SCREEN 讓window佔滿整個手機螢幕,不留任何邊界(border)
FLAG_LAYOUT_NO_LIMITS window大小不再不受手機螢幕大小限制,即window可能超出螢幕之外,這時部分內容在螢幕之外
FLAG_NOT_FOCUSABLE 讓window不能獲得焦點,這樣使用者快就不能向該window傳送按鍵事件及按鈕事件
FLAG_NOT_TOUCHABLE 讓該window不接受觸控式螢幕事件
FLAG_NOT_TOUCH_MODAL 即使在該window在可獲得焦點情況下,仍然把該window之外的任何event傳送到該window之後的其他window.
FLAG_SECURE 當該window在進行顯示的時候,不允許截圖。
FLAG_SHOW_WALLPAPER 在該window後顯示系統的牆紙(wallpaper)
FLAG_SHOW_WHEN_LOCKED 當鎖屏的時候,顯示該window.
FLAG_TOUCHABLE_WHEN_WAKING 當手機處於睡眠狀態時,如果螢幕被按下,那麼該window將第一個收到到事件
FLAG_TURN_SCREEN_ON 當然window被顯示的時候,系統將把它當做一個使用者活動事件,以點亮手機螢幕。
FLAG_WATCH_OUTSIDE_TOUCH 如果你設定了該flag,那麼在你FLAG_NOT_TOUNCH_MODAL的情況下,即使觸控式螢幕事件傳送在該window之外,其事件被髮送到了後面的window,那麼該window仍然將以MotionEvent.ACTION_OUTSIDE形式收到該觸控式螢幕事件

layoutParams.type

型別 int 屬性介紹
TYPE_APPLICATION 普通的應用程式window,token必須設定為Activity的token,以指出該視窗屬誰
TYPE_APPLICATION_ATTACHED_DIALOG 對話方塊。類似於面板視窗,繪製類似於頂層視窗,而不是宿主的子視窗。
TYPE_APPLICATION_MEDIA 媒體視窗,例如視訊。顯示於宿主視窗下層。
TYPE_APPLICATION_PANEL 面板視窗,顯示於宿主視窗上層
TYPE_APPLICATION_STARTING 用於應用程式啟動時所顯示的視窗。應用本身不要使用這種型別。它用於讓系統顯示些資訊,直到應用程式可以開啟自己的視窗
TYPE_APPLICATION_SUB_PANEL 應用程式視窗的子面板。顯示於所有面板視窗的上層。(GUI的一般規律,越“子”越靠上)
TYPE_BASE_APPLICATION 所有程式視窗的“基地”視窗,其他應用程式視窗都顯示在它上面。
TYPE_INPUT_METHOD 內部輸入法視窗,顯示於普通UI之上。應用程式可重新佈局以免被此視窗覆蓋
TYPE_INPUT_METHOD_DIALOG 內部輸入法對話方塊,顯示於當前輸入法視窗之上
TYPE_KEYGUARD 鎖屏視窗
TYPE_KEYGUARD_DIALOG 鎖屏時顯示的對話方塊
TYPE_PHONE 電話視窗。它用於電話互動(特別是呼入)。它置於所有應用程式之上,狀態列之下。
TYPE_PRIORITY_PHONE 電話優先,當鎖屏時顯示。此視窗不能獲得輸入焦點,否則影響鎖屏。
TYPE_SEARCH_BAR 搜尋欄。只能有一個搜尋欄;它位於螢幕上方。
TYPE_STATUS_BAR 狀態列型別的window。只能有一個狀態列window;它位於螢幕頂端,其他視窗都位於它下方。
TYPE_STATUS_BAR_PANEL 狀態列的滑動面板
TYPE_SYSTEM_ALERT 系統提示window,比如電池低的警告。它總是出現在應用程式視窗之上。
TYPE_SYSTEM_DIALOG 系統對話方塊。(例如音量調節框)
TYPE_SYSTEM_ERROR 系統內部錯誤提示,顯示於所有內容之上
TYPE_SYSTEM_OVERLAY 系統頂層視窗。顯示在其他一切內容之上。此視窗不能獲得輸入焦點,否則影響鎖屏。
TYPE_TOAST toast型別的window
TYPE_WALLPAPER 用於牆紙的window

桌面浮窗許可權申請

<usespermission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

此 許可權6.0以上在清單檔案註冊沒有效果,測試一下,就是動態申請許可權也不能申請成功,只能使用者手動開啟,開啟方法
許可權預覽

 if (Build.VERSION.SDK_INT >= 23) {
            if (!Settings.canDrawOverlays(this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                        Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, 10);
            }
        }

設定彈窗

  private void showWindow() {
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
        layoutParams.width = 200;
        layoutParams.height = 200;
        layoutParams.gravity =  Gravity.CENTER_VERTICAL;
        layoutParams.format = PixelFormat.TRANSPARENT;
        layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
        layoutParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_BLUR_BEHIND
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

        view = View.inflate(MainActivity.this, R.layout.window_view, null);
        TextView textView= (TextView) view;
        textView.setText("自定義彈窗");
        manager.addView(view, layoutParams);
        view.setOnTouchListener(new View.OnTouchListener() {//可以根據TouchView邏輯設定窗體跟著手指移動
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return false;
            }
        });

    }

移除彈窗

windowManager.removeView(view)

自定義跟著手指移動的View

package cn.evun.snakebardemo;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

/**
 */

public class TouchView extends TextView {

    private int downX;
    private int downY;
    private boolean isMove;
    //螢幕密度
    private float density = getResources().getDisplayMetrics().density;

    public TouchView(Context context) {
        super(context, null);

    }

    public TouchView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TouchView);
        isMove = typedArray.getBoolean(R.styleable.TouchView_isMove, true);
        typedArray.recycle();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //相對於控制元件左邊緣的距離
                downX = (int) event.getRawX();
                downY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getRawX();
                int moveY = (int) event.getRawY();

                int dx = moveX - downX;
                int dy = moveY - downY;

                CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) getLayoutParams();
                layoutParams.leftMargin = layoutParams.leftMargin + (int) (dx * density);
                layoutParams.topMargin = layoutParams.topMargin + (int) (dy * density);

                setLayoutParams(layoutParams);
                requestLayout();

                downX = moveX;
                downY = moveY;

                break;
            case MotionEvent.ACTION_UP:
                break;
        }

        return isMove;
    }
}

原始碼傳送