Android10/11 原生Launcher3动态显示Widget

一、引言

        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后更新。

猜你喜欢

转载自blog.csdn.net/CGTAIHYQ/article/details/123839164