PS:看了9年的小说,自己开始动手写了一本,请各位猿们动动手指,点击下,有起点账号的可以收藏下!!《武意长存》
前两周公司接了个运行在车机上的行车记录仪小项目,在做完提交给对方公司测试后,他们提出了当程序在后台录像时,需要在状态栏上显示个闪烁的图标给用户进行提示。
好吧,虽然对方给的需求文档上没这个功能,但既然提了那就做吧。
要想完成这个功能,我首先想到的就是利用Notification,我们只需要循环的发送图标不同的Notification就行实现,是不是很简单。当然最后我才发现这个方法在手机实现闪烁图标提示没问题,但在车机上却不管用,这个后面再讲。还是先来先讲讲使用Notification如何实现
界面很简单就两个按钮,一个用来开启闪烁提示,一个用来关闭。主要实现代码放置服务中,界面如下
按钮点击执行代码:
@Override
public void onClick(View v) {
Intent service = new Intent(this, MyService.class);
switch (v.getId()) {
case R.id.btn_start:
startService(service);
break;
case R.id.btn_stop:
stopService(service);
break;
}
}
MyService.java
public class MyService extends Service {
private static final int MSG_ADD_ICON = 400;
private static final int NOTIFICATION_ID_ICON = 666;
private NotificationManager nm;
private Notification notification;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_ADD_ICON:
boolean flag = (Boolean) msg.obj;
addIconToStatusbar(flag);
break;
}
};
};
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
addIconToStatusbar(true);
}
@Override
public void onDestroy() {
mHandler.removeMessages(MSG_ADD_ICON);
nm.cancel(NOTIFICATION_ID_ICON);
super.onDestroy();
}
@SuppressLint("NewApi")
private void addIconToStatusbar(boolean isShow) {
if (notification == null) {
notification = new Notification();
notification.icon = R.drawable.notification;
notification.iconLevel = Integer.MAX_VALUE;
// 将此通知放到通知栏的"Ongoing"即"正在运行"组中
notification.flags |= Notification.FLAG_ONGOING_EVENT;
notification.flags |= Notification.FLAG_INSISTENT;
notification.flags |= Notification.FLAG_HIGH_PRIORITY;
// 表明在点击了通知栏中的"清除通知"后,此通知不清除,
// 经常与FLAG_ONGOING_EVENT一起使用
notification.flags |= Notification.FLAG_NO_CLEAR;
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
notification.contentIntent = pendingIntent;
notification.setLatestEventInfo(this, "DudooDVR", "正在录像", pendingIntent);
}
if (isShow) {
notification.icon = R.drawable.notification;
} else {
notification.icon = R.drawable.notification2;
}
nm.notify(NOTIFICATION_ID_ICON, notification);
Message msg = Message.obtain();
msg.what = MSG_ADD_ICON;
msg.obj = !isShow;
mHandler.sendMessageDelayed(msg, 800);
}
}
代码其实很简单,就是利用handler,循环发送延迟消息,每次只需要根据isShow标记替换图标即可,这里我准备的是一张红色圆点小图标和一张一样大小的透明图标。
效果如下:
如果只是在手机上使用,这样就完成了,但悲催的是在车机上一运行,根本就没效果。
原来车机上Notification并不会显示在状态栏上,只有当你下拉状态栏,然后点击通知按钮才能进入查看通知。当然这有可能只是我们要运行的这款车机是这种情况,没办法,都是定制过的系统。(由于写这篇文章是在宿舍,没办法截图车机上的效果,大家自己想象,哈哈...)
好吧,本以为轻松搞定,没想到代码白敲了。郁闷归郁闷,功能还是要实现的,那该怎么来实现了呢,我能想到的就是利用WindowManager往屏幕上添加一个全局的view,当然有可能还有其他更好的实现方法。
不过这里得做个提醒,这个方法用在手机不好,会和其他通知重叠,只能用在我上面提到的状态不显示Notification的情况下。好吧,废话不好多说,上代码吧
public class RedPoint {
private static final int FLASH_DELAY_TIME = 1000;
private static final int MSG_FLASH = 1;
public static final int REDPOINT_HEIGHT = 15;
public static final int REDPOINT_POSITION_X = 15;
public static final int REDPOINT_POSITION_Y = 7;
public static final int REDPOINT_WIDTH = 15;
private static boolean hasRedpoint = false;
private static RedPoint mRedPoint;
private static WindowManager redPointWm;
boolean isShouldFlash;
private Context mContext;
private WindowManager.LayoutParams redPointParams;
private View redpointFramelayout;
private Handler mHandler = new Handler() {
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
switch (msg.what) {
case MSG_FLASH: {
if (hasRedpoint) {
redpointFramelayout.setVisibility(isShouldFlash ? View.VISIBLE : View.INVISIBLE);
isShouldFlash = (!isShouldFlash);
mHandler.sendEmptyMessageDelayed(MSG_FLASH, FLASH_DELAY_TIME);
break;
}
}
}
}
};
public RedPoint(Context paramContext) {
this.mContext = paramContext;
init();
}
public static RedPoint getInstance(Context context) {
synchronized (RedPoint.class) {
if (mRedPoint == null) {
mRedPoint = new RedPoint(context);
}
}
return mRedPoint;
}
private void init() {
redPointWm = (WindowManager) this.mContext.getSystemService("window");
initRedPointWm();
}
private void initRedPointWm() {
this.redPointParams = new WindowManager.LayoutParams();
// 期望的位图格式。默认为不透明。参考android.graphics.PixelFormat。
// 1 表示 RGBA_8888
this.redPointParams.format = 1;
this.redPointParams.width = 30;
this.redPointParams.height = 30;
this.redPointParams.x = 15;
this.redPointParams.y = 7;
// Gravity.AXIS_PULL_BEFORE | Gravity.AXIS_SPECIFIED | Gravity.TOP 这个值等于51
this.redPointParams.gravity = Gravity.AXIS_PULL_BEFORE | Gravity.AXIS_SPECIFIED | Gravity.TOP;
this.redPointParams.type = LayoutParams.TYPE_SYSTEM_OVERLAY; // 设置窗口的级别
// 312 相当于
// LayoutParams.FLAG_LAYOUT_IN_SCREEN | LayoutParams.FLAG_NOT_FOCUSABLE
// | LayoutParams.FLAG_NOT_TOUCHABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL;
this.redPointParams.flags = 312;
this.redpointFramelayout = View.inflate(this.mContext, R.layout.redpoint, null);
}
private void startFlash(boolean isFlash) {
if (isFlash) {
isShouldFlash = true;
mHandler.sendEmptyMessage(MSG_FLASH);
return;
}
mHandler.removeMessages(MSG_FLASH);
}
public void addRedPointWm() {
if (!hasRedpoint) {
try {
redPointWm.addView(redpointFramelayout, redPointParams);
hasRedpoint = true;
startFlash(true);
return;
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void removeRedPointWm() {
if (hasRedpoint) {
startFlash(false);
try {
redPointWm.removeView(redpointFramelayout);
} catch (Exception e) {
e.printStackTrace();
}
hasRedpoint = false;
}
mHandler.removeCallbacksAndMessages(MSG_FLASH);
}
}
RedPoint 类用于闪烁图标的管理,其实还是利用handler发送延迟消息,循环的显示和隐藏添加到屏幕上的view。
我们只需要在服务开启的时候调用addRedPointWm方法,把view添加到屏幕中,然后在关闭服务时调用removeRedPointWm方法就可以了。至于添加时设置的一些参数注释也写得挺清楚了,不清楚的请自行百度或者谷歌
对了,由于我们设置成的是系统级别的window,所以别忘了添加上权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
由于在宿舍没车机,就用大概看下手机实现后的效果吧
demo下载:FlickerIconDemo