此代碼只能應用於 版本號19以上的手機
懸浮窗的關鍵是 :WindowManager 以下列出了 button ,Imageview,SurfaceView(視頻) 三種懸浮窗
要想懸浮窗不影響到其他應用的使用 需要將 WindowManager 的 LayoutParams flag 參數設置成以下的模式
// 設置LayoutParam mLayoutParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } mLayoutParams.flags = mLayoutParams.FLAG_NOT_TOUCH_MODAL | mLayoutParams.FLAG_NOT_FOCUSABLE | mLayoutParams.FLAG_FULLSCREEN | mLayoutParams.FLAG_LAYOUT_IN_SCREEN;
設置權限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
檢查是否設置了權限,因為可以再其他應用之上使用 所以需要通過服務來控制懸浮窗
@RequiresApi(api = Build.VERSION_CODES.M) public void startFloatingService(View view) { if(!Settings.canDrawOverlays(this)){ Toast.makeText(this, "當前無權限,請授權", Toast.LENGTH_SHORT).show(); startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName())),0); } else { startService(new Intent(FloatActivity.this, FloatServices.class)); } }
具體代碼如下 Activity:
package com.example.android.recycleautocarousel; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.annotation.RequiresApi; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; import com.example.android.recycleautocarousel.services.FloatServices; public class FloatActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_float); } @RequiresApi(api = Build.VERSION_CODES.M) public void startFloatingService(View view) { if(!Settings.canDrawOverlays(this)){ Toast.makeText(this, "當前無權限,請授權", Toast.LENGTH_SHORT).show(); startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:"+getPackageName())),0); } else { startService(new Intent(FloatActivity.this, FloatServices.class)); } } @RequiresApi(api = Build.VERSION_CODES.M) @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 0) { if (!Settings.canDrawOverlays(this)) { Toast.makeText(this, "授權失敗", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "授權成功", Toast.LENGTH_SHORT).show(); startService(new Intent(FloatActivity.this, FloatServices.class)); } } } }
Service:
package com.example.android.recycleautocarousel.services; import android.app.Service; import android.content.Intent; import android.graphics.Color; import android.graphics.PixelFormat; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.IBinder; import android.provider.Settings; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import com.example.android.recycleautocarousel.R; import java.io.IOException; /** * Created by android on 2018/6/22. */ public class FloatServices extends Service { private Button mButton; private WindowManager mWindowManager; private WindowManager.LayoutParams mLayoutParams; @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { showFloatingWindow(); return super.onStartCommand(intent, flags, startId); } private class FloatingOnTouchListener implements View.OnTouchListener{ private int x; private int y; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: x = (int) event.getRawX(); y = (int) event.getRawY(); break; case MotionEvent.ACTION_MOVE: int nowX = (int) event.getRawX(); int nowY = (int) event.getRawY(); int movedX = nowX - x; int movedY = nowY - y; x = nowX; y = nowY; mLayoutParams.x = mLayoutParams.x + movedX; mLayoutParams.y = mLayoutParams.y + movedY; // 更新懸浮窗控件布局 mWindowManager.updateViewLayout(v, mLayoutParams); break; default: break; } return false; } } /** * 懸浮窗口 視屏 */ private void showFloatingWindow() { if (Settings.canDrawOverlays(this)) { mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); LayoutInflater layoutInflater = LayoutInflater.from(this); View displayView = layoutInflater.inflate(R.layout.image_display, null); displayView.setOnTouchListener(new FloatingOnTouchListener()); // 獲取WindowManager服務 final MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); SurfaceView surfaceView = displayView.findViewById(R.id.video_display_surfaceview); final SurfaceHolder surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { mediaPlayer.setDisplay(surfaceHolder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } }); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mediaPlayer.start(); } }); try { mediaPlayer.setDataSource(this, Uri.parse("https://raw.githubusercontent.com/dongzhong/ImageAndVideoStore/master/Bruno%20Mars%20-%20Treasure.mp4")); mediaPlayer.prepareAsync(); } catch (IOException e) { Toast.makeText(this, "無法打開視頻源", Toast.LENGTH_LONG).show(); } // 設置LayoutParam mLayoutParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } // flag參數必須設置 不然有懸浮窗 但其他應用也無法使用 mLayoutParams.flags = mLayoutParams.FLAG_NOT_TOUCH_MODAL | mLayoutParams.FLAG_NOT_FOCUSABLE | mLayoutParams.FLAG_FULLSCREEN | mLayoutParams.FLAG_LAYOUT_IN_SCREEN; mLayoutParams.format = PixelFormat.RGBA_8888; mLayoutParams.width = 300; mLayoutParams.height = 300; mLayoutParams.x = 300; mLayoutParams.y = 300; // 將懸浮窗控件添加到WindowManager mWindowManager.addView(displayView, mLayoutParams); } } /** * 懸浮窗口Imageview */ private void showFloatingWindow2() { if (Settings.canDrawOverlays(this)) { // 獲取WindowManager服務 mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); LayoutInflater layoutInflater = LayoutInflater.from(this); View displayView = layoutInflater.inflate(R.layout.image_display, null); displayView.setOnTouchListener(new FloatingOnTouchListener()); ImageView imageView = displayView.findViewById(R.id.image_display_imageview); imageView.setImageResource(R.drawable.item1); // 設置LayoutParam mLayoutParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } // flag參數必須設置 不然有懸浮窗 但其他應用也無法使用 mLayoutParams.flags = mLayoutParams.FLAG_NOT_TOUCH_MODAL | mLayoutParams.FLAG_NOT_FOCUSABLE | mLayoutParams.FLAG_FULLSCREEN | mLayoutParams.FLAG_LAYOUT_IN_SCREEN; mLayoutParams.format = PixelFormat.RGBA_8888; mLayoutParams.width = 100; mLayoutParams.height = 100; mLayoutParams.x = 300; mLayoutParams.y = 300; // 將懸浮窗控件添加到WindowManager mWindowManager.addView(displayView, mLayoutParams); } } /** * 懸浮窗口button */ private void showFloatingWindow1() { if (Settings.canDrawOverlays(this)) { // 獲取WindowManager服務 mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); // 新建懸浮窗控件 mButton = new Button(getApplicationContext()); mButton.setText("Floating Window"); mButton.setBackgroundColor(Color.BLUE); // 設置LayoutParam mLayoutParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } // flag參數必須設置 不然有懸浮窗 但其他應用也無法使用 mLayoutParams.flags = mLayoutParams.FLAG_NOT_TOUCH_MODAL | mLayoutParams.FLAG_NOT_FOCUSABLE | mLayoutParams.FLAG_FULLSCREEN | mLayoutParams.FLAG_LAYOUT_IN_SCREEN; mLayoutParams.format = PixelFormat.RGBA_8888; mLayoutParams.width = 500; mLayoutParams.height = 100; mLayoutParams.x = 300; mLayoutParams.y = 300; // 將懸浮窗控件添加到WindowManager mWindowManager.addView(mButton, mLayoutParams); } } }
activity_float.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_float" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.android.recycleautocarousel.FloatActivity" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="nihao " android:onClick="startFloatingService" /> </RelativeLayout>
image_display.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:visibility="gone" android:id="@+id/image_display_imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <SurfaceView android:id="@+id/video_display_surfaceview" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
打完收工