Android-->Launcher拖拽事件详解

转自:

http://blog.csdn.net/wdaming1986/article/details/7585649

http://blog.csdn.net/wdaming1986/article/details/7671318

 

最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程。Launcher其实是贯彻于手机的整个系统的,时时刻刻都在运行,要是Launcher不运行了,手机就得黑屏了。Launcher的LauncherMode=singletask,所以说不管Launcher启动了哪个应用,总有个Launcher的实例在堆栈中,并且位于栈底。点击Home键进入到Launcher,上篇Android的全局键(home键/长按耳机键)详解【android源码解析八】 中有详细的介绍。大致思路其实就是启动launcher的时候,新启动一个task。大致先说这么多,先看截图:

大明原创,转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7585649

                                    

                                                                           图(1)

      上图是4.0的Launcher界面,下面我们分步来解析一下Launcher的启动过程。

     Step 0:首先要给大家介绍一下Launcher的数据库,这个数据库中存放着待机界面的图标,主屏底部的应用程序图标和桌面folder中各应用程序的图标,ICS4.0的folder中只能放应用程序的快捷方式,shortcut不能放到这个folder中,先看截图: 

                                                                        图(2)

       说说各字段的含义:

                title:表示桌面应用程序的名字,有的title为空,表示是widget的快捷方式;

             intent:表示启动这个图标的intent放到数据库中,当click的时候就会调用这个字段,启动相应的应用程序;

       container:表示应用程序的容器,folder的容器为整数,-100:表示在桌面的程序,-101:表示是主屏底部的程序;

           screen:表示在第几个屏,folder的screen都是0, container=-101的为0,1,3,4;2为allapp的按钮;

               cellX:表示在屏幕X轴的位置,(0,1,2,3),左上角为0点,往右依次增加;

               cellY:表示在屏幕Y轴的位置,(0,1,2,3),左上角为0点,往下依次增加;

              spallX:表示占X轴几个格;

              spallY:表示占Y轴几个格;

         itemType:应用程序用0表示,shortcut用1表示,folder用2表示,widget用4表示;

    appWidgetId:-1表示不是widget,数字大于0表示才是widget;

       isShortCut:值为0表示不是应用程序的ShortCut,值为1表示是应用程序的ShortCut;

         iconType:值为0表示图标的名字被定义为包名的资源id,值为1表示图标用bitmap保存;

                 icon:表示应用程序的图标,二进制的;显示为一张图片;

       说明:folder中的应用快捷方式绑定folder---->是用container的值绑定folder的id的;

        详细的讲解请参考LauncherSettings.java这个类,有数据库字段的详细讲解;

         手机是在第一次烧机完成后,数据库的值还没有,这时候launcher解析default_workspace.xml把这个值存到数据库中;所以说想定制什么样的开机桌面就在default_workspace.xml中做相应的配置,具体参照我前面的博客:

