项目需要用到调节音量及亮度,效果类似iPhone。需求点击增加音量键后现实音量界面并增加下方小格子,用户不再点击调节音量后音量界面会在4s后消失。
整个功能用Service实现,在Service中创建亮度调节界面(通过WindowManager)。在主线程中提供两个按钮来绑定服务,当服务已经绑定时直接调用Bind对象的setLight方法来设置亮度。
实现调节亮度界面的Service
重写了onCreate方法,并在该方法内调用了方法createFloatView。方法createFloatView的作用是创建音量界面。
private void createFloatView() {
wmParams = new WindowManager.LayoutParams();
//获取WindowManagerImpl.CompatModeWrapper
mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
//设置window type
wmParams.type = android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
//设置图片格式,效果为背景透明
wmParams.format = PixelFormat.TRANSLUCENT;
//整个窗口的半透明值,1.0表示不透明,0.0表示全透明。
wmParams.alpha = 0.9f;
//当FLAG_DIM_BEHIND设置后生效。该变量指示后面的窗口变暗的程度。
//1.0表示完全不透明,0.0表示没有变暗。
wmParams.dimAmount = 0.5f;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
wmParams.flags =
android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
//调整悬浮窗显示的停靠位置为左侧置顶
wmParams.gravity = Gravity.CENTER;
// 以屏幕左上角为原点,设置x、y初始值
wmParams.x = 0;
wmParams.y = 0;
// 设置悬浮窗口长宽数据
wmParams.width = 300;
wmParams.height = 300;
// LayoutInflater inflater = LayoutInflater.from(getApplication());
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//获取浮动窗口视图所在布局
mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
light1 = (ImageView) mFloatLayout.findViewById(R.id.light1);
light2 = (ImageView) mFloatLayout.findViewById(R.id.light2);
light3 = (ImageView) mFloatLayout.findViewById(R.id.light3);
light4 = (ImageView) mFloatLayout.findViewById(R.id.light4);
light5 = (ImageView) mFloatLayout.findViewById(R.id.light5);
light6 = (ImageView) mFloatLayout.findViewById(R.id.light6);
light7 = (ImageView) mFloatLayout.findViewById(R.id.light7);
light8 = (ImageView) mFloatLayout.findViewById(R.id.light8);
light9 = (ImageView) mFloatLayout.findViewById(R.id.light9);
light10 = (ImageView) mFloatLayout.findViewById(R.id.light10);
light11 = (ImageView) mFloatLayout.findViewById(R.id.light11);
light12 = (ImageView) mFloatLayout.findViewById(R.id.light12);
light13 = (ImageView) mFloatLayout.findViewById(R.id.light13);
setLight(defaultLightNumber);
//添加mFloatLayout
try {
mWindowManager.addView(mFloatLayout, wmParams);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
构建界面用到了WindowManager,WindowManager.LayoutParams,LinearLayout。
WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。
通过Context.getSystemService(Context.WINDOW_SERVICE)的方式可以获得WindowManager的实例.
WindowManager继承自ViewManager,里面涉及到窗口管理的三个重要方法,分别是:
- addView();
- updateViewLayout();
- removeView();
这里我用到了addView和removeView方法,分别是用来在onCreate()方法中添加View,和在onDestroy()方法中移除View。
在WindowManager中还有一个重要的静态类LayoutParams.通过它可以设置和获得当前窗口的一些属性。
我们先来看看addView()方法,在addView中,会利用LayoutParams获得window的View属性,并为每个window创建ViewRoot,ViewRoot是View和WindowManager之间的桥梁,真正把View传递给WindowManager的是通过ViewRoot的setView()方法,ViewRoot实现了View和WindowManager之间的消息传递。
主线程中调用Service方法
两个按钮中都是调用handleLight()方法来修改亮度变化。
handleLight()方法首先判断服务是否在绑定,如果不在绑定会进行绑定。如果还在绑定过程中直接调用Service中绑定对象的changeLight方法。然后在调用handleDelayDisppearWindowThread方法。
private void handleLight() {
if (IS_START_BINDER) {
mBinderService.changeLight(changeLight);
} else {
intent = new Intent(MainActivity.this, FxService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
IS_START_BINDER = true;
handleDelayDisppearWindowThread();
}
handleDelayDisppearWindowThread方法用来过4s后移除亮度调节界面。如果当界面还没移除前用户又点击按钮调节亮度会先截断移除界面的线程,在重新开启移除界面线程(重新开始计时)。
private void handleDelayDisppearWindowThread() {
if (delayDisappearWindowThread.isAlive()) {
try {
delayDisappearWindowThread.interrupt();
delayDisappearWindowThread.join();
} catch (Exception ex) {
ex.printStackTrace();
}
}
delayDisappearWindowThread = new DelayDisappearWindowThread();
delayDisappearWindowThread.start();
}