赞
踩
记录一下基础的悬浮窗实现,分为几个重要的点进行阐述。
app要实现悬浮窗功能,首先app要添加对悬浮窗功能的支持。
manifest文件添加权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
app内也要去进行界面跳转,在设置里打开该应用的悬浮窗权限支持。
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(intent);
}
通过Button开启服务的方式来实现悬浮窗,使用Windowmanager添加悬浮窗View。
悬浮窗的属性一般为TYPE_APPLICATION_OVERLAY、TYPE_SYSTEM_ALERT、TYPE_TOAST、TYPE_APPLICATION_OVERLAY 等,这里采用TYPE_APPLICATION_OVERLAY 赋予悬浮窗基本属性。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
params.type = WindowManager.LayoutParams.TYPE_PHONE;
}
// 设置悬浮框不可触摸
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
对view进行ontouch事件的重写,更新坐标即可
// 设置悬浮框的Touch监听 btnView.setOnTouchListener(new View.OnTouchListener() { //保存悬浮框最后位置的变量 int lastX, lastY; int paramX, paramY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); paramX = params.x; paramY = params.y; break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; params.x = paramX + dx; params.y = paramY + dy; // 更新悬浮窗位置 windowManager.updateViewLayout(btnView, params); break; } return true; } });
确实挺简单的,没什么可讲的。
activity
public class WindowActivity extends AppCompatActivity implements View.OnClickListener { private Button btn_on; Button btn_off; Boolean isOpen = false; Intent mIntent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_window); bindViews(); } private void bindViews() { btn_on = findViewById(R.id.btn_on); btn_on.setOnClickListener(this); btn_off = findViewById(R.id.btn_off); btn_off.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_on: mIntent = new Intent(WindowActivity.this, FloatService.class); mIntent.putExtra(FloatService.OPERATION, FloatService.OPERATION_SHOW); if (!Settings.canDrawOverlays(this)) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(intent); } else { startService(mIntent); Toast.makeText(WindowActivity.this, "悬浮框已开启~", Toast.LENGTH_SHORT).show(); isOpen = true; } break; case R.id.btn_off: if (isOpen) { stopService(mIntent); isOpen = false; } Toast.makeText(WindowActivity.this, "悬浮框已关闭~", Toast.LENGTH_SHORT).show(); } } @Override protected void onDestroy() { super.onDestroy(); if (isOpen) { stopService(mIntent); isOpen = false; } } }
FloatService
public class FloatService extends Service { Button btnView; WindowManager windowManager; WindowManager.LayoutParams params; Boolean isAdded; public static String OPERATION = "是否需要开启"; public static int OPERATION_SHOW = 1; public static int OPERATION_HIDE = 2; int HANDLE_CHECK_ACTIVITY = 0; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { int operation = intent.getIntExtra(OPERATION, 3); if (operation == OPERATION_SHOW) { mHandler.sendEmptyMessage(HANDLE_CHECK_ACTIVITY); } else if (operation == OPERATION_HIDE) { mHandler.removeMessages(HANDLE_CHECK_ACTIVITY); } return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { createWindowView(); super.onCreate(); } @SuppressLint("ClickableViewAccessibility") private void createWindowView() { btnView = new Button(getApplicationContext()); btnView.setBackgroundResource(R.drawable.author); windowManager = (WindowManager) getApplicationContext() .getSystemService(Context.WINDOW_SERVICE); params = new WindowManager.LayoutParams(); // 设置悬浮框不可触摸 params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应 params.format = PixelFormat.RGBA_8888; // 设置悬浮框的宽高 params.width = 200; params.height = 200; params.gravity = Gravity.LEFT; params.x = 200; params.y = 000; // 设置Window Type if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { params.type = WindowManager.LayoutParams.TYPE_PHONE; } // 设置悬浮框的Touch监听 btnView.setOnTouchListener(new View.OnTouchListener() { //保存悬浮框最后位置的变量 int lastX, lastY; int paramX, paramY; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: lastX = (int) event.getRawX(); lastY = (int) event.getRawY(); paramX = params.x; paramY = params.y; break; case MotionEvent.ACTION_MOVE: int dx = (int) event.getRawX() - lastX; int dy = (int) event.getRawY() - lastY; params.x = paramX + dx; params.y = paramY + dy; // 更新悬浮窗位置 windowManager.updateViewLayout(btnView, params); break; } return true; } }); windowManager.addView(btnView, params); isAdded = true; } /** * 判断当前界面是否是桌面 * android 6.0以上只能判断当前应用包名和Launcher */ private boolean isAtHome() { ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> runningTaskInfos = mActivityManager.getRunningTasks(1); Log.d("henry", "是否在主页面" + runningTaskInfos); return getHomeApplicationList().contains(runningTaskInfos.get(0).topActivity.getPackageName()); } /** * 获得属于桌面的应用的应用包名称 * * @return 返回包含所有包名的字符串列表 */ /** * 获得属于桌面的应用的应用包名称 * 返回包含所有包名的字符串列表数组 * * @return */ private List<String> getHomeApplicationList() { List<String> names = new ArrayList<String>(); PackageManager packageManager = this.getPackageManager(); Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo resolveInfo : resolveInfos) { names.add(resolveInfo.activityInfo.packageName); } Log.d("henry", "主屏幕应用列表" + names); return names; } //定义一个更新界面的Handler @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == HANDLE_CHECK_ACTIVITY) { // if (isAtHome()) { if (!isAdded) { windowManager.addView(btnView, params); isAdded = true; new Thread(new Runnable() { public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Message m = new Message(); m.what = 2; mHandler.sendMessage(m); } } }).start(); } // } else { // if (isAdded) { // windowManager.removeView(btnView); // isAdded = false; // } // } mHandler.sendEmptyMessageDelayed(HANDLE_CHECK_ACTIVITY, 100); } } }; @Override public void onDestroy() { if (isAdded) { windowManager.removeView(btnView); } mHandler.removeCallbacksAndMessages(null); windowManager = null; mHandler = null; super.onDestroy(); } }
别忘了在Manifest文件声明service
<service android:name="com.henry.windowManagerTest.My_Floating_Window.FloatService" />
看一下实现效果:
后续增加缩放+MPAndroidChart效果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。