Android添加一个悬浮窗

在Android 添加悬浮窗需要两个了解两个知识点

1. Window
Window是一个抽象类,PhoneWindow是它的唯一实现类。
Window实际上是View的直接管理者。
Android中的所有视图都是通过Window来实现的。
不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的。
View是Android中呈现视图的方式,但是View不能单独存在,必须附着在Window这个抽象的概念上。
有视图的地方就有Window。
2. WindowManager
创建一个Window需要通过WindowManager。
Window的具体实现在WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。

Window 参数

Window设置属性都是通过一个LayoutParams来实现的,这个WindowManager.LayoutParams是WindowManager的静态内部类,用来管理Window的参数。

1. Flags参数
Flags参数可以控制Window的显示特性。Flags参数非常多,详细看这里。
常用的有下面几种:

FLAG_NOT_TOUCH_MODAL
设置后Window区域外的事件传递给下层的Window,区域内的事件自己处理。
一般都会设置该参数,否则其它Window会收不到事件。

FLAG_NOT_FOCUSABLE
不接受任何输入事件,跳不出软键盘。
这个参数会同时也会开启FLAG_NOT_TOUCH_MODAL。

2. Type参数
这个参数表示Window的类型,分为三种:

应用Window:对应着一个Activity。
子Window:不能单独存在,附属在父Window中,比如Dialog。
系统Window:需要声明权限,比如Toast、系统状态栏。
Window是分层的,每个Window都有对应的z-ordered,大层级的Window会覆盖在小层级的上面。层级就对应着Type参数。

下篇文章将讲解下如何通过Type参数 自定义层级关系

言归正传

Window的添加是通过addView()来实现的。

WindowManagerImpl # addView()
在源码中,可以看出WindowManagerImpl并没有直接实现,而是全部交给mGlobal。

下面的代码为创造一个 可以拖动的悬浮窗口

package com.example.floatwindow;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.lang.reflect.Method;

public class FloatWindow extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "FloatWindow";
    private static final int REQUEST_CODE = 1;
    WindowManager mWindowManager;
    WindowManager.LayoutParams mwParams;
    LinearLayout mFloatLayout;
    Button float_btn;
    Button  start_btn;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        start_btn = findViewById(R.id.start_btn);
        start_btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_btn:
                if (floatPermissionCheck(this)) {
                    createFloatView();
                }else {
                    requestFLoatPermission();
                }
                    
        }
    }

    private void requestFLoatPermission() {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, REQUEST_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (Settings.canDrawOverlays(this)) {
                    Log.i(TAG, "onActivityResult granted");
                    createFloatView();
                } else {
                    Log.i(TAG, "onActivityResult not Granted");
                }
            }
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    private void createFloatView() {
        mwParams = new WindowManager.LayoutParams();
        mWindowManager = this.getWindowManager();
        mwParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        mwParams.format = PixelFormat.RGBA_8888;
        mwParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        mwParams.gravity = Gravity.LEFT | Gravity.TOP;
        mwParams.x = 0;
        mwParams.y = 100;
        mwParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mwParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mwParams.width = 200;
        mwParams.height = 200;

        LayoutInflater inflater = this.getLayoutInflater();
        mFloatLayout = (LinearLayout) inflater.inflate(R.layout.layout_item, null);
        float_btn = mFloatLayout.findViewById(R.id.float_btn);
        mWindowManager.addView(mFloatLayout,mwParams);

        float_btn.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mwParams.x = (int) event.getRawX() - mFloatLayout.getWidth() / 2;
                mwParams.y = (int) event.getRawY() - mFloatLayout.getHeight()/ 2;
                mWindowManager.updateViewLayout(mFloatLayout,mwParams);
                return false;
            }
        });

        float_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(FloatWindow.this, "我可以移动", Toast.LENGTH_SHORT).show();

            }
        });




    }

//    @Override
//    protected void onDestroy() {
//        super.onDestroy();
//        if (mFloatLayout != null) {
//            mWindowManager.removeView(mFloatLayout);
//        }
//    }
    //判断权限
    private boolean floatPermissionCheck(Context context) {
        Boolean result = true;
        if (Build.VERSION.SDK_INT >= 23) {
            try {
                Class clazz = Settings.class;
                Method canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context.class);
                result = (Boolean) canDrawOverlays.invoke(null, context);
            } catch (Exception e) {
                Log.e(TAG, Log.getStackTraceString(e));
            }
        }
        return result;
    }


}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值