Android中源码Launcher主屏幕程序排列详解【安卓Launcher进化一】中有详细的介绍:

       i f (!convertDatabase(db)) {
                 // Populate favorites table with initial favorites
                loadFavorites(db, R.xml.default_workspace);
        }

      Step 1:开机后先启动LauncherApplication.java这个类的onCreate()方法,下面看代码:  

   

  1. @Override 
  2. public void onCreate() { 
  3.      super.onCreate(); 
  4.  
  5.      // set sIsScreenXLarge and sScreenDensity *before* creating icon cache 
  6.      // 在创建图标缓存之前先设置sIsScreenXLarge和屏幕设备的分辨率 
  7.      final int screenSize = getResources().getConfiguration().screenLayout & 
  8.              Configuration.SCREENLAYOUT_SIZE_MASK; 
  9.      sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE || 
  10.          screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE; 
  11.      sScreenDensity = getResources().getDisplayMetrics().density; 
  12.  
  13.      // 实例化图标缓存区的对象 
  14.      mIconCache = new IconCache(this); 
  15.      // 实例化一个LauncherModel对象,这个类是保存Launcher的内存启动状态,更新Launcher的数据库的作用 
  16.      mModel = new LauncherModel(this, mIconCache); 
  17.  
  18.      // Register intent receivers 
  19.      // 注册监听,应用package增加,删除,改变的监听。 
  20.      IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 
  21.      filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 
  22.      filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 
  23.      filter.addDataScheme("package"); 
  24.      registerReceiver(mModel, filter); 
  25.      filter = new IntentFilter(); 
  26.      // 注册application是否可用,语言改变,方向改变的监听。4.0支持横竖屏 
  27.      filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 
  28.      filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 
  29.      filter.addAction(Intent.ACTION_LOCALE_CHANGED); 
  30.      filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 
  31.      registerReceiver(mModel, filter); 
  32.      filter = new IntentFilter(); 
  33.      filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 
  34.      registerReceiver(mModel, filter); 
  35.      filter = new IntentFilter(); 
  36.      filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); 
  37.      registerReceiver(mModel, filter); 
  38.  
  39.      // Register for changes to the favorites 
  40.      // 注册favorites应用程序数据库改变的监听 
  41.      ContentResolver resolver = getContentResolver(); 
  42.      resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true
  43.              mFavoritesObserver); 
   @Override
    public void onCreate() {
        super.onCreate();

        // set sIsScreenXLarge and sScreenDensity *before* creating icon cache
        // 在创建图标缓存之前先设置sIsScreenXLarge和屏幕设备的分辨率
        final int screenSize = getResources().getConfiguration().screenLayout &
                Configuration.SCREENLAYOUT_SIZE_MASK;
        sIsScreenLarge = screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE ||
            screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;
        sScreenDensity = getResources().getDisplayMetrics().density;

        // 实例化图标缓存区的对象
        mIconCache = new IconCache(this);
        // 实例化一个LauncherModel对象,这个类是保存Launcher的内存启动状态,更新Launcher的数据库的作用
        mModel = new LauncherModel(this, mIconCache);

        // Register intent receivers
        // 注册监听,应用package增加,删除,改变的监听。
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addDataScheme("package");
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        // 注册application是否可用,语言改变,方向改变的监听。4.0支持横竖屏
        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
        registerReceiver(mModel, filter);
        filter = new IntentFilter();
        filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
        registerReceiver(mModel, filter);

        // Register for changes to the favorites
        // 注册favorites应用程序数据库改变的监听
        ContentResolver resolver = getContentResolver();
        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
                mFavoritesObserver);
    }

 


   

       Step 2:在LauncherApplication.java中onTerminate()的方法,解除监听的绑定;

  1. /**
  2.     * There's no guarantee that this function is ever called.
  3.     */ 
  4.    @Override 
  5.    public void onTerminate() { 
  6.        super.onTerminate(); 
  7.  
  8.        unregisterReceiver(mModel); 
  9.  
  10.        ContentResolver resolver = getContentResolver(); 
  11.        resolver.unregisterContentObserver(mFavoritesObserver); 
  12.    } 
 /**
     * There's no guarantee that this function is ever called.
     */
    @Override
    public void onTerminate() {
        super.onTerminate();

        unregisterReceiver(mModel);

        ContentResolver resolver = getContentResolver();
        resolver.unregisterContentObserver(mFavoritesObserver);
    }

     Step 3:Step1中的数据库mFavoritesObserver监听内部类如下:

  1. /**
  2.   * Receives notifications whenever the user favorites have changed.
  3.   */ 
  4. private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) { 
  5.      @Override 
  6.      public void onChange(boolean selfChange) { 
  7.          mModel.startLoader(LauncherApplication.this, false); 
  8.      } 
  9. }; 
   /**
     * Receives notifications whenever the user favorites have changed.
     */
    private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange) {
            mModel.startLoader(LauncherApplication.this, false);
        }
    };

 


      说明:mModel.startLoader(。。,。。)是开启一个线程,设置线程的优先级NORM_PRIORITY,开始load桌面图标对应的数据库,这个过程是和Launcher.onCreate()同时进行的;

   Step 4: 接着我们来看看mModel.startLoader(LauncherApplication.this, false)的方法:

  1. public void startLoader(Context context, boolean isLaunching) { 
  2.      synchronized (mLock) { 
  3.          if (DEBUG_LOADERS) { 
  4.              Log.d(TAG, "startLoader isLaunching=" + isLaunching); 
  5.          } 
  6.  
  7.          // Don't bother to start the thread if we know it's not going to do anything 
  8.          if (mCallbacks != null && mCallbacks.get() != null) { 
  9.              // If there is already one running, tell it to stop. 
  10.              // also, don't downgrade isLaunching if we're already running 
  11.              isLaunching = isLaunching || stopLoaderLocked(); 
  12.              mLoaderTask = new LoaderTask(context, isLaunching); 
  13.              sWorkerThread.setPriority(Thread.NORM_PRIORITY); 
  14.              sWorker.post(mLoaderTask); 
  15.          } 
  16.      } 
   public void startLoader(Context context, boolean isLaunching) {
        synchronized (mLock) {
            if (DEBUG_LOADERS) {
                Log.d(TAG, "startLoader isLaunching=" + isLaunching);
            }

            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                // If there is already one running, tell it to stop.
                // also, don't downgrade isLaunching if we're already running
                isLaunching = isLaunching || stopLoaderLocked();
                mLoaderTask = new LoaderTask(context, isLaunching);
                sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                sWorker.post(mLoaderTask);
            }
        }
    }

   Step 5:接着我们来看看LoaderTask.java的run()方法:

  1. public void run() { 
  2.     // Optimize for end-user experience: if the Launcher is up and // running with the 
  3.     // All Apps interface in the foreground, load All Apps first. Otherwise, load the 
  4.     // workspace first (default). 
  5.     final Callbacks cbk = mCallbacks.get(); 
  6.     final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true
  7.  
  8.     keep_running: { 
  9.         // Elevate priority when Home launches for the first time to avoid 
  10.         // starving at boot time. Staring at a blank home is not cool. 
  11.         synchronized (mLock) { 
  12.             if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to "
  13.                     (mIsLaunching ? "DEFAULT" : "BACKGROUND")); 
  14.             android.os.Process.setThreadPriority(mIsLaunching 
  15.                     ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); 
  16.         } 
  17.         if (loadWorkspaceFirst) { 
  18.             if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace"); 
  19.             loadAndBindWorkspace(); 
  20.         } else
  21.             if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps"); 
  22.             loadAndBindAllApps(); 
  23.         } 
  24.  
  25.         if (mStopped) { 
  26.             break keep_running; 
  27.         } 
  28.  
  29.         // Whew! Hard work done.  Slow us down, and wait until the UI thread has 
  30.         // settled down. 
  31.         synchronized (mLock) { 
  32.             if (mIsLaunching) { 
  33.                 if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND"); 
  34.                 android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 
  35.             } 
  36.         } 
  37.         waitForIdle(); 
  38.  
  39.         // second step 
  40.         if (loadWorkspaceFirst) { 
  41.             if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps"); 
  42.             loadAndBindAllApps(); 
  43.         } else
  44.             if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace"); 
  45.             loadAndBindWorkspace(); 
  46.         } 
  47.  
  48.         // Restore the default thread priority after we are done loading items 
  49.         synchronized (mLock) { 
  50.             android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 
  51.         } 
  52.     } 
  53.  
  54.  
  55.     // Update the saved icons if necessary 
  56.     if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons"); 
  57.     for (Object key : sDbIconCache.keySet()) { 
  58.         updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key)); 
  59.     } 
  60.     sDbIconCache.clear(); 
  61.  
  62.     // Clear out this reference, otherwise we end up holding it until all of the 
  63.     // callback runnables are done. 
  64.     mContext = null
  65.  
  66.     synchronized (mLock) { 
  67.         // If we are still the last one to be scheduled, remove ourselves. 
  68.         if (mLoaderTask == this) { 
  69.             mLoaderTask = null
  70.         } 
  71.     } 
  72.  
  73. public void stopLocked() { 
  74.     synchronized (LoaderTask.this) { 
  75.         mStopped = true
  76.         this.notify(); 
  77.     } 
        public void run() {
            // Optimize for end-user experience: if the Launcher is up and // running with the
            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
            // workspace first (default).
            final Callbacks cbk = mCallbacks.get();
            final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;

            keep_running: {
                // Elevate priority when Home launches for the first time to avoid
                // starving at boot time. Staring at a blank home is not cool.
                synchronized (mLock) {
                    if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
                            (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
                    android.os.Process.setThreadPriority(mIsLaunching
                            ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
                }
                if (loadWorkspaceFirst) {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                    loadAndBindWorkspace();
                } else {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 1: special: loading all apps");
                    loadAndBindAllApps();
                }

                if (mStopped) {
                    break keep_running;
                }

                // Whew! Hard work done.  Slow us down, and wait until the UI thread has
                // settled down.
                synchronized (mLock) {
                    if (mIsLaunching) {
                        if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
                        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    }
                }
                waitForIdle();

                // second step
                if (loadWorkspaceFirst) {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                    loadAndBindAllApps();
                } else {
                    if (DEBUG_LOADERS) Log.d(TAG, "step 2: special: loading workspace");
                    loadAndBindWorkspace();
                }

                // Restore the default thread priority after we are done loading items
                synchronized (mLock) {
                    android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
                }
            }


            // Update the saved icons if necessary
            if (DEBUG_LOADERS) Log.d(TAG, "Comparing loaded icons to database icons");
            for (Object key : sDbIconCache.keySet()) {
                updateSavedIcon(mContext, (ShortcutInfo) key, sDbIconCache.get(key));
            }
            sDbIconCache.clear();

            // Clear out this reference, otherwise we end up holding it until all of the
            // callback runnables are done.
            mContext = null;

            synchronized (mLock) {
                // If we are still the last one to be scheduled, remove ourselves.
                if (mLoaderTask == this) {
                    mLoaderTask = null;
                }
            }
        }

        public void stopLocked() {
            synchronized (LoaderTask.this) {
                mStopped = true;
                this.notify();
            }
        }

加载桌面图标对应的数据库的值,这些值能把这些图标显示在屏幕上

    Step 6:LauncherApplication.onCreate()方法启动完成后,接着开始调用Launcher.java的onCreate()方法。代码如下:

  1. @Override 
  2. protected void onCreate(Bundle savedInstanceState) { 
  3.     super.onCreate(savedInstanceState); 
  4.     // 得到LauncherApplication的对象app 
  5.     LauncherApplication app = ((LauncherApplication)getApplication()); 
  6.     // 得到LauncherModel对象mModel,设置一个mCallbacks = new WeakReference<Callbacks>(callbacks)的 
  7.     // 回调callbacks 
  8.     mModel = app.setLauncher(this); 
  9.     // 得到图标缓存的对象mIconCache 
  10.     mIconCache = app.getIconCache(); 
  11.     // 得到拖拽控制类DragController的对象 
  12.     mDragController = new DragController(this); 
  13.     // 得到一个LayoutInflater布局的对象 
  14.     mInflater = getLayoutInflater(); 
  15.  
  16.     // 得到一个AppWidgetManager的对象 
  17.     mAppWidgetManager = AppWidgetManager.getInstance(this); 
  18.     // 得到LauncherAppWidgetHost的一个对象 
  19.     mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); 
  20.     // Start receiving onAppWidgetChanged calls for your AppWidgets. 
  21.     mAppWidgetHost.startListening(); 
  22.  
  23.     if (PROFILE_STARTUP) { 
  24.         android.os.Debug.startMethodTracing( 
  25.                 Environment.getExternalStorageDirectory() + "/launcher"); 
  26.     } 
  27.  
  28.     // 检查Locale的语言级别,mcc, mnc的改变 
  29.     checkForLocaleChange(); 
  30.     // 加载Launcher.xml布局文件 
  31.     setContentView(R.layout.launcher); 
  32.     // Launcher的布局的初始化 
  33.     setupViews(); 
  34.     // 第一次启动Android的展示设置向导, 
  35.     // 这个SharedPreferences中存在 
  36.     // <boolean name="cling.workspace.dismissed" value="true" /> 
  37.     // 如果值为true,则不显示设置向导,为false,则显示设置向导。 
  38.     showFirstRunWorkspaceCling(); 
  39.  
  40.     // 注册数据库观察者 
  41.     registerContentObservers(); 
  42.  
  43.     lockAllApps(); 
  44.  
  45.     mSavedState = savedInstanceState; 
  46.     restoreState(mSavedState); 
  47.  
  48.     // Update customization drawer _after_ restoring the states 
  49.     if (mAppsCustomizeContent != null) { 
  50.         mAppsCustomizeContent.onPackagesUpdated(); 
  51.     } 
  52.  
  53.     if (PROFILE_STARTUP) { 
  54.         android.os.Debug.stopMethodTracing(); 
  55.     } 
  56.  
  57.     if (!mRestoring) { 
  58.         mModel.startLoader(this, true); 
  59.     } 
  60.  
  61.     if (!mModel.isAllAppsLoaded()) { 
  62.         ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent(); 
  63.         mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent); 
  64.     } 
  65.  
  66.     // For handling default keys 
  67.     mDefaultKeySsb = new SpannableStringBuilder(); 
  68.     Selection.setSelection(mDefaultKeySsb, 0); 
  69.  
  70.     // 注册系统对话框消失的监听 
  71.     IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 
  72.     registerReceiver(mCloseSystemDialogsReceiver, filter); 
  73.  
  74.     boolean searchVisible = false
  75.     boolean voiceVisible = false
  76.     // If we have a saved version of these external icons, we load them up immediately 
  77.     // 如果我们已经保存了外部图标的版本,我们立即加载它们 
  78.     int coi = getCurrentOrientationIndexForGlobalIcons(); 
  79.     if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null || 
  80.             sAppMarketIcon[coi] == null) { 
  81.         updateAppMarketIcon(); 
  82.         searchVisible = updateGlobalSearchIcon(); 
  83.         voiceVisible = updateVoiceSearchIcon(searchVisible); 
  84.     } 
  85.     if (sGlobalSearchIcon[coi] != null) { 
  86.          updateGlobalSearchIcon(sGlobalSearchIcon[coi]); 
  87.          searchVisible = true
  88.     } 
  89.     if (sVoiceSearchIcon[coi] != null) { 
  90.         updateVoiceSearchIcon(sVoiceSearchIcon[coi]); 
  91.         voiceVisible = true
  92.     } 
  93.     if (sAppMarketIcon[coi] != null) { 
  94.         updateAppMarketIcon(sAppMarketIcon[coi]); 
  95.     } 
  96.     mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible); 
  97.  
  98.     // On large interfaces, we want the screen to auto-rotate based on the current orientation 
  99.     if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng")) { 
  100.         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 
  101.     } 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 得到LauncherApplication的对象app
        LauncherApplication app = ((LauncherApplication)getApplication());
        // 得到LauncherModel对象mModel,设置一个mCallbacks = new WeakReference<Callbacks>(callbacks)的
        // 回调callbacks
        mModel = app.setLauncher(this);
        // 得到图标缓存的对象mIconCache
        mIconCache = app.getIconCache();
        // 得到拖拽控制类DragController的对象
        mDragController = new DragController(this);
        // 得到一个LayoutInflater布局的对象
        mInflater = getLayoutInflater();

        // 得到一个AppWidgetManager的对象
        mAppWidgetManager = AppWidgetManager.getInstance(this);
        // 得到LauncherAppWidgetHost的一个对象
        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
        // Start receiving onAppWidgetChanged calls for your AppWidgets.
        mAppWidgetHost.startListening();

        if (PROFILE_STARTUP) {
            android.os.Debug.startMethodTracing(
                    Environment.getExternalStorageDirectory() + "/launcher");
        }

        // 检查Locale的语言级别,mcc, mnc的改变
        checkForLocaleChange();
        // 加载Launcher.xml布局文件
        setContentView(R.layout.launcher);
        // Launcher的布局的初始化
        setupViews();
        // 第一次启动Android的展示设置向导,
        // 这个SharedPreferences中存在
        // <boolean name="cling.workspace.dismissed" value="true" />
        // 如果值为true,则不显示设置向导,为false,则显示设置向导。
        showFirstRunWorkspaceCling();

        // 注册数据库观察者
        registerContentObservers();

        lockAllApps();

        mSavedState = savedInstanceState;
        restoreState(mSavedState);

        // Update customization drawer _after_ restoring the states
        if (mAppsCustomizeContent != null) {
            mAppsCustomizeContent.onPackagesUpdated();
        }

        if (PROFILE_STARTUP) {
            android.os.Debug.stopMethodTracing();
        }

        if (!mRestoring) {
            mModel.startLoader(this, true);
        }

        if (!mModel.isAllAppsLoaded()) {
            ViewGroup appsCustomizeContentParent = (ViewGroup) mAppsCustomizeContent.getParent();
            mInflater.inflate(R.layout.apps_customize_progressbar, appsCustomizeContentParent);
        }

        // For handling default keys
        mDefaultKeySsb = new SpannableStringBuilder();
        Selection.setSelection(mDefaultKeySsb, 0);

        // 注册系统对话框消失的监听
        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        registerReceiver(mCloseSystemDialogsReceiver, filter);

        boolean searchVisible = false;
        boolean voiceVisible = false;
        // If we have a saved version of these external icons, we load them up immediately
        // 如果我们已经保存了外部图标的版本,我们立即加载它们
        int coi = getCurrentOrientationIndexForGlobalIcons();
        if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
                sAppMarketIcon[coi] == null) {
            updateAppMarketIcon();
            searchVisible = updateGlobalSearchIcon();
            voiceVisible = updateVoiceSearchIcon(searchVisible);
        }
        if (sGlobalSearchIcon[coi] != null) {
             updateGlobalSearchIcon(sGlobalSearchIcon[coi]);
             searchVisible = true;
        }
        if (sVoiceSearchIcon[coi] != null) {
            updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
            voiceVisible = true;
        }
        if (sAppMarketIcon[coi] != null) {
            updateAppMarketIcon(sAppMarketIcon[coi]);
        }
        mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);

        // On large interfaces, we want the screen to auto-rotate based on the current orientation
        if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng")) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        }
    }

    Step 7:其中LauncherModel这个类中有个回调接口,具体定义如下:

  1. public interface Callbacks { 
  2.        public boolean setLoadOnResume(); 
  3.        public int getCurrentWorkspaceScreen(); 
  4.        public void startBinding(); 
  5.        public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end); 
  6.        public void bindFolders(HashMap<Long,FolderInfo> folders); 
  7.        public void finishBindingItems(); 
  8.        public void bindAppWidget(LauncherAppWidgetInfo info); 
  9.        public void bindAllApplications(ArrayList<ApplicationInfo> apps); 
  10.        public void bindAppsAdded(ArrayList<ApplicationInfo> apps); 
  11.        public void bindAppsUpdated(ArrayList<ApplicationInfo> apps); 
  12.        public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent); 
  13.        public void bindPackagesUpdated(); 
  14.        public boolean isAllAppsVisible(); 
  15.        public void bindSearchablesChanged(); 
  16.    } 
 public interface Callbacks {
        public boolean setLoadOnResume();
        public int getCurrentWorkspaceScreen();
        public void startBinding();
        public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end);
        public void bindFolders(HashMap<Long,FolderInfo> folders);
        public void finishBindingItems();
        public void bindAppWidget(LauncherAppWidgetInfo info);
        public void bindAllApplications(ArrayList<ApplicationInfo> apps);
        public void bindAppsAdded(ArrayList<ApplicationInfo> apps);
        public void bindAppsUpdated(ArrayList<ApplicationInfo> apps);
        public void bindAppsRemoved(ArrayList<ApplicationInfo> apps, boolean permanent);
        public void bindPackagesUpdated();
        public boolean isAllAppsVisible();
        public void bindSearchablesChanged();
    }

 


