Skip to content

Commit ff1806c

Browse files
author
maqiang
committed
WindowManager
桌面悬浮窗demo
1 parent ec2fc0e commit ff1806c

File tree

13 files changed

+401
-10
lines changed

13 files changed

+401
-10
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
<uses-permission android:name="android.permission.INTERNET" />
66
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
77
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
8-
8+
<!--全屏悬浮窗 需要申请此权限 -->
9+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
910
<application
11+
android:name=".MyApplication"
1012
android:allowBackup="true"
1113
android:icon="@mipmap/android_study"
1214
android:label="@string/app_name"
1315
android:supportsRtl="true"
1416
android:theme="@style/AppTheme">
17+
1518
<meta-data
1619
android:name="UMENG_CHANNEL"
1720
android:value="${UMENG_CHANNEL_VALUE}" />
@@ -125,6 +128,9 @@
125128
<activity
126129
android:name=".NestedScroll.nested.NestedScrollActivity"
127130
android:screenOrientation="portrait" />
131+
<activity
132+
android:name=".popup.WindowManagerActivity"
133+
android:screenOrientation="portrait" />
128134
</application>
129135

130136
</manifest>

app/src/main/java/org/ninetripods/mq/study/MainActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void initViews() {
4242
navigationView = (NavigationView) findViewById(R.id.navigation_view);
4343
ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, drawer_layout, toolbar, 0, 0);
4444
drawerToggle.syncState();
45-
selectItem(5);
45+
selectItem(1);
4646
}
4747

