一、引言
Android10/11的Launcher3关于widget的运用很多,如果需要动态在Launcher3的指定位置
展示指定的widget,就得对其流程进行研究,知晓原理后修改起来事半功倍,此文主要从
流程+实例进行阐述:
1.1、长按widget拖拽到桌面的流程
1.2.、动态实现添加widget的逻辑描述
二、Launcher3 widget拖拽流程
分析思路:首先要分析一个事物的流程,就得有比较宏观的思考,源码的思想围绕着复用原
则,故此基本上都有一个大基类和子类去实现,我们就从基类入手,找到view所在的长按事
件,进行日志的添加调试,这样对代码的把握带来很好的掌控,以下从两个流程分析。
2.1、触发事件代码流程
长按拖拽触发系统事件后Launcher会绘制一个虚拟的悬浮view,此view在用户移动过程
中跟随手指移动的,在用户放开手指时销毁,流程如下
2.2、绘制的代码流程
用户在移动到对应位置放开手指后,会触发view的onDrop方法,此时真正触发view显示
在桌面上的逻辑,流程如下
完成上述两个步骤,如果位置有效的情况下,widget已经显示在桌面上,如果widget所选位
置为无效位置,消除虚拟view的同时不绘制显示在桌面的逻辑。
三、动态添加widget实现
3.1、需求
Launcher3第一次初始化时在指定的位置显示一个搜索框和数字时钟的widget。
3.2、思考如何实现
经过上述流程的讲述,我们要实现此功能其实已经很简单,我们换一个思路思考,既然
Google可以拖拽到桌面并且显示出来,那么是否可以仿照原生省去拖拽的动作,直接在
代码中调用实现,这样的好处就是遵循原生逻辑,保证稳定性。
3.3、实现步骤
1. 在Launcher.java类中添加widget的实现方法,各个参数说明如下:
widgetPack 变量表示要展示widget的包名
widgetClass 变量表示要展示widget的类名
cellX 变量表示要展示widget的起始横坐标
cellY 变量表示要展示widget的起始纵坐标
spanX 变量表示要展示widget横坐标占的格子数
spanY 变量表示要展示widget纵坐标占的格子数
screenId 变量表示要展示widget在哪个分页
private void addIminAppWidgetFromDrop(String widgetPack , String widgetClass ,int cellX , int cellY , int spanX , int spanY , int screenId){
//id= 11 , cellX= 1 , cellY= 1 , spanX= 2 , spanY= 1 , rank= 0 , screenId= 0 , itemType= 4 , container= -100
ArrayList<WidgetListRowEntry> appWidgets = mPopupDataProvider.getAllWidgets();
Log.d(TAG, "imin Launcher addIminAppWidgetFromDrop appWidgets= " + appWidgets.size());
for(WidgetListRowEntry app : appWidgets) {
if (widgetPack.equals(app.pkgItem.packageName)) {
ArrayList<WidgetItem> widgets = app.widgets;
Log.d(TAG, "imin Launcher addIminAppWidgetFromDrop widgets= " + widgets.size());
for (WidgetItem apps : widgets) {
Log.d(TAG, "imin Launcher addIminAppWidgetFromDrop widgetInfo= " + apps.widgetInfo
+ " , getClassName= " + apps.widgetInfo.provider.getClassName());
if (widgetClass.equals(apps.widgetInfo.provider.getClassName())) {
Log.d(TAG, "imin Launcher addIminAppWidgetFromDrop ** widgetInfo= " + apps.widgetInfo);
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(apps.widgetInfo);
pendingInfo.cellX = cellX;
pendingInfo.cellY = cellY;
pendingInfo.spanX = spanX;
pendingInfo.spanY = spanY;
pendingInfo.rank = 0;
pendingInfo.screenId = screenId;
pendingInfo.itemType = 4;
pendingInfo.container = -100;
addAppWidgetFromDrop(pendingInfo);
saveClockWidgetFlagBySP(getApplicationContext(), "record_widget_flag");
}
}
}
}
}
2. 在Launcher.java类中添加保存获取数据的方法,防止widget叠加显示
注:判断位置占用情况来展示更好,Launcher3排序规则后续有空会讲解
private void saveClockWidgetFlagBySP(Context context, String value) {
SharedPreferences sp = context.getSharedPreferences("imin_clock_widget", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("imin_widget_key", value);
editor.apply();
}
private String getClockWidgetFlagSP(Context context) {
SharedPreferences sp = context.getSharedPreferences("imin_clock_widget", Context.MODE_PRIVATE);
String value = sp.getString("imin_widget_key", null);
return value;
}
3. 在Launcher.java类中添加展示两个widget的逻辑
private void setIminWidget(){
mHandler.post(new Runnable(){
public void run() {
Log.d(TAG , "imin Launcher onCreate 495 getAllWidgets= " + mPopupDataProvider.getAllWidgets());
if(mPopupDataProvider.getAllWidgets().size() > 0){
setIminWidgetDefaultDisplay();
return;
}
mHandler.postDelayed(this , 1000);
}
});
}
private void setIminWidgetDefaultDisplay() {
String defaultStr = getClockWidgetFlagSP(getApplicationContext());
if(defaultStr != null && "record_widget_flag".equals(defaultStr)){
return;
}
addIminAppWidgetFromDrop("com.android.quicksearchbox" , "com.android.quicksearchbox.SearchWidgetProvider" , 0 , 0 , 4 , 1 , 1);//quicksearchbox
addIminAppWidgetFromDrop("com.android.deskclock" , "com.android.alarmclock.DigitalAppWidgetProvider" , 0 , 1 , 4 , 1 , 1);//deskclock
}
说明:为防止在第一次初始化launcher时,由于widgets列表未加载,如果只设置一次展
示的 动作,此时由于wigets未加载,导致无法展示,故此使用handler循环判断存在列表
后设置,避免无法展示widget。
4. 在Launcher的onCreate方法中调用setIminWidget方法进行widget的展示,满足需求
注:如需要其他地方控制,也可以写个监听后调用此方法就能实现动态展示的效果
四、总结
Launcher3的定制在第一篇博文其实已经概括差不多了,目前基本能满足市面上的修改,当然
有些个别较偏门的内容并未包含,其实也简单,只要有大局思维,我觉得不管是系统应用,
framework还是底层驱动,其实都不是不可企及的,后续会总结framework相关的内容,目前
博主正在学习C的运用,准备学习驱动知识,驱动相关的会在framework后更新。