前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android实现可移动的悬浮窗

Android实现可移动的悬浮窗

作者头像
Vaccae
发布2019-07-24 11:34:11
4.5K1
发布2019-07-24 11:34:11
举报
文章被收录于专栏:微卡智享微卡智享

目的

我们在很多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,都在这里处理


代码语言:javascript
复制
FloatWindow

做一个floatWindow继承自Service

通过Handler和Time控件来刷新悬浮窗是否一直存在

代码语言:javascript
复制
/**
 * 判断当前界面是否是扫码界面
 */
private boolean isForeground(String[] classNames) {
    if (classNames.length <= 0) {
        return false;
    }

    ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    //判断当前进程
    List<ActivityManager.RunningAppProcessInfo> 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;
            }
        }
    }

    //判断顶层Activity
    List<ActivityManager.RunningTaskInfo> 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;
}
代码语言:javascript
复制
class RefreshTask extends TimerTask {

    @Override
    public void run() {
        // 当前界面是要显示的界面,且没有悬浮窗显示,则创建悬浮窗。
        if (isForeground(activityname) && !MyWindowManager.isWindowShowing()) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    MyWindowManager.createSmallWindow(getApplicationContext());
                }
            });
        }
        // 当前界面要显示的界面,且有悬浮窗显示,则移除悬浮窗。
        else if (!isForeground(activityname) && MyWindowManager.isWindowShowing()) {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    MyWindowManager.removeSmallWindow(getApplicationContext());
                    MyWindowManager.removeBigWindow(getApplicationContext());
                }
            });
        }
        // 当前界面是要显示的界面,且有悬浮窗显示,则更新内存数据。
        else if (isForeground(activityname) && MyWindowManager.isWindowShowing()) {
            handler.post(new Runnable() {
                @Override
                public 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的类型需要修改了下,否则进去后程序崩溃

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
语音识别
腾讯云语音识别(Automatic Speech Recognition,ASR)是将语音转化成文字的PaaS产品,为企业提供精准而极具性价比的识别服务。被微信、王者荣耀、腾讯视频等大量业务使用,适用于录音质检、会议实时转写、语音输入法等多个场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档