仿360悬浮窗——进阶篇

仿360悬浮窗——进阶篇

上一章讲了仿360基础篇,这一章在基础上做一下功能的拓展,如果你需要做成像360一样的东西,可以在我源码基础上修改即可,当然如果你做launcher开发,也可以做成苹果手机那样的悬浮快捷按钮,还是老规矩,先贴图(两种效果):
仿360悬浮窗——进阶篇_第1张图片仿360悬浮窗——进阶篇_第2张图片
这一次加了一个大的悬浮窗,用来处理复杂需求,我只是随便画了个圆形,你们可以做的更漂亮,功能更强大:
对于悬浮框的建立,我上一章已经见过,这里就不多做讲解,请参考:仿360悬浮窗基础版
因为有一大一小两个悬浮窗,我们不妨建立一个WindowManger来管理两个悬浮窗加载的状态,代码如下(注意SmallWindow和BigWindow创立时的差别):
public class FloatWindowManager {
    /**
     * 小悬浮窗
     */
    private static FloatWindowSmall smallWindow;

    /**
     * 大悬浮窗
     */
    private static FloatWindowBig bigWindow;

    /**
     * 小悬浮窗的params
     */
    private static LayoutParams smallWindowParams;

    /**
     * 大悬浮窗的params
     */
    private static LayoutParams bigWindowParams;

    /**
     * 用于控制在屏幕上添加或移除悬浮窗
     */
    private static WindowManager mWindowManager;

    /**
     * 创建一个小悬浮窗。初始位置为屏幕的右部中间位置。
     *
     * @param context 必须为应用程序的Context.
     */
    public static void createSmallWindow(Context context) {
        WindowManager windowManager = getWindowManager(context);
        DisplayMetrics dm = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(dm);
        if (smallWindow == null) {
            //这里必须先初始化小悬浮窗
            smallWindow = new FloatWindowSmall(context);
            if (smallWindowParams == null) {
                smallWindowParams = new LayoutParams();
                smallWindowParams.type = LayoutParams.TYPE_PHONE;
                smallWindowParams.format = PixelFormat.RGBA_8888;
                //如何不加这个,则会出现它一直霸占焦点,其他点击事件失效,切记
                smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | LayoutParams.FLAG_NOT_FOCUSABLE;
                smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;//这里相当于确定起点位置
                smallWindowParams.width = FloatWindowSmall.viewWidth;
                smallWindowParams.height = FloatWindowSmall.viewHeight;
                smallWindowParams.x = dm.widthPixels;
                smallWindowParams.y = dm.heightPixels / 2 - smallWindowParams.height / 2;
            }
            smallWindow.setParams(smallWindowParams);
            windowManager.addView(smallWindow, smallWindowParams);
        }
    }

    /**
     * 将小悬浮窗从屏幕上移除。
     *
     * @param context 必须为应用程序的Context.
     */
    public static void removeSmallWindow(Context context) {
        if (smallWindow != null) {
            WindowManager windowManager = getWindowManager(context);
            windowManager.removeView(smallWindow);
            smallWindow = null;
        }
    }

    /**
     * 创建一个大悬浮窗。位置根据小悬浮窗确定。
     *
     * @param context 必须为应用程序的Context.
     */
    public static void createBigWindow(Context context) {
        WindowManager windowManager = getWindowManager(context);
        DisplayMetrics dm = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(dm);
        if (bigWindow == null) {
            //这里必须先初始化大悬浮窗
            bigWindow = new FloatWindowBig(context);
        }
        //参数是变动的,所以每一次开启都必须更新
        bigWindowParams = new LayoutParams();
        bigWindowParams.type = LayoutParams.TYPE_PHONE;
        bigWindowParams.format = PixelFormat.RGBA_8888;
        bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
        bigWindowParams.width = FloatWindowBig.viewWidth;
        bigWindowParams.height = FloatWindowBig.viewHeight;
        //如何不加这个,则会出现它一直霸占焦点,其他点击事件失效,切记
        bigWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
                | LayoutParams.FLAG_NOT_FOCUSABLE;
        //这里是根据小悬浮窗的位置来确定大悬浮窗的位置,当然我们这里强制聚焦,所以屏蔽掉
//        bigWindowParams.x = FloatWindowSmall.smallParams.x + FloatWindowSmall.viewWidth -
//                bigWindowParams.width;
//        bigWindowParams.y = FloatWindowSmall.smallParams.y + FloatWindowSmall.viewHeight / 2
//                - bigWindowParams.height / 2;
        //如果你想实现类似苹果手机的快捷操作悬浮窗效果,可以让他显示在中间位置
        bigWindowParams.x = dm.widthPixels/2 - bigWindowParams.width/2;
        bigWindowParams.y = dm.heightPixels/2 - bigWindowParams.height /2;
        windowManager.addView(bigWindow, bigWindowParams);
    }