对LauncherModel进行初始化的时候 mModel = app.setLauncher(this);---->mModel.initialize(launcher);----->

              public void initialize(Callbacks callbacks) {
                      synchronized (mLock) {
                              mCallbacks = new WeakReference<Callbacks>(callbacks);
                       }
               }

这个callbacks就是定义的接口回调,具体实现是在Launcher.java中定义的,启动Launcher的过程中,这些实现是异步来实现的。还有Launcher.java的onResume()方法没有讲解,到这儿基本上Android的Launcher已经启动起来了,这个onResume()我研究后再更新。

AndroidICS4.0版本的launcher拖拽的流程,基本和2.3的相似。就是比2.3写的封装的接口多了一些,比如删除类的写法就多了个类。等等。4.0的改变有一些,但是不是特别大。这个月一直在改动Launcher的缩略图的效果,4.0的缩略图的功能没有实现,还得从2.3的Launcher中摘出来。通过做这个缩略图对Launcher的模块有一点点了解,拿来分享一下Launcher拖拽的工作流程。微笑有图有真相!吐舌头

转载请标明出处:http://blog.csdn.net/wdaming1986/article/details/7671318

                (1) 先来看看类之间的继承关系

                          

                                                          图(1)

                                                                                                                                               

              (2)再来看看Launcher拖拽流程的时序图

              

                                                                       图(2)

