Notification弹出实现
Notification的几种基本使用方法,大家肯定都已经烂熟于心,我也不必多说.给一个链接:https://zhuanlan.zhihu.com/p/25841482
接下来我想说的是android5.0 后的弹出通知,
网上的方法是:
//第一步:实例化通知栏构造器Notification.Builder:
Notification.Builder builder =new Notification.Builder(MainActivity.this);//实例化通知栏构造器Notification.Builder,参数必填(Context类型),为创建实例的上下文 //第二步:获取状态通知栏管理: NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);//获取状态栏通知的管理类(负责发通知、清除通知等操作) //第三步:设置通知栏PendingIntent(点击动作事件等都包含在这里): Intent push =new Intent(MainActivity.this,MainActivity.class);//新建一个显式意图,第一个参数 Context 的解释是用于获得 package name,以便找到第二个参数 Class 的位置 //PendingIntent可以看做是对Intent的包装,通过名称可以看出PendingIntent用于处理即将发生的意图,而Intent用来用来处理马上发生的意图 //本程序用来响应点击通知的打开应用,第二个参数非常重要,点击notification 进入到activity, 使用到pendingIntent类方法,PengdingIntent.getActivity()的第二个参数,即请求参数,实际上是通过该参数来区别不同的Intent的,如果id相同,就会覆盖掉之前的Intent了 PendingIntent contentIntent = PendingIntent.getActivity(MainActivity.this,0,push,0); //第四步:对Builder进行配置: builder .setContentTitle("My notification")//标题 .setContentText("Hello World!")// 详细内容 .setContentIntent(contentIntent)//设置点击意图 .setTicker("New message")//第一次推送,角标旁边显示的内容 .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher))//设置大图标 .setDefaults(Notification.DEFAULT_ALL);//打开呼吸灯,声音,震动,触发系统默认行为 /*Notification.DEFAULT_VIBRATE //添加默认震动提醒 需要VIBRATE permission Notification.DEFAULT_SOUND //添加默认声音提醒 Notification.DEFAULT_LIGHTS//添加默认三色灯提醒 Notification.DEFAULT_ALL//添加默认以上3种全部提醒*/ //.setLights(Color.YELLOW, 300, 0)//单独设置呼吸灯,一般三种颜色:红,绿,蓝,经测试,小米支持黄色 //.setSound(url)//单独设置声音 //.setVibrate(new long[] { 100, 250, 100, 250, 100, 250 })//单独设置震动 //比较手机sdk版本与Android 5.0 Lollipop的sdk if(android.os.Build.VERSION.SDK_INT>= android.os.Build.VERSION_CODES.LOLLIPOP) { builder /*android5.0加入了一种新的模式Notification的显示等级,共有三种: VISIBILITY_PUBLIC只有在没有锁屏时会显示通知 VISIBILITY_PRIVATE任何情况都会显示通知 VISIBILITY_SECRET在安全锁和没有锁屏的情况下显示通知*/ .setVisibility(Notification.VISIBILITY_PUBLIC) .setPriority(Notification.PRIORITY_DEFAULT)//设置该通知优先级 .setCategory(Notification.CATEGORY_MESSAGE)//设置通知类别 //.setColor(context.getResources().getColor(R.color.small_icon_bg_color))//设置smallIcon的背景色 .setFullScreenIntent(contentIntent, true)//将Notification变为悬挂式Notification .setSmallIcon(R.drawable.ic_launcher);//设置小图标 } else{ builder .setSmallIcon(R.drawable.ic_launcher);//设置小图标 } //第五步:发送通知请求: Notification notify = builder.build();//得到一个Notification对象 mNotifyMgr.notify(1,notify);//发送通知请求 }
但上面的做法并不能在android5.0以下的设备上使通知弹出,因此下面的做法是自己重写Notification(网上查找的一些资料,来源忘记了,不好意思)
如果需要使通知自动显示,那么就需要我们在接收到通知后重新定义通知的界面,并使其加载显示在Window界面上,这点需要读者了解Window的加载机制.
其实简单点来说,就是通过windowManager的仅有的三个方法(加载,更新,删除)来实现的.如果有大神熟悉这方面的知识可以分享分享.
自定义Notification的思路:
1.继承重写NotificationCompat,Builder来实现类似的Notification
2.自定义通知界面
3.自定义NotificationManager,发送显示通知
废话不多说,先上主要代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class HeadsUp { 2 3 private Context context; 4 /** 5 * 出现时间 单位是 second 6 */ 7 private long duration= 3; 8 /** 9 * 10 */ 11 private Notification notification; 12 13 private Builder builder; 14 15 private boolean isSticky=false; 16 17 18 private boolean activateStatusBar=true; 19 20 private Notification silencerNotification; 21 /** 22 * 间隔时间 23 */ 24 private int code; 25 private CharSequence titleStr; 26 private CharSequence msgStr; 27 private int icon; 28 private View customView; 29 private boolean isExpand; 30 private HeadsUp(Context context) { 31 this.context=context; 32 } 33 public static class Builder extends NotificationCompat.Builder { 34 private HeadsUp headsUp; 35 public Builder(Context context) { 36 super(context); 37 headsUp=new HeadsUp(context); 38 } 39 public Builder setContentTitle(CharSequence title) { 40 headsUp.setTitle(title); 41 super.setContentTitle(title); //状态栏显示内容 42 return this; 43 } 44 public Builder setContentText(CharSequence text) { 45 headsUp.setMessage(text); 46 super.setContentText(text); 47 return this; 48 } 49 public Builder setSmallIcon(int icon) { 50 headsUp.setIcon(icon); 51 super.setSmallIcon(icon); 52 return this; 53 } 54 public HeadsUp buildHeadUp(){ 55 headsUp.setNotification(this.build()); 56 headsUp.setBuilder(this); 57 return headsUp; 58 } 59 public Builder setSticky(boolean isSticky){ 60 headsUp.setSticky(isSticky); 61 return this; 62 } 63 } 64 65 public Context getContext() { 66 return context; 67 } 68 69 public long getDuration() { 70 return duration; 71 } 72 73 public Notification getNotification() { 74 return notification; 75 } 76 77 protected void setNotification(Notification notification) { 78 this.notification = notification; 79 } 80 81 public View getCustomView() { 82 return customView; 83 } 84 85 public void setCustomView(View customView) { 86 this.customView = customView; 87 } 88 89 public int getCode() { 90 return code; 91 } 92 93 protected void setCode(int code) { 94 this.code = code; 95 } 96 97 protected Builder getBuilder() { 98 return builder; 99 } 100 101 private void setBuilder(Builder builder) { 102 this.builder = builder; 103 } 104 105 106 public boolean isSticky() { 107 return isSticky; 108 } 109 110 public void setSticky(boolean isSticky) { 111 this.isSticky = isSticky; 112 } 113 114 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class HeadsUpManager { 2 3 private WindowManager wmOne; 4 private FloatView floatView; 5 private Queue<HeadsUp> msgQueue; 6 private static HeadsUpManager manager; 7 private Context context; 8 9 private boolean isPolling = false; 10 11 private Map<Integer, HeadsUp> map; 12 private NotificationManager notificationManager=null; 13 14 public static HeadsUpManager getInstant(Context c) { 15 16 if (manager == null) { 17 manager = new HeadsUpManager(c); 18 19 } 20 return manager; 21 22 } 23 24 private HeadsUpManager(Context context) { 25 this.context = context; 26 map = new HashMap<Integer, HeadsUp>(); 27 msgQueue = new LinkedList<HeadsUp>(); 28 wmOne = (WindowManager) context 29 .getSystemService(Context.WINDOW_SERVICE); 30 31 notificationManager= (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 32 } 33 34 public void notify(HeadsUp headsUp) { 35 36 37 if (map.containsKey(headsUp.getCode())) { 38 msgQueue.remove(map.get(headsUp.getCode())); 39 } 40 map.put(headsUp.getCode(), headsUp); 41 msgQueue.add(headsUp); 42 43 if (!isPolling) { 44 poll(); 45 } 46 } 47 public synchronized void notify(int code,HeadsUp headsUp) { 48 headsUp.setCode(code); 49 notify(headsUp); 50 51 } 52 public synchronized void cancel(HeadsUp headsUp) { 53 cancel(headsUp.getCode()); 54 } 55 56 57 private synchronized void poll() { 58 if (!msgQueue.isEmpty()) { 59 HeadsUp headsUp = msgQueue.poll(); 60 map.remove(headsUp.getCode()); 61 62 // if ( Build.VERSION.SDK_INT < 21 || headsUp.getCustomView() != null ){ 63 isPolling = true; 64 show(headsUp); 65 System.out.println("自定义notification"); 66 // }else { 67 // //当 系统是 lollipop 以上,并且没有自定义布局以后,调用系统自己的 notification 68 // isPolling = false; 69 // notificationManager.notify(headsUp.getCode(),headsUp.getBuilder().setSmallIcon(headsUp.getIcon()).build()); 70 // System.out.println("调用系统notification"); 71 // } 72 73 } else { 74 isPolling = false; 75 } 76 } 77 private void show(HeadsUp headsUp) { 78 floatView = new FloatView(context, 20); 79 WindowManager.LayoutParams params = FloatView.winParams; 80 params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 81 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 82 |WindowManager.LayoutParams.FLAG_FULLSCREEN 83 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 84 params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; 85 params.width = WindowManager.LayoutParams.MATCH_PARENT; 86 params.height = WindowManager.LayoutParams.WRAP_CONTENT; 87 params.format = -3; 88 params.gravity = Gravity.CENTER | Gravity.TOP; 89 params.x = floatView.originalLeft; 90 params.y = 10; 91 params.alpha = 1f; 92 wmOne.addView(floatView, params); 93 ObjectAnimator a = ObjectAnimator.ofFloat(floatView.rootView, "translationY", -700, 0); 94 a.setDuration(600); 95 a.start(); 96 floatView.setNotification(headsUp); 97 if(headsUp.getNotification()!=null){ 98 notificationManager.notify(headsUp.getCode(), headsUp.getNotification()); 99 } 100 } 101 102 public void cancel(){ 103 104 if(floatView !=null && floatView.getParent()!=null) { 105 106 floatView.cancel(); 107 } 108 } 109 110 protected void dismiss() { 111 if (floatView.getParent()!=null) { 112 wmOne.removeView(floatView); 113 floatView.postDelayed(new Runnable() { 114 @Override 115 public void run() { 116 poll(); 117 } 118 }, 200); 119 } 120 121 } 122 123 protected void animDismiss(){ 124 if(floatView !=null && floatView.getParent()!=null){ 125 126 ObjectAnimator a = ObjectAnimator.ofFloat(floatView.rootView, "translationY", 0, -700); 127 a.setDuration(700); 128 a.start(); 129 130 a.addListener(new Animator.AnimatorListener() { 131 @Override 132 public void onAnimationStart(Animator animator) { 133 134 } 135 @Override 136 public void onAnimationEnd(Animator animator) { 137 138 dismiss(); 139 } 140 @Override 141 public void onAnimationCancel(Animator animator) { 142 } 143 @Override 144 public void onAnimationRepeat(Animator animator) { 145 146 } 147 }); 148 } 149 150 } 151 152 protected void animDismiss(HeadsUp headsUp){ 153 if(floatView.getHeadsUp().getCode()==headsUp.getCode()){ 154 animDismiss(); 155 } 156 157 } 158 public void cancel(int code) { 159 if (map.containsKey(code)) { 160 msgQueue.remove(map.get(code)); 161 } 162 if(floatView!=null && floatView.getHeadsUp().getCode()==code){ 163 animDismiss(); 164 } 165 166 } 167 public void close() { 168 cancelAll(); 169 manager = null; 170 } 171 public void cancelAll() { 172 msgQueue.clear(); 173 if (floatView!=null && floatView.getParent()!=null) { 174 animDismiss(); 175 } 176 } 177 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class FloatView extends LinearLayout { 2 private float rawX = 0; 3 private float rawY=0; 4 private float touchX = 0; 5 private float startY = 0; 6 public LinearLayout rootView; 7 public int originalLeft; 8 public int viewWidth; 9 private float validWidth; 10 private VelocityTracker velocityTracker; 11 private int maxVelocity; 12 private Distance distance; 13 14 private ScrollOrientationEnum scrollOrientationEnum=ScrollOrientationEnum.NONE; 15 16 public static WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(); 17 18 public FloatView(final Context context, int i) { 19 super(context); 20 LinearLayout view = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.notification_bg, null); 21 maxVelocity= ViewConfiguration.get(context).getScaledMaximumFlingVelocity(); 22 rootView = (LinearLayout) view.findViewById(R.id.rootView); 23 addView(view); 24 viewWidth = context.getResources().getDisplayMetrics().widthPixels; 25 validWidth=viewWidth/2.0f; 26 originalLeft = 0; 27 28 } 29 30 public void setCustomView(View view) { 31 rootView.addView(view); 32 } 33 34 35 @Override 36 protected void onFinishInflate() { 37 super.onFinishInflate(); 38 } 39 40 41 private HeadsUp headsUp; 42 private long cutDown; 43 private Handler mHandle=null; 44 private CutDownTime cutDownTime; 45 private class CutDownTime extends Thread{ 46 47 @Override 48 public void run() { 49 super.run(); 50 51 52 while (cutDown>0){ 53 try { 54 Thread.sleep(1000); 55 cutDown--; 56 } catch (InterruptedException e) { 57 e.printStackTrace(); 58 } 59 } 60 61 if(cutDown==0) { 62 mHandle.sendEmptyMessage(0); 63 } 64 65 66 } 67 }; 68 69 70 71 public HeadsUp getHeadsUp() { 72 return headsUp; 73 } 74 75 private int pointerId; 76 public boolean onTouchEvent(MotionEvent event) { 77 rawX = event.getRawX(); 78 rawY=event.getRawY(); 79 acquireVelocityTracker(event); 80 cutDown= headsUp.getDuration(); 81 switch (event.getAction()) { 82 case MotionEvent.ACTION_DOWN: 83 touchX = event.getX(); 84 startY = event.getRawY(); 85 pointerId=event.getPointerId(0); 86 break; 87 case MotionEvent.ACTION_MOVE: 88 switch (scrollOrientationEnum){ 89 case NONE: 90 if(Math.abs((rawX - touchX))>20) { 91 scrollOrientationEnum=ScrollOrientationEnum.HORIZONTAL; 92 93 }else if(startY-rawY>20){ 94 scrollOrientationEnum=ScrollOrientationEnum.VERTICAL; 95 96 } 97 98 break; 99 case HORIZONTAL: 100 updatePosition((int) (rawX - touchX)); 101 break; 102 case VERTICAL: 103 if(startY-rawY>20) { 104 cancel(); 105 } 106 break; 107 } 108 109 break; 110 case MotionEvent.ACTION_UP: 111 velocityTracker.computeCurrentVelocity(1000,maxVelocity); 112 int dis= (int) velocityTracker.getYVelocity(pointerId); 113 if(scrollOrientationEnum==ScrollOrientationEnum.NONE){ 114 if(headsUp.getNotification().contentIntent!=null){ 115 116 try { 117 headsUp.getNotification().contentIntent.send(); 118 cancel(); 119 } catch (PendingIntent.CanceledException e) { 120 e.printStackTrace(); 121 } 122 } 123 break; 124 } 125 126 127 int toX; 128 if(preLeft>0){ 129 toX= (int) (preLeft+Math.abs(dis)); 130 }else{ 131 toX= (int) (preLeft-Math.abs(dis)); 132 } 133 if (toX <= -validWidth) { 134 float preAlpha=1-Math.abs(preLeft)/validWidth; 135 preAlpha=preAlpha>=0?preAlpha:0; 136 translationX(preLeft,-(validWidth+10),preAlpha,0); 137 } else if (toX <= validWidth) { 138 float preAlpha=1-Math.abs(preLeft)/validWidth; 139 preAlpha=preAlpha>=0?preAlpha:0; 140 translationX(preLeft,0,preAlpha,1); 141 142 }else{ 143 float preAlpha=1-Math.abs(preLeft)/validWidth; 144 preAlpha=preAlpha>=0?preAlpha:0; 145 translationX(preLeft, validWidth + 10, preAlpha, 0); 146 } 147 preLeft = 0; 148 scrollOrientationEnum=ScrollOrientationEnum.NONE; 149 break; 150 } 151 152 return super.onTouchEvent(event); 153 154 } 155 /** 156 * 157 * @param event 向VelocityTracker添加MotionEvent 158 * 159 * @see android.view.VelocityTracker#obtain() 160 * @see android.view.VelocityTracker#addMovement(MotionEvent) 161 */ 162 private void acquireVelocityTracker( MotionEvent event) { 163 if(null == velocityTracker) { 164 velocityTracker = VelocityTracker.obtain(); 165 } 166 velocityTracker.addMovement(event); 167 } 168 private int preLeft; 169 170 public void updatePosition(int left) { 171 172 float preAlpha=1-Math.abs(preLeft)/validWidth; 173 float leftAlpha=1-Math.abs(left)/validWidth; 174 preAlpha = preAlpha>=0 ? preAlpha : 0; 175 leftAlpha = leftAlpha>=0 ? leftAlpha : 0; 176 translationX(preLeft,left,preAlpha,leftAlpha); 177 178 preLeft = left; 179 } 180 181 public void translationX(float fromX,float toX,float formAlpha, final float toAlpha ){ 182 ObjectAnimator a1=ObjectAnimator.ofFloat(rootView,"alpha",formAlpha,toAlpha); 183 ObjectAnimator a2 = ObjectAnimator.ofFloat(rootView, "translationX", fromX, toX); 184 AnimatorSet animatorSet=new AnimatorSet(); 185 animatorSet.playTogether(a1,a2); 186 animatorSet.addListener(new Animator.AnimatorListener() { 187 @Override 188 public void onAnimationStart(Animator animation) { 189 } 190 191 @Override 192 public void onAnimationEnd(Animator animation) { 193 if(toAlpha==0){ 194 HeadsUpManager.getInstant(getContext()).dismiss(); 195 196 cutDown=-1; 197 if(velocityTracker!=null) { 198 velocityTracker.clear(); 199 try { 200 velocityTracker.recycle(); 201 } catch (IllegalStateException e) { 202 203 } 204 } 205 206 } 207 } 208 209 @Override 210 public void onAnimationCancel(Animator animation) { 211 } 212 213 @Override 214 public void onAnimationRepeat(Animator animation) { 215 } 216 }); 217 animatorSet.start(); 218 } 219 220 public void setNotification(final HeadsUp headsUp) { 221 222 this.headsUp = headsUp; 223 224 mHandle= new Handler(){ 225 @Override 226 public void handleMessage(Message msg) { 227 super.handleMessage(msg); 228 HeadsUpManager.getInstant(getContext()).animDismiss(headsUp); 229 } 230 }; 231 232 233 234 cutDownTime= new CutDownTime(); 235 236 if(!headsUp.isSticky()){ 237 cutDownTime.start(); 238 } 239 240 241 cutDown= headsUp.getDuration(); 242 243 if (headsUp.getCustomView() == null) { 244 245 View defaultView = LayoutInflater.from(getContext()).inflate(R.layout.notification, rootView, false); 246 rootView.addView(defaultView); 247 ImageView imageView = (ImageView) defaultView.findViewById(R.id.iconIM); 248 TextView titleTV = (TextView) defaultView.findViewById(R.id.titleTV); 249 TextView timeTV = (TextView) defaultView.findViewById(R.id.timeTV); 250 TextView messageTV = (TextView) defaultView.findViewById(R.id.messageTV); 251 imageView.setImageResource(headsUp.getIcon()); 252 titleTV.setText(headsUp.getTitleStr()); 253 messageTV.setText(headsUp.getMsgStr()); 254 SimpleDateFormat simpleDateFormat=new SimpleDateFormat("HH:mm"); 255 timeTV.setText( simpleDateFormat.format(new Date())); 256 257 258 } else { 259 setCustomView(headsUp.getCustomView()); 260 } 261 262 } 263 264 265 protected void cancel(){ 266 HeadsUpManager.getInstant(getContext()).animDismiss(); 267 cutDown = -1; 268 cutDownTime.interrupt(); 269 270 271 if(velocityTracker!=null) { 272 try { 273 velocityTracker.clear(); 274 velocityTracker.recycle(); 275 } catch (IllegalStateException e) { 276 277 } 278 } 279 } 280 281 282 enum ScrollOrientationEnum { 283 VERTICAL,HORIZONTAL,NONE 284 } 285 }
具体用法:
PendingIntent pendingIntent=PendingIntent.getActivity(MainActivity.this,11,new Intent(MainActivity.this,MainActivity.class),PendingIntent.FLAG_UPDATE_CURRENT); View view=getLayoutInflater().inflate(R.layout.custom_notification, null); view.findViewById(R.id.openSource).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } }); HeadsUpManager manage = HeadsUpManager.getInstant(getApplication()); HeadsUp.Builder builder = new HeadsUp.Builder(MainActivity.this); builder.setContentTitle("提醒") //要显示通知栏通知,这个一定要设置 .setSmallIcon(R.drawable.icon) .setContentText("你有新的消息") //2.3 一定要设置这个参数,负责会报错 .setContentIntent(pendingIntent) //.setFullScreenIntent(pendingIntent, false) HeadsUp headsUp = builder.buildHeadUp(); headsUp.setCustomView(view); manage.notify(1, headsUp); }
目前先这样吧,以后继续更新。。。