先來看一下實現效果吧
功能較爲簡單,直接貼出主要實現代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener{
private WindowManager.LayoutParams mParams;
private WindowManager mWindowManager;
private Button mFloatingButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init(){
mWindowManager=(WindowManager) getSystemService(Context.WINDOW_SERVICE);
findViewById(R.id.add).setOnClickListener(this);
findViewById(R.id.remove).setOnClickListener(this);
}
private void requestWindowPermission(){
//android 6.0或者之後的版本需要發一個intent讓用戶授權
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if(!Settings.canDrawOverlays(getApplicationContext())){
Intent intent=new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:"+getPackageName()));
startActivityForResult(intent,100);
}
}
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.add){
//設置允許彈出懸浮窗口的權限
requestWindowPermission();
//創建窗口布局參數
mParams=new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,0,0,PixelFormat.TRANSPARENT);
//設置懸浮窗座標
mParams.x=100;
mParams.y=100;
//表示該Window無需獲取焦點,也不需要接收輸入事件
mParams.flags=WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mParams.gravity = Gravity.LEFT | Gravity.TOP;
Log.d("MainActivity","sdk:"+Build.VERSION.SDK_INT);
//設置window 類型
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){//API Level 26
mParams.type=WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mParams.type=WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
}
//創建懸浮窗(其實就創建了一個Button,這裏也可以創建其他類型的控件)
if(null == mFloatingButton){
mFloatingButton=new Button(this);
mFloatingButton.setText("Floating");
mFloatingButton.setOnTouchListener(this);
mFloatingButton.setOnClickListener(this);
mWindowManager.addView(mFloatingButton,mParams);
}
} else if(v.getId()==R.id.remove){
if(null != mFloatingButton){
mWindowManager.removeView(mFloatingButton);
mFloatingButton=null;
}
} else if(v==mFloatingButton){
Toast.makeText(getApplicationContext(),"Click",Toast.LENGTH_SHORT).show();
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
int rawX=(int)event.getRawX();
int rawY=(int)event.getRawY();
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
mParams.x=rawX;
mParams.y=rawY;
mWindowManager.updateViewLayout(mFloatingButton,mParams);
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(null != mFloatingButton){
mWindowManager.removeView(mFloatingButton);
}
}
}
相關說明:(源碼也已上傳,有興趣的可以獲取源碼運行一下)
1. 不要忘記在 manifest 文件中添加以下權限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
因爲在android 6.0 以後添加懸浮窗需要在manifest中添加權限:
Checks if the specified context can draw on top of other apps. As of API level 23, an app cannot draw on top of other apps unless it declares the Manifest.permission.SYSTEM_ALERT_WINDOW
permission in its manifest, and the user specifically grants the app this capability. To prompt the user to grant this approval, the app must send an intent with the action ACTION_MANAGE_OVERLAY_PERMISSION
, which causes the system to display a permission management screen.
詳情可以參考 google 文檔:
2. 關於 WindowManager.LayoutParams 中 type參數的設置
由於在 Android 8.0 或者之後的版本 以下字段被廢棄
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
TYPE_PHONE
所以在設置 type 字段時需要進行版本的判斷
詳細信息也可以參考 Google 官方文檔
public static final int TYPE_PHONE
This constant was deprecated in API level 26.
for non-system apps. Use TYPE_APPLICATION_OVERLAY
instead.
Google 文檔地址:
資源下載地址:
https://download.csdn.net/download/lollo01/12099115