下面咱们分步来解析Launcher拖拽的详细过程:

step 1 :先来看看Launcher.java这个类的onCreate()方法中的setupViews()方法中的一部分代码:

  1. <strong> </strong><span style="color: rgb(0, 0, 0); font-size: 16px;">// Setup the workspace 
  2.         mWorkspace.setHapticFeedbackEnabled(false); 
  3.         mWorkspace.setOnLongClickListener(this); 
  4.         mWorkspace.setup(dragController); 
  5.         dragController.addDragListener(mWorkspace);</span> 
 // Setup the workspace
        mWorkspace.setHapticFeedbackEnabled(false);
        mWorkspace.setOnLongClickListener(this);
        mWorkspace.setup(dragController);
        dragController.addDragListener(mWorkspace);

Workspace设置长按事件的监听交给了Launcher.java这个类了。所以在主屏上长按事件会走到Launcher.java----->

onLongClick()这个方法中去;

step 2 :接着我们来看看Launcher.java中onLongClick()的代码:

 

  1. public boolean onLongClick(View v) { 
  2.          ·············· 
  3. // The hotseat touch handling does not go through Workspace, and we always allow long press 
  4.         // on hotseat items. 
  5.         final View itemUnderLongClick = longClickCellInfo.cell; 
  6.         boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress(); 
  7.         if (allowLongPress && !mDragController.isDragging()) { 
  8.             if (itemUnderLongClick == null) { 
  9.                 // User long pressed on empty space 
  10.                 mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, 
  11.                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); 
  12.                 startWallpaper(); 
  13.             } else
  14.                 if (!(itemUnderLongClick instanceof Folder)) { 
  15.                     // User long pressed on an item 
  16.                     mWorkspace.startDrag(longClickCellInfo); 
  17.                 } 
  18.             } 
  19.         } 
  20.         return true
  21.     } 