    /**
     * 将大悬浮窗从屏幕上移除。
     *
     * @param context 必须为应用程序的Context.
     */
    public static void removeBigWindow(Context context) {
        if (bigWindow != null) {
            WindowManager windowManager = getWindowManager(context);
            windowManager.removeView(bigWindow);
            bigWindow = null;
        }
    }

    /**
     * 更新小悬浮窗的TextView上的数据,显示内存使用的百分比。
     */
    public static void updateUsedPercent() {
        if (smallWindow != null) {
            TextView percentView = (TextView) smallWindow.findViewById(R.id.percent);
            percentView.setText(smallWindow.getUsedPercentValue());
        }
    }

    /**
     * 是否有悬浮窗(包括小悬浮窗和大悬浮窗)显示在屏幕上。
     *
     * @return 有悬浮窗显示在桌面上返回true,没有的话返回false。
     */
    public static boolean isWindowShowing() {
        return smallWindow != null || bigWindow != null;
    }

    /**
     * @param context 必须为getApplicationContext().
     * @return WindowManager的实例
     */
    private static WindowManager getWindowManager(Context context) {
        if (mWindowManager == null) {
            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        }
        return mWindowManager;
    }
}
至于悬浮窗,一大一小,小的上一章见过了,就不贴代码呢,我会上传整个源码,所以不要担心学不会,主要我不喜欢我的博客篇幅太长,还是讲思路和主要代码,大悬浮窗,代码很简单:
public class FloatWindowBig extends LinearLayout {
	/**
	 * 记录大悬浮窗的宽度
	 */
	public static int viewWidth;

	/**
	 * 记录大悬浮窗的高度
	 */
	public static int viewHeight;
	public FloatWindowBig(final Context context) {
		super(context);
		LayoutInflater.from(context).inflate(R.layout.float_window_big, this);
		View view = findViewById(R.id.big_window_layout);
		viewWidth = view.getLayoutParams().width;
		viewHeight = view.getLayoutParams().height;
		TextView close = (TextView) findViewById(R.id.close);
		TextView back = (TextView) findViewById(R.id.back);
		close.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// 点击关闭悬浮窗的时候,移除所有悬浮窗,并停止Service
				FloatWindowManager.removeBigWindow(context);
				FloatWindowManager.removeSmallWindow(context);
				Intent intent = new Intent(getContext(), FloatWindowService.class);
				context.stopService(intent);
			}
		});
		back.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// 点击返回的时候,移除大悬浮窗,创建小悬浮窗
				FloatWindowManager.removeBigWindow(context);
				FloatWindowManager.createSmallWindow(context);
			}
		});
	}
}
最后就是service部分,直接调用 FloatWindowManager的方法创建大小悬浮窗即可,这里需要做的是判断用户界面是否在laucher,当然如果模仿苹果手机的功能悬浮快捷按钮则不需要,代码如下:
	/**
	 * 判断当前界面是否是桌面
	 */
	private boolean isHome() {
		ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
		List rti = mActivityManager.getRunningTasks(1);
		return getHomes().contains(rti.get(0).topActivity.getPackageName());
	}
	/**
	 * 获得属于桌面的应用的应用包名称
	 *
	 * @return 返回包含所有包名的字符串列表
	 */
	private List getHomes() {
		List names = new ArrayList();
		PackageManager packageManager = this.getPackageManager();
		Intent intent = new Intent(Intent.ACTION_MAIN);
		intent.addCategory(Intent.CATEGORY_HOME);
		List resolveInfo = packageManager.queryIntentActivities(intent,
				PackageManager.MATCH_DEFAULT_ONLY);
		for (ResolveInfo ri : resolveInfo) {
			names.add(ri.activityInfo.packageName);
		}
		return names;
	}
因为只是针对上一章做一下功能拓展,方便大家用于自己项目,所以没有太多思路可讲,效果图已经呈现,相信大家最需要的还是源码: 360悬浮窗——进阶篇








你可能感兴趣的:(WindowManager)