常驻通知栏
Notification
//使用Notification,需要注意在android8.0以上的系统中,需要定义channel,否则无法显示通知
mBuilder = new NotificationCompat.Builder(this, CHANNEL_TOOLS);
mBuilder.setSmallIcon(R.drawable.ic_notification); // 设置顶部图标(状态栏)
//需要实现自定义的布局,需要实现RemoteView,通过setContent()方法设置
mBuilder.setContent(remoteViews);
注意点:
1. 布局尽量不要限制死高度,在不同的Room下高度过大可能导致View超出通知框范围
2. 布局背景颜色需要考虑,尽量使用透明背景色,同时布局内其他组件也尽量保证在父布局透明时能够保持正常视图
3. 不能保证透明时较好的效果,则需要统一定死背景颜色,牺牲一定的room兼容(某些room获取通知栏颜色不准,会影响动态设置背景色)
RemoteView
自定义通知栏中,需要自定义通知栏的视图时,需要使用RemoteView定义视图,代码示例
//定义RemoteView视图
RemoteView remoteViews = new RemoteViews(getPackageName(), R.layout.layout_tools_unit_normal_white);
//需要注意,在RemoteView中使用的空间有比较严格的限制,仅支持有限的几种控件
//支持控件:
//布局
FrameLayout,LinearLayout,RelativeLayout,GridLayout
//控件
AnalogClock,Button,Chronometer,ImageButton,ImageView,ProgressBar,TextView,ViewFlipper,ListView,GridView,StackView,AdapterViewFlipper,ViewStub
注意点
1. 在RemoteView中自定义控件是莫得用的
2. 在RemoteView中没法直接拿到子View对象(可以通过方法操控)
RemoteView内的控件无法直接通过findViewById来获取,所以控制RemoteView中的组件需要通过提供的方法:
例如:
setTextViewText(int viewId, CharSequence text)
setImageViewResourse(int viewId, int resId)
setTextColor(int viewId, int color)
//实际上这一些方法的实现机制是通过反射完成的,所有RemoteView的子组件都通过RemoteView调用提供的方法来操作
常驻效果
//常驻效果可以通过这行代码实现
mBuilder.setOngoing(true);
小部件
小部件是可以单独在桌面显示的内容,本质上AppWidget实现的是一个广播(BroadCastReceiver),生成一个小部件会通知我们对AppWidget的定义,然后生成相应的布局以及内容
定义小部件主体
实现小部件:继承AppWidgetProvider
public class MyAgentWidget extends AppWidgetProvider {
/*
* 核心方法,在更新的时候调用
*/
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
//appWidgetManager:主要用来调用updataAppWidget(int appwidgetId, RemoteView view)
//这一方法用来替换刷新appwidgetId对应的小部件的布局显示,通过RemoteView控制布局内容
//AppWidget装载内容同样使用的是RemoteView,所以RemoteView相关的限制在小部件上也一样存在
//appWidgetIds:小部件定义完成以后,用户可以在桌面生成多个相同的小部件,所以一个小部件update()中对应的id会是数组类型的数据
}
public void onEnable(Context context) {
//小部件首次添加到桌面时调用
}
public void onDisable(Context context) {
//小部件移除桌面时调用
}
}
AppWidgetManager的实例可以通过getInstance()来获取,在非Widget定义部分的代码也可以对小部件的内容进行修改
//将RemoteView设置给指定的小部件替换为新的布局样式
AppWidgetManager.getInstance().updateAppWidget(new ComponentName(Context, 小部件.class), RemoteView)
小部件注册
小部件本体是一个BroadCastReceiver,那么对应的在AndroidMainfest里要进行注册
<receiver android:name=".appwidget.MyAppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
<!-- resource指向的xml文件是对Widget的一些基本定义 -->
android:resource="@xml/msg_widget_mine" />
</receiver>
小部件定义
在xml文件夹下,
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/agent_tool_widget" <!-- 初始定义锁屏页面中布局 -->
android:initialLayout="@layout/agent_tool_widget" <!-- 初始定义桌面中布局 -->
android:minHeight="40dp" <!-- 布局最小高度 -->
android:minWidth="250dp" <!-- 布局最小宽度 -->
android:previewImage="@mipmap/ic_tools" <!-- 在选择小部件时展示的图片 -->
android:updatePeriodMillis="86400000" <!-- 更新时间毫秒值 -->
android:widgetCategory="home_screen"></appwidget-provider> <!-- 相当于一个类别小部件的标识 -->
需要注意的是:通常定义下,桌面中的一格,宽高都是40dp,但在不同的room下表现都会有差别(部分系统会在边界添加padding),所以整体父布局尽量避免使用padding,保持和桌面应用的入口相似。
只占单格的小部件样式为了更好的适配,最好只用一张图片展示
快捷方式
快捷方式的实现和小部件原理相似,同样是发送广播生成;广播发送后由系统接收,之后根据广播中包含的内容生成快捷方式;
权限申请
<!-- 快捷方式生成 -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- 快捷方式移除 -->
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
<!-- 快捷方式读取信息 -->
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher2.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
生成快捷方式
ShortcutInfoCompat info = new ShortcutInfoCompat(Context context, String title)
.setIcon(快捷方式图标(Bitmap,IconCompat,Drawable))
.setShortLabel(String title)
.setIntent(点击快捷方式发出的Intent)
.build();
//生成制定快捷方式
PendingIntent shortcutCallbackIntent = PendingIntent.getBroadCast(Context context, int type:快捷方式的唯一标识, Intent intent, PendingIntent.FLAG_UPDATE_CURRENT)
ShortcutManagerCompat.requestPinShortcut(Context context, ShortcutInfoCompat info, IntentSender intentSender)
快捷方式点击
Intnet intent = new Intent(Context context, Class clazz);
intent.setAction(Intent.ACTION_VIEW); //必须设置项,缺失会导致崩溃
在首页点击快捷方式后将会直接根据Intent的内容进行相应的跳转,此处的Intent可以设置FLAG来控制Activity的启动模式
关于PendingIntent的点击效果
在RemoteView中要申明点击事件需要借助PendingIntent来完成,但是有非常重要的一点在于,PendingIntent中申明的Flag是没有效果的,也就是说需要跳转到首页之类的页面时,在Intent中指定的启动模式是不会起到作用的;
如果原先的首页是standard模式(一般都设置为这个模式),那么就会在跳转是创建一个新的首页(emm....),而修改首页的launchMode也并不可取,会影响到切置后台再从图标进入时的逻辑;
对于RemoteView的点击目标最好不要直接跳转到首页,如果有必须跳转的情况,这时就需要一个中转页面来辅助跳转,因为PendingIntent无法传递launchMode,所以需要先跳转至中转页,再由中转页通过Intent来定义launchMode,通过这种方式可以做到正确的跳转