public boolean onLongClick(View v) {
         ··············
 // The hotseat touch handling does not go through Workspace, and we always allow long press
        // on hotseat items.
        final View itemUnderLongClick = longClickCellInfo.cell;
        boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress();
        if (allowLongPress && !mDragController.isDragging()) {
            if (itemUnderLongClick == null) {
                // User long pressed on empty space
                mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                startWallpaper();
            } else {
                if (!(itemUnderLongClick instanceof Folder)) {
                    // User long pressed on an item
                    mWorkspace.startDrag(longClickCellInfo);
                }
            }
        }
        return true;
    }

通过itemUnderLongClick == null 来判断,在屏幕上触发长按事件是否选中了shortcut或者widget。如果为空,就启动桌面的壁纸,else,就把拖拽事件往Workspace.java这个类传递。

Step 3 :通过mWorkspace.startDrag(longClickCellInfo),把长按事件传递给workspace来处理,具体来看代码:

  1. void startDrag(CellLayout.CellInfo cellInfo) { 
  2.      View child = cellInfo.cell; 
  3.  
  4.      // Make sure the drag was started by a long press as opposed to a long click. 
  5.      if (!child.isInTouchMode()) { 
  6.          return
  7.      } 
  8.  
  9.      mDragInfo = cellInfo; 
  10.      //隐藏拖拽的child 
  11.      child.setVisibility(GONE); 
  12.  
  13.      child.clearFocus(); 
  14.      child.setPressed(false); 
  15.  
  16.      final Canvas canvas = new Canvas(); 
  17.  
  18.      // We need to add extra padding to the bitmap to make room for the glow effect 
  19.      final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS; 
  20.  
  21.      // The outline is used to visualize where the item will land if dropped 
  22.      mDragOutline = createDragOutline(child, canvas, bitmapPadding); 
  23.      beginDragShared(child, this); 
   void startDrag(CellLayout.CellInfo cellInfo) {
        View child = cellInfo.cell;

        // Make sure the drag was started by a long press as opposed to a long click.
        if (!child.isInTouchMode()) {
            return;
        }

        mDragInfo = cellInfo;
        //隐藏拖拽的child
        child.setVisibility(GONE);

        child.clearFocus();
        child.setPressed(false);

        final Canvas canvas = new Canvas();

        // We need to add extra padding to the bitmap to make room for the glow effect
        final int bitmapPadding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;

        // The outline is used to visualize where the item will land if dropped
        mDragOutline = createDragOutline(child, canvas, bitmapPadding);
        beginDragShared(child, this);
    }

上面的代码主要做的工作是:把正在拖拽的这个view隐藏掉,在主屏幕上绘制一个蓝色的,大小和图标相似的一个边框,以表示能在主屏的这个位置放置。

Step 4 :接着调用beginDragShared(child, this)这个方法,代码如下:

  1. public void beginDragShared(View child, DragSource source) { 
  2.     ··· ··· 
  3. // Clear the pressed state if necessary 
  4.         if (child instanceof BubbleTextView) { 
  5.             BubbleTextView icon = (BubbleTextView) child; 
  6.             icon.clearPressedOrFocusedBackground(); 
  7.         } 
  8.  
  9.         mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), 
  10.                 DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect); 
  11.         b.recycle(); 
  12.     } 
 public void beginDragShared(View child, DragSource source) {
    ··· ···
// Clear the pressed state if necessary
        if (child instanceof BubbleTextView) {
            BubbleTextView icon = (BubbleTextView) child;
            icon.clearPressedOrFocusedBackground();
        }

        mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect);
        b.recycle();
    }

