Android实现可移动的悬浮窗
目的
我们在很多android应用中可能会看到悬浮窗按钮,最多的应该就是360了,通过代码我们也可以实现这个功能
先上干货
Demo做完的效果演示视频:
代码
整个程序的代码我们在最后放到网盘里,这里我们只讲关键的几点
首先我们先建一个windowmangerdemo的项目
在activity_main里面增加一个button的按钮
然后在布局Layout里面增加window_small.xml和window_big.xml两个布局文件,用于点击小窗口后展开大窗口
window_small.xml
我们在线性布局管理嚣中直接加上一个相对布局管理嚣.然后设上背景图片即可
FloatWindowSmallView
我们的floatWindowSmallView继承自LinearLayout
重写onTouchEvent事件,我们用于可以随时点击移动和展开我们的悬浮框
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
xInView = event.getX();
yInView = event.getY();
xDownInScreen = event.getRawX();
yDownInScreen = event.getRawY() - getStatusBarHeight();
xInScreen = event.getRawX();
yInScreen = event.getRawY() - getStatusBarHeight();
break;
case MotionEvent.ACTION_MOVE:
xInScreen = event.getRawX();
yInScreen = event.getRawY() - getStatusBarHeight();
// 手指移动的时候更新小悬浮窗的位置
updateViewPosition();
break;
case MotionEvent.ACTION_UP:
// 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
openBigWindow();
}
break;
default:
break;
}
return true;
}
window_big.xml
我们设了三个垂直的ImageButton,代表点击小窗体后展开的大的窗体
FloatWindowBigView
代码比较简单,直接写对应的事件即可
public class FloatWindowBigView extends LinearLayout {
public static int viewWidth;
public static int viewHeight;
public FloatWindowBigView(final Context context) {
super(context);
LayoutInflater.from(context).inflate(R.layout.window_big, this);
View view = findViewById(R.id.big_window_layout);
// viewWidth = view.getLayoutParams().width;
// viewHeight = view.getLayoutParams().height;
viewWidth= 120;
viewHeight = 370;
ImageButton imgbig = (ImageButton) findViewById(R.id.imgbig);
ImageButton imgcamera = findViewById(R.id.imgcamera);
ImageButton imgvoice = findViewById(R.id.imgvoice);
imgcamera.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "点击了照相机", Toast.LENGTH_SHORT).show();
}
});
imgvoice.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "点击了语音识别", Toast.LENGTH_SHORT).show();
}
});
imgbig.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 点击返回的时候,移除大悬浮窗,创建小悬浮窗
MyWindowManager.removeBigWindow(context);
MyWindowManager.createSmallWindow(context);
}
});
}
}
MyWindowManager
这个类是我们管理大小悬浮框的事件类,包括关闭小悬浮窗,展开大悬浮窗等
我们把大小悬浮穿的参数分别对应不同的LayoutParams,都在这里处理
FloatWindow 做一个floatWindow继承自Service
通过Handler和Time控件来刷新悬浮窗是否一直存在
/*** 判断当前界面是否是扫码界面*/
private boolean isForeground(String[] classNames) {if (classNames.length <= 0) {return false;}ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);//判断当前进程List appProcesses = am.getRunningAppProcesses();for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {if (appProcess.processName.equals(getPackageName())) {//判断程序是否处于前台,如果是后台则不显示悬浮框if (appProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {Log.i(getPackageName(), "处于后台"+ appProcess.processName);return false;}}}//判断顶层ActivityList list = am.getRunningTasks(1);if (list != null && list.size() > 0) {ComponentName cpn = list.get(0).topActivity;for (String className : classNames) {if (className.equals(cpn.getClassName())) {return true;}}}return false;
} class RefreshTask extends TimerTask {@Overridepublic void run() {// 当前界面是要显示的界面,且没有悬浮窗显示,则创建悬浮窗。if (isForeground(activityname) && !MyWindowManager.isWindowShowing()) {handler.post(new Runnable() {@Overridepublic void run() {MyWindowManager.createSmallWindow(getApplicationContext());}});}// 当前界面要显示的界面,且有悬浮窗显示,则移除悬浮窗。else if (!isForeground(activityname) && MyWindowManager.isWindowShowing()) {handler.post(new Runnable() {@Overridepublic void run() {MyWindowManager.removeSmallWindow(getApplicationContext());MyWindowManager.removeBigWindow(getApplicationContext());}});}// 当前界面是要显示的界面,且有悬浮窗显示,则更新内存数据。else if (isForeground(activityname) && MyWindowManager.isWindowShowing()) {handler.post(new Runnable() {@Overridepublic void run() {MyWindowManager.updateUsedPercent(getApplicationContext());}});}}} 我们在设置WindowManager.LayoutParams里面的type参数时需要注意的问题
//设置类型 android8.0后有变化此处进行修改
if (Build.VERSION.SDK_INT >= 26) {
bigWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
bigWindowParams.type = WindowManager.Layout
Params.TYPE_SYSTEM_ALERT;
}
android8.0后,也就是sdk大于等于26后,type的类型需要修改了下,否则进去后程序崩溃
9
源码网盘分享地址:
链接:https://pan.baidu.com/s/1YK7jDIcSkLYyNGN4detNoQ 密码:cylx
-END-
长按下方二维码关注
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
