前言
悬浮窗是一种比较常见的需求。例如把视频通话界面缩小成一个悬浮窗,然后用户可以在其他界面上处理事情。
本文将讲解悬浮窗实现步骤、原理、实例代码等
实现原理
1. WindowMananger 接口
Android 的界面绘制,都是通过 WindowMananger
的服务来实现的。那么,既然要实现一个能够在自身应用以外的界面上的悬浮窗,我们就要利用 WindowManager
来实现。
(frameworks/base/core/java/android/view/WindowMananger.java)@SystemService(Context.WINDOW_SERVICE)public interface WindowManager extends ViewManager {...}
WindowManager
实现了 ViewManager
接口,可以通过获取 WINDOW_SERVICE
系统服务得到。而ViewManager
接口有 addView
方法,我们就是通过这个方法将悬浮窗控件加入到屏幕中去。
2. LayoutParam 设置
这里需要着重说明的是 LayoutParam
里的 type
变量。这个变量是用来指定窗口类型的。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else { layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;}
具体实现
实现一个自动轮播图的悬浮窗。
1. 效果图
2. 声明及申请权限
@RequiresApi(api = Build.VERSION_CODES.M)public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1) { if (!Settings.canDrawOverlays(this)) { Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show(); startService(new Intent(MainActivity.this, FloatingImageDisplayService.class)); } } } public void startFloatingImageDisplayService(View view) { if (FloatingImageDisplayService.isStarted) { return; } if (!Settings.canDrawOverlays(this)) { Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT); startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 1); } else { startService(new Intent(MainActivity.this, FloatingImageDisplayService.class)); } }}
3. 构建悬浮窗需要的控件
image_display.xml
4. 将控件添加到WindowManager
@RequiresApi(api = Build.VERSION_CODES.M)public class FloatingImageDisplayService extends Service { public static boolean isStarted = false; private WindowManager windowManager; private WindowManager.LayoutParams layoutParams; private View displayView; private int[] images; private int imageIndex = 0; private Handler changeImageHandler; @Override public void onCreate() { super.onCreate(); isStarted = true; windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); layoutParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } layoutParams.format = PixelFormat.RGBA_8888; layoutParams.gravity = Gravity.LEFT | Gravity.TOP; layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; layoutParams.width = 500; layoutParams.height = 500; layoutParams.x = 300; layoutParams.y = 300; images = new int[] { R.mipmap.image_01, R.mipmap.image_02, R.mipmap.image_03, R.mipmap.image_04, R.mipmap.image_05 }; changeImageHandler = new Handler(this.getMainLooper(), changeImageCallback); } @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 void showFloatingWindow() { if (Settings.canDrawOverlays(this)) { LayoutInflater layoutInflater = LayoutInflater.from(this); displayView = layoutInflater.inflate(R.layout.image_display, null); displayView.setOnTouchListener(new FloatingOnTouchListener()); ImageView imageView = displayView.findViewById(R.id.image_display_imageview); imageView.setImageResource(images[imageIndex]); windowManager.addView(displayView, layoutParams); changeImageHandler.sendEmptyMessageDelayed(0, 2000); } } private Handler.Callback changeImageCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == 0) { imageIndex++; if (imageIndex >= 5) { imageIndex = 0; } if (displayView != null) { ((ImageView) displayView.findViewById(R.id.image_display_imageview)).setImageResource(images[imageIndex]); } changeImageHandler.sendEmptyMessageDelayed(0, 2000); } return false; } }; private class FloatingOnTouchListener implements View.OnTouchListener { private int x; private int y; @Override public boolean onTouch(View view, 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; layoutParams.x = layoutParams.x + movedX; layoutParams.y = layoutParams.y + movedY; windowManager.updateViewLayout(view, layoutParams); break; default: break; } return false; } }}
来源地址:https://blog.csdn.net/duoduo_11011/article/details/130031272