这个方法做的工作是:开始进行拖拽,绘制正在拖拽的图片,把拖拽的事件交给DragController来处理。

Step 5 :接着来看看mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect)这个方法,代码如下:

  1. public void startDrag(Bitmap b, int dragLayerX, int dragLayerY, 
  2.             DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion) { 
  3. ··· ··· 
  4. mDragObject.dragComplete = false
  5.         mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft); 
  6.         mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop); 
  7.         mDragObject.dragSource = source; 
  8.         mDragObject.dragInfo = dragInfo; 
  9. mVibrator.vibrate(VIBRATE_DURATION); 
  10.  
  11.         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX, 
  12.                 registrationY, 0, 0, b.getWidth(), b.getHeight()); 
  13.  
  14.         if (dragOffset != null) { 
  15.             dragView.setDragVisualizeOffset(new Point(dragOffset)); 
  16.         } 
  17.         if (dragRegion != null) { 
  18.             dragView.setDragRegion(new Rect(dragRegion)); 
  19.         } 
  20.  
  21.         dragView.show(mMotionDownX, mMotionDownY); 
  22.         handleMoveEvent(mMotionDownX, mMotionDownY); 
  23.     } 
 public void startDrag(Bitmap b, int dragLayerX, int dragLayerY,
            DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion) {
··· ···
 mDragObject.dragComplete = false;
        mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
        mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
        mDragObject.dragSource = source;
        mDragObject.dragInfo = dragInfo;
mVibrator.vibrate(VIBRATE_DURATION);

        final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
                registrationY, 0, 0, b.getWidth(), b.getHeight());

        if (dragOffset != null) {
            dragView.setDragVisualizeOffset(new Point(dragOffset));
        }
        if (dragRegion != null) {
            dragView.setDragRegion(new Rect(dragRegion));
        }

        dragView.show(mMotionDownX, mMotionDownY);
        handleMoveEvent(mMotionDownX, mMotionDownY);
    }

这个方法的作用是:计算要拖拽的view的大小,显示在workspace上,dragView.show(mMotionDownX, mMotionDownY);这个show()会根据手指的移动而移动的。然后在通过handleMoveEvent()方法来分发拖拽的目标到底在哪个目标上。DropTarget一共有3个:workspace,ButtonDropTarget(删除类),Folder;他们分别实现了DropTarget这个接口。

下面来看看这个接口有一下几个方法:

 

  1. boolean isDropEnabled(); 
  2. void onDrop(DragObject dragObject); 
  3.  
  4.     void onDragEnter(DragObject dragObject); 
  5.  
  6.     void onDragOver(DragObject dragObject); 
  7.  
  8.     void onDragExit(DragObject dragObject); 
  9. DropTarget getDropTargetDelegate(DragObject dragObject); 
  10. boolean acceptDrop(DragObject dragObject); 
  11.  
  12.     // These methods are implemented in Views 
  13.     void getHitRect(Rect outRect); 
  14.     void getLocationInDragLayer(int[] loc); 
  15.     int getLeft(); 
  16.     int getTop(); 
boolean isDropEnabled();
void onDrop(DragObject dragObject);

    void onDragEnter(DragObject dragObject);

    void onDragOver(DragObject dragObject);

    void onDragExit(DragObject dragObject);
DropTarget getDropTargetDelegate(DragObject dragObject);
boolean acceptDrop(DragObject dragObject);

    // These methods are implemented in Views
    void getHitRect(Rect outRect);
    void getLocationInDragLayer(int[] loc);
    int getLeft();
    int getTop();

这些方法不是每个类继承了DropTarget的接口,都要把每个方法都实现,这要看具体的需要来定。