4848
@Override
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.ninetripods.mq.study;
2+
3+
import android.app.Application;
4+
5+
/**
6+
* Created by mq on 2018/8/19 下午9:17
7+
8+
*/
9+
10+
public class MyApplication extends Application {
11+
12+
private static MyApplication application;
13+
14+
@Override
15+
public void onCreate() {
16+
super.onCreate();
17+
application = this;
18+
}
19+
20+
public static MyApplication getApplication() {
21+
return application;
22+
}
23+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package org.ninetripods.mq.study.popup.WindowManager;
2+
3+
import android.annotation.SuppressLint;
4+
import android.content.Context;
5+
import android.graphics.PixelFormat;
6+
import android.graphics.Point;
7+
import android.os.Build;
8+
import android.view.Gravity;
9+
import android.view.MotionEvent;
10+
import android.view.View;
11+
import android.view.WindowManager;
12+
13+
import org.ninetripods.mq.study.MyApplication;
14+
import org.ninetripods.mq.study.popup.WindowManager.view.SmallWindowView;
15+
16+
/**
17+
* Created by mq on 2018/8/19 下午4:38
18+
19+
*/
20+
21+
public class WindowController implements View.OnTouchListener {
22+
23+
@SuppressLint("StaticFieldLeak")
24+
private static WindowController instance;
25+
26+
private WindowManager windowManager;
27+
28+
private WindowManager.LayoutParams layoutParams;
29+
30+
private Context mContext;
31+
32+
private SmallWindowView sys_view;
33+
34+
private WindowController() {
35+
this.mContext = MyApplication.getApplication();
36+
}
37+
38+
public static WindowController getInstance() {
39+
if (instance == null) {
40+
synchronized (WindowController.class) {
41+
if (instance == null) {
42+
instance = new WindowController();
43+
}
44+
}
45+
}
46+
return instance;
47+
}
48+
49+
50+
/**
51+
* 显示悬浮窗
52+
*/
53+
@SuppressLint("ClickableViewAccessibility")
54+
public void showThumbWindow() {
55+
if (sys_view != null) return;
56+
sys_view = new SmallWindowView(mContext);
57+
sys_view.setText("50%");
58+
sys_view.setOnTouchListener(this);
59+
windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
60+
int screenWidth = 0, screenHeight = 0;
61+
if (windowManager != null) {
62+
//获取屏幕的宽和高
63+
Point point = new Point();
64+
windowManager.getDefaultDisplay().getSize(point);
65+
screenWidth = point.x;
66+
screenHeight = point.y;
67+
layoutParams = new WindowManager.LayoutParams();
68+
// layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
69+
// layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
70+
layoutParams.width = 200;
71+
layoutParams.height = 200;
72+
//设置type
73+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
74+
//26及以上必须使用TYPE_APPLICATION_OVERLAY @deprecated TYPE_PHONE
75+
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
76+
} else {
77+
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
78+
}
79+
//设置flags
80+
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
81+
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
82+
layoutParams.gravity = Gravity.START | Gravity.TOP;
83+
//背景设置成透明
84+
layoutParams.format = PixelFormat.TRANSPARENT;
85+
layoutParams.x = screenWidth;
86+
layoutParams.y = screenHeight / 2;
87+
//将View添加到屏幕上
88+
windowManager.addView(sys_view, layoutParams);
89+
}
90+
}
91+
92+
/**
93+
* 更新window
94+
*/
95+
public void updateWindowLayout() {
96+
if (windowManager != null && layoutParams != null) {
97+
windowManager.updateViewLayout(sys_view, layoutParams);
98+
}
99+
}
100+
101+
102+
/**
103+
* 关闭悬浮窗
104+
*/
105+
public void destroyThumbWindow() {
106+
if (windowManager != null && sys_view != null) {
107+
windowManager.removeView(sys_view);
108+
sys_view = null;
109+
}
110+
}
111+
112+
113+
private int mLastY, mLastX;
114+
115+
@SuppressLint("ClickableViewAccessibility")
116+
@Override
117+
public boolean onTouch(View v, MotionEvent event) {
118+
int mInScreenX = (int) event.getRawX();
119+
int mInScreenY = (int) event.getRawY();
120+
switch (event.getAction()) {
121+
case MotionEvent.ACTION_DOWN:
122+
mLastX = (int) event.getRawX();
123+
mLastY = (int) event.getRawY();
124+
break;
125+
case MotionEvent.ACTION_MOVE:
126+
layoutParams.x += mInScreenX - mLastX;
127+
layoutParams.y += mInScreenY - mLastY;
128+
mLastX = mInScreenX;
129+
mLastY = mInScreenY;
130+
updateWindowLayout();
131+
break;
132+
case MotionEvent.ACTION_UP:
133+
break;
134+
}
135+
return false;
136+
}
137+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.ninetripods.mq.study.popup.WindowManager;
2+
3+
import android.annotation.TargetApi;
4+
import android.app.Activity;
5+
import android.app.AppOpsManager;
6+
import android.content.Context;
7+
import android.content.Intent;
8+
import android.net.Uri;
9+
import android.os.Build;
10+
import android.provider.Settings;
11+
import android.util.Log;
12+
13+
/**
14+
* Created by mq on 2018/8/19 下午10:09
15+
16+
*/
17+
18+
public class WindowUtil {
19+
20+
/**
21+
* @param context context
22+
* @return true表示已同意悬浮窗权限
23+
*/
24+
public static boolean canOverDraw(Context context) {
25+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
26+
//兼容Android O
27+
boolean canDraw = Settings.canDrawOverlays(context);
28+
return canDraw || canDrawOverlaysO(context);
29+
}
30+
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context);
31+
}
32+
33+
/**
34+
* 兼容Android O
35+
*
36+
* @param context Context
37+
* @return true if permission granted
38+
*/
39+
@TargetApi(Build.VERSION_CODES.KITKAT)
40+
private static boolean canDrawOverlaysO(Context context) {
41+
AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
42+
if (appOpsMgr == null) return false;
43+
int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), context.getPackageName());
44+
Log.e("TTT", "android:system_alert_window: mode=" + mode);
45+
return false;
46+
47+
// WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
48+
// if (windowManager == null) return false;
49+
// final View view = new View(context);
50+
// WindowManager.LayoutParams params = new WindowManager.LayoutParams(0, 0,
51+
// Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
52+
// WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
53+
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT);
54+
// view.setLayoutParams(params);
55+
// windowManager.addView(view, params);
56+
// windowManager.removeView(view);
57+
// Log.e("TTT", "000");
58+
// return true;
59+
}
60+
61+
62+
/**
63+
* 跳转到设置页面
64+
*/
65+
@TargetApi(Build.VERSION_CODES.M)
66+
public static void jump2Setting(Activity context, int requestCode) {
67+
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
68+
Uri.parse("package:" + context.getPackageName()));
69+
context.startActivityForResult(intent, requestCode);
70+
}
71+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.ninetripods.mq.study.popup.WindowManager.view;
2+
3+
import android.content.Context;
4+
import android.graphics.Color;
5+
import android.support.annotation.Nullable;
6+
import android.util.AttributeSet;
7+
import android.view.Gravity;
8+
import android.view.ViewGroup;
9+
10+
import org.ninetripods.mq.study.R;
11+
12+
/**
13+
* Created by mq on 2018/8/26 下午5:55
14+
15+
*/
16+
17+
public class SmallWindowView extends android.support.v7.widget.AppCompatTextView {
18+
19+
public SmallWindowView(Context context) {
20+
this(context, null);
21+
}
22+
23+
public SmallWindowView(Context context, @Nullable AttributeSet attrs) {
24+
this(context, attrs, 0);
25+
}
26+
27+
public SmallWindowView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
28+
super(context, attrs, defStyleAttr);
29+
init(context);
30+
}
31+
32+
private void init(Context context) {
33+
setBackgroundResource(R.drawable.popup_sys_circle_bg);
34+
setGravity(Gravity.CENTER);
35+
setTextColor(Color.WHITE);
36+
}
37+
38+
/**
39+
* 设置宽和高的属性
40+
*
41+
* @param height
42+
* @param width
43+
*/
44+
public void setHW(int height, int width) {
45+
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(width, height);
46+
setLayoutParams(params);
47+
}
48+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.ninetripods.mq.study.popup;
2+
3+
import android.content.Intent;
4+
import android.os.Build;
5+
import android.support.v7.widget.Toolbar;
6+
import android.view.View;
7+
8+
import org.ninetripods.mq.study.BaseActivity;
9+
import org.ninetripods.mq.study.R;
10+
import org.ninetripods.mq.study.popup.WindowManager.WindowController;
11+
import org.ninetripods.mq.study.popup.WindowManager.WindowUtil;
12+
13+
public class WindowManagerActivity extends BaseActivity {
14+
15+
private static final int REQUEST_CODE = 101;
16+
17+
@Override
18+
public void setContentView() {
19+
setContentView(R.layout.activity_window_manager);
20+
}
21+
22+
@Override
23+
public void initViews() {
24+
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
25+
initToolBar(toolbar, "WindowManager", true);
26+
}
27+
28+
@Override
29+
public void initEvents() {
30+
super.initEvents();
31+
}
32+
33+
public void start_window(View view) {
34+
if (!WindowUtil.canOverDraw(this)) {
35+
//跳转到设置页面
36+
WindowUtil.jump2Setting(this, REQUEST_CODE);
37+
return;
38+
}
39+
//开启悬浮窗
40+
WindowController.getInstance().showThumbWindow();
41+
}
42+
43+
@Override
44+
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
45+
// super.onActivityResult(requestCode, resultCode, data);
46+
switch (requestCode) {
47+
case REQUEST_CODE:
48+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
49+
if (!WindowUtil.canOverDraw(this)) {
50+
toast("悬浮窗权限未开启,请在设置中手动打开");
51+
return;
52+
}
53+
WindowController.getInstance().showThumbWindow();
54+
break;
55+
}
56+
}
57+
58+
@Override
59+
protected void onDestroy() {
60+
super.onDestroy();
61+
WindowController.getInstance().destroyThumbWindow();
62+
}
63+
64+
public void close_window(View view) {
65+
WindowController.getInstance().destroyThumbWindow();
66+
}
67+
}

0 commit comments

Comments
 (0)