另外这个接口中有个内部类-----DragObject:如下

  1. class DragObject { 
  2.         public int x = -1
  3.         public int y = -1
  4.  
  5.         /** X offset from the upper-left corner of the cell to where we touched.  */ 
  6.         public int xOffset = -1
  7.  
  8.         /** Y offset from the upper-left corner of the cell to where we touched.  */ 
  9.         public int yOffset = -1
  10.  
  11.         /** This indicates whether a drag is in final stages, either drop or cancel. It
  12.          * differentiates onDragExit, since this is called when the drag is ending, above
  13.          * the current drag target, or when the drag moves off the current drag object.
  14.          */ 
  15.         public boolean dragComplete = false
  16.  
  17.         /** The view that moves around while you drag.  */ 
  18.         public DragView dragView = null
  19.  
  20.         /** The data associated with the object being dragged */ 
  21.         public Object dragInfo = null
  22.  
  23.         /** Where the drag originated */ 
  24.         public DragSource dragSource = null
  25.  
  26.         /** Post drag animation runnable */ 
  27.         public Runnable postAnimationRunnable = null
  28.  
  29.         /** Indicates that the drag operation was cancelled */ 
  30.         public boolean cancelled = false
  31.  
  32.         public DragObject() { 
  33.         } 
  34.     } 
class DragObject {
        public int x = -1;
        public int y = -1;

        /** X offset from the upper-left corner of the cell to where we touched.  */
        public int xOffset = -1;

        /** Y offset from the upper-left corner of the cell to where we touched.  */
        public int yOffset = -1;

        /** This indicates whether a drag is in final stages, either drop or cancel. It
         * differentiates onDragExit, since this is called when the drag is ending, above
         * the current drag target, or when the drag moves off the current drag object.
         */
        public boolean dragComplete = false;

        /** The view that moves around while you drag.  */
        public DragView dragView = null;

        /** The data associated with the object being dragged */
        public Object dragInfo = null;

        /** Where the drag originated */
        public DragSource dragSource = null;

        /** Post drag animation runnable */
        public Runnable postAnimationRunnable = null;

        /** Indicates that the drag operation was cancelled */
        public boolean cancelled = false;

        public DragObject() {
        }
    }

这个类的作用是存储一些坐标,拖拽点距离整个view左上角x轴上的距离,y轴上的距离,还有一些拖拽的信息都保存在这个类中,还有动画线程类等等。在拖拽过程中这些信息都是会用到的。

Step 6 :接着来看看handleMoveEvent()这个类,这个类频繁被调用,因为在DragLayer.java这个类中onTouchEvent()方法,最后调用的是 mDragController.onTouchEvent(ev)这个方法,长按后,移动的事件就传递到了DragController中的onTouchEvent()方法中,先来看看mDragController.onTouchEvent(ev)这个方法,代码如下:

 

  1. /**
  2.      * Call this from a drag source view.
  3.      */ 
  4.     public boolean onTouchEvent(MotionEvent ev) { 
  5.         if (!mDragging) { 
  6.             return false
  7.         } 
  8.  
  9.         final int action = ev.getAction(); 
  10.         final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY()); 
  11.         final int dragLayerX = dragLayerPos[0]; 
  12.         final int dragLayerY = dragLayerPos[1]; 
  13.  
  14.         switch (action) { 
  15.         case MotionEvent.ACTION_DOWN: 
  16.             // Remember where the motion event started 
  17.             mMotionDownX = dragLayerX; 
  18.             mMotionDownY = dragLayerY; 
  19.  
  20.             if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) { 
  21.                 mScrollState = SCROLL_WAITING_IN_ZONE; 
  22.                 mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY); 
  23.             } else
  24.                 mScrollState = SCROLL_OUTSIDE_ZONE; 
  25.             } 
  26.             break
  27.         case MotionEvent.ACTION_MOVE: 
  28.             handleMoveEvent(dragLayerX, dragLayerY); 
  29.             break
  30.         case MotionEvent.ACTION_UP: 
  31.             // Ensure that we've processed a move event at the current pointer location. 
  32.             handleMoveEvent(dragLayerX, dragLayerY); 
  33.  
  34.             mHandler.removeCallbacks(mScrollRunnable); 
  35.             if (mDragging) { 
  36.                 drop(dragLayerX, dragLayerY); 
  37.             } 
  38.             endDrag(); 
  39.             break
  40.         case MotionEvent.ACTION_CANCEL: 
  41.             cancelDrag(); 
  42.             break
  43.         } 
  44.  
  45.         return true
  46.     } 
/**
     * Call this from a drag source view.
     */
    public boolean onTouchEvent(MotionEvent ev) {
        if (!mDragging) {
            return false;
        }

        final int action = ev.getAction();
        final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
        final int dragLayerX = dragLayerPos[0];
        final int dragLayerY = dragLayerPos[1];

        switch (action) {
        case MotionEvent.ACTION_DOWN:
            // Remember where the motion event started
            mMotionDownX = dragLayerX;
            mMotionDownY = dragLayerY;

            if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
                mScrollState = SCROLL_WAITING_IN_ZONE;
                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
            } else {
                mScrollState = SCROLL_OUTSIDE_ZONE;
            }
            break;
        case MotionEvent.ACTION_MOVE:
            handleMoveEvent(dragLayerX, dragLayerY);
            break;
        case MotionEvent.ACTION_UP:
            // Ensure that we've processed a move event at the current pointer location.
            handleMoveEvent(dragLayerX, dragLayerY);

            mHandler.removeCallbacks(mScrollRunnable);
            if (mDragging) {
                drop(dragLayerX, dragLayerY);
            }
            endDrag();
            break;
        case MotionEvent.ACTION_CANCEL:
            cancelDrag();
            break;
        }

        return true;
    }

在这个方法中清楚的可以看见handleMoveEvent()这个方法会在move,up的时候频繁地调用。

现在再来看看这个handleMoveEvent()方法,看看它的庐山真面目:

  1. private void handleMoveEvent(int x, int y) { 
  2.         mDragObject.dragView.move(x, y); 
  3.  
  4.         // Drop on someone? 
  5.         final int[] coordinates = mCoordinatesTemp; 
  6.         DropTarget dropTarget = findDropTarget(x, y, coordinates); 
  7.         mDragObject.x = coordinates[0]; 
  8.         mDragObject.y = coordinates[1]; 
  9.         if (dropTarget != null) { 
  10.             DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject); 
  11.             if (delegate != null) { 
  12.                 dropTarget = delegate; 
  13.             } 
  14.  
  15.             if (mLastDropTarget != dropTarget) { 
  16.                 if (mLastDropTarget != null) { 
  17.                     mLastDropTarget.onDragExit(mDragObject); 
  18.                 } 
  19.                 dropTarget.onDragEnter(mDragObject); 
  20.             } 
  21.             dropTarget.onDragOver(mDragObject); 
  22.         } else
  23.             if (mLastDropTarget != null) { 
  24.                 mLastDropTarget.onDragExit(mDragObject); 
  25.             } 
  26.         } 
  27.         mLastDropTarget = dropTarget; 
  28.  
  29. ··· ··· 
private void handleMoveEvent(int x, int y) {
        mDragObject.dragView.move(x, y);

        // Drop on someone?
        final int[] coordinates = mCoordinatesTemp;
        DropTarget dropTarget = findDropTarget(x, y, coordinates);
        mDragObject.x = coordinates[0];
        mDragObject.y = coordinates[1];
        if (dropTarget != null) {
            DropTarget delegate = dropTarget.getDropTargetDelegate(mDragObject);
            if (delegate != null) {
                dropTarget = delegate;
            }

            if (mLastDropTarget != dropTarget) {
                if (mLastDropTarget != null) {
                    mLastDropTarget.onDragExit(mDragObject);
                }
                dropTarget.onDragEnter(mDragObject);
            }
            dropTarget.onDragOver(mDragObject);
        } else {
            if (mLastDropTarget != null) {
                mLastDropTarget.onDragExit(mDragObject);
            }
        }
        mLastDropTarget = dropTarget;

··· ···
}

这个方法的作用:通过findDropTarget(x, y, coordinates),来判断在哪个拖拽目标里面,然后通过下面的if判断来执行不同的onDragOver,onDragExit等的方法。这样就在相应的类中去做处理,以后的事情就明朗了。这就是Launcher的拖拽事件的分发与处理,用到了MVC的思想,代码阅读起来还是比较顺利的。有图有真相。

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Exception while marshalling C:\Program Files\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\build-tools\32.0.0\package.xml. Probably the SDK is read-only Exception while marshalling C:\Program Files\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools\package.xml. Probably the SDK is read-only Exception while marshalling C:\Program Files\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-31\package.xml. Probably the SDK is read-only Exception while marshalling C:\Program Files\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms\android-32\package.xml. Probably the SDK is read-only Exception while marshalling C:\Program Files\Unity\Hub\Editor\2022.3.3f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\tools\package.xml. Probably the SDK is read-only > Task :launcher:preBuild UP-TO-DATE > Task :unityLibrary:preBuild UP-TO-DATE > Task :launcher:preReleaseBuild UP-TO-DATE > Task :unityLibrary:preReleaseBuild UP-TO-DATE > Task :unityLibrary:compileReleaseAidl NO-SOURCE > Task :launcher:generateReleaseBuildConfig > Task :launcher:compileReleaseAidl NO-SOURCE > Task :launcher:javaPreCompileRelease > Task :launcher:generateReleaseResValues > Task :launcher:createReleaseCompatibleScreenManifests > Task :launcher:extractDeepLinksRelease > Task :launcher:extractProguardFiles > Task :launcher:mergeReleaseJniLibFolders > Task :unityLibrary:mergeReleaseJniLibFolders > Task :unityLibrary:compileReleaseRenderscript NO-SOURCE > Task :unityLibrary:generateReleaseBuildConfig > Task :unityLibrary:generateReleaseResValues > Task :unityLibrary:generateReleaseResources > Task :unityLibrary:packageReleaseResources > Task :unityLibrary:parseReleaseLocalResources > Task :unityLibrary:javaPreCompileRelease > Task :unityLibrary:mergeReleaseShaders > Task :unityLibrary:compileReleaseShaders NO-SOURCE > Task :unityLibrary:generateReleaseAssets UP-TO-DATE > Task :unityLibrary:processReleaseManifest > Task :unityLibrary:mergeReleaseNativeLibs > Task :unityLibrary:packageReleaseAssets > Task :unityLibrary:stripReleaseDebugSymbols > Task :unityLibrary:packageReleaseRenderscript NO-SOURCE > Task :unityLibrary:prepareLintJarForPublish > Task :unityLibrary:prepareReleaseArtProfile > Task :unityLibrary:processReleaseJavaRes > Task :unityLibrary:extractDeepLinksRelease > Task :unityLibrary:writeReleaseAarMetadata > Task :unityLibrary:compileReleaseLibraryResources > Task :unityLibrary:writeReleaseLintModelMetadata > Task :unityLibrary:bundleLibResRelease > Task :unityLibrary:mergeReleaseJavaResource > Task :unityLibrary:copyReleaseJniLibsProjectAndLocalJars > Task :unityLibrary:copyReleaseJniLibsProjectOnly > Task :launcher:compileReleaseRenderscript NO-SOURCE > Task :launcher:generateReleaseResources > Task :launcher:checkReleaseAarMetadata > Task :unityLibrary:generateReleaseRFile > Task :unityLibrary:extractReleaseAnnotations > Task :unityLibrary:compileReleaseJavaWithJavac > Task :unityLibrary:mergeReleaseGeneratedProguardFiles > Task :unityLibrary:mergeReleaseConsumerProguardFiles > Task :unityLibrary:syncReleaseLibJars > Task :unityLibrary:bundleReleaseLocalLintAar > Task :unityLibrary:bundleLibRuntimeToJarRelease > Task :unityLibrary:bundleLibCompileToJarRelease > Task :unityLibrary:createFullJarRelease > Task :launcher:processReleaseMainManifest FAILED See http://g.co/androidstudio/manifest-merger for more information about the manifest merger. > Task :launcher:mergeReleaseResources 42 actionable tasks: 42 executed UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
最新发布
07-21

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值