Android 10.0 Launcher3拖拽图标进入hotseat自适应布局功能实现三

1.前言

在10.0的系统rom定制化开发中,在对于launcher3的一些开发定制中,在对hotseat的一些开发中,需要实现动态hotseat居中
的功能,就是在拖拽图标进入和拖出hotseat,都可以保持hotseat居中的功能,接下来分析下相关功能实现
具体如图:

hotseat


2.Launcher3拖拽图标进入hotseat自适应布局功能实现三的核心类

packages\apps\Launcher3\src\com\android\launcher3\CellLayout.java

3.Launcher3拖拽图标进入hotseat自适应布局功能实现三的核心功能分析和实现

Launcher顾名思义,就是桌面的意思,也是android系统启动后第一个启动的应用程序,
:Launcher3负责管理和展示用户手机桌面上的各个应用程序图标。它通过GridView或者LinearLayout等布局管理器将
图标进行排列,并支持滑动、放大缩小等手势操作
Hotseat也是属于在导航栏底部的BubbleTextView的布局,只是不显示app图标
CellLayout:主屏幕中的每一页,其父布局就是Workspace,左右滑动屏幕,就是每一个CellLayout的变化过程,这个类中有很多处理拖拽相关方法

3.1 新增一部分关于处理拖拽图标重叠的工具类

在实现Launcher3拖拽图标进入hotseat自适应布局功能实现三中,通过分析得知,在进行
一些拖拽的过程中,在只有两个三个hotseat的时候,这时候会出现hotseat重叠的问题,这时候就需要先清除掉hotseat的所有view,然后添加hotseat,

package com.android.launcher3.util;

import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.data.ItemInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import java.util.ArrayList;

public class HotseatController{
    private static final String TAG = "HotseatController";

    private static final int INVALID = -1;

    private boolean mDelayClearEmptyGridFlag = false;

    private CellLayout.CellInfo mBackupDragInfo = null;
    private LauncherAppMonitor mMonitor;
    protected Context mContext;
    public HotseatController(Context context, LauncherAppMonitor monitor) {
		this.mContext = context;
        mMonitor = monitor;
    }

    public boolean isFull(Launcher launcher) {
        if (launcher == null) {
            return false;
        }

        Hotseat hs = launcher.getHotseat();
        int gridCount = getGridCount(launcher);
        for (int i = 0; i < gridCount; i++) {
            int cx = hs.getCellXFromOrder(i);
            int cy = hs.getCellYFromOrder(i);
            if (!hs.isOccupied(cx, cy)) {
                return false;
            }
        }
        return true;
    }

    public boolean clearEmptyGrid(Launcher launcher) {
        if (launcher == null) {
            Log.e(TAG, "launcher is null, clear empty grid failed.");
            return false;
        }

        if (isFull(launcher)) {
            Log.d(TAG, "there are no empty grid in hotseat, no need to clear.");
            return false;
        }

        if (mDelayClearEmptyGridFlag) {
            Log.d(TAG, "mDelayClearEmptyGridFlag is true, to clear empty when it is false.");
            return false;
        }

        ArrayList<View> views = backupHotseatChildren(launcher);
        if (views.size() == 0) {
            return false;
        }

        Hotseat hs = launcher.getHotseat();
        boolean isLandscape = hs.mHasVerticalHotseat;
        int count = views.size();
        int countX = isLandscape ? 1 : Math.max(count, 1);
        int countY = isLandscape ? Math.max(count, 1) : 1;
        if(count>1){
		  hs.removeAllViews();
		}else{
		  hs.removeView(views.get(0));
		}
          hs.setGridSize(countX, countY);
          for (int i = 0; i < count; i++) {
            if (!addViewToHotseat(launcher, views.get(i), i)) {
                Log.e("Hotseat", "failed to add view to hotseat, rank:" + i);
            }
          }
		
        updateFolderIconLeaveBehind(launcher);

        return true;
    }
    public int dip2px(float dpValue,Launcher launcher) {
		final float scale = launcher.getResources().getDisplayMetrics().density;
		return (int) (dpValue * scale + 0.5f);
	}
    private void updateFolderIconLeaveBehind(Launcher launcher) {
        Folder folder = Folder.getOpen(launcher);
        if (folder != null && folder.mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
            folder.getFolderIcon().drawLeaveBehindIfExists();
        }
    }
    public boolean addViewToHotseat(Launcher launcher, View v, int rank) {
        Hotseat hs = launcher.getHotseat();
        if (v != null && rank < getGridCount(launcher)) {
            int cellX = hs.getCellXFromOrder(rank);
            int cellY = hs.getCellYFromOrder(rank);
            if (v.getTag() instanceof ItemInfo) {
                CellLayout.LayoutParams lp = new CellLayout.LayoutParams(cellX, cellY, 1, 1);
                if (hs.addViewToCellLayout(v, -1, v.getId(), lp, true)) {
                    ((ItemInfo)v.getTag()).screenId = rank;
                    if (true) {
                        Log.d(TAG, "update " + ((ItemInfo)v.getTag()).title + " to:" + rank);
                    }
                    return true;
                }
            }
        }
        return false;
    }

    public void resetDragCell(Launcher launcher, CellLayout.CellInfo cellInfo) {
        if (cellInfo == null || cellInfo.cell == null) {
            if (cellInfo != null) {
                Log.d(TAG, "cell is null, can not reset cell to original position.");
            }
            return;
        }

        if (cellInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
            View v = cellInfo.cell;
            Hotseat hs = launcher.getHotseat();
            int gridCount = getGridCount(launcher);
            CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
            int index = getOrderInHotseat(launcher, lp.cellX, lp.cellY, gridCount + 1);
            if (index >= gridCount) {
                insertEmptyGrid(launcher, gridCount);
            } else {
                int cx = hs.getCellXFromOrder(index);
                int cy = hs.getCellYFromOrder(index);
                if (hs.isOccupied(cx, cy)) {
                    insertEmptyGrid(launcher, index);
                }
            }

            if (addViewToHotseat(launcher, v, index)) {
                v.setVisibility(View.VISIBLE);
                Log.d(TAG, "resetDragCell to :" + index);
            }
        }
    }

    public int calculateBestIndex(Launcher launcher, float[] dragViewVisualCenterint,
                                  int findCellX, int findCellY) {
        Hotseat hs = launcher.getHotseat();
        int index = getOrderInHotseat(launcher, findCellX, findCellY);
        int[] cellCenter = new int[2];
        hs.cellToCenterPoint(findCellX, findCellY, cellCenter);
        boolean isDragInEndArea = hs.mHasVerticalHotseat ?
                dragViewVisualCenterint[1] < cellCenter[1] : dragViewVisualCenterint[0] > cellCenter[0];
        return isDragInEndArea ? index + 1 : index;
    }
    public boolean insertEmptyGrid(Launcher launcher, int index) {
        if (launcher == null) {
            Log.d(TAG, "launcher is null, insert empty grid failed.");
            return false;
        }

        if (!canInsert(launcher)) {
            Log.d(TAG, "achieve maximum limit, can not insert empty grid.");
            return false;
        }

        if (!isFull(launcher)) {
            Log.d(TAG, "the hotseat has empty grid, can not insert empty grid.");
            return false;
        }

        ArrayList<View> views = backupHotseatChildren(launcher);
        Hotseat hs = launcher.getHotseat();
        final boolean isLandscape = hs.mHasVerticalHotseat;
        int newCount = views.size() + 1;
        int countX = isLandscape ? 1 : newCount;
        int countY = isLandscape ? newCount : 1;
        if(views.size()>1){
		  hs.removeAllViews();
		}else{
		  hs.removeView(views.get(0));
		}
        hs.setGridSize(countX, countY);
        for (int i = 0; i < views.size(); i++) {
            View view = views.get(i);
            if (i < index) {
                addViewToHotseat(launcher, view, i);
            } else {
                addViewToHotseat(launcher, view, i + 1);
            }

            if (view.getTag() instanceof ItemInfo) {
                launcher.getModelWriter().updateItemInDatabase((ItemInfo) view.getTag());
            }
        }
        return true;
    }

    private ArrayList<View> backupHotseatChildren(Launcher launcher) {
        Hotseat hs = launcher.getHotseat();
        int gridCount = getGridCount(launcher);
        ArrayList<View> views = new ArrayList<>();
        for (int i = 0; i < gridCount; i++) {
            int cx = hs.getCellXFromOrder(i);
            int cy = hs.getCellYFromOrder(i);
            View v = hs.getShortcutsAndWidgets().getChildAt(cx, cy);
            if (hs.isOccupied(cx, cy)) {
                if (v != null) {
                    if (true) {
                        Log.d(TAG, "backup child:" + i);
                    }
                    views.add(v);
                }
            }
        }
        return views;
    }

    public void resetGridIfNeeded(Launcher launcher, int index) {
        if (launcher == null) {
            return;
        }

        if (!isCellOccupied(launcher)) {
            insertEmptyGrid(launcher, index);
        }
    }

    public boolean isCellOccupied(Launcher launcher) {
        if (launcher == null) {
            return false;
        }

        Hotseat hs = launcher.getHotseat();
        int gridCount = getGridCount(launcher);
        if (gridCount == 1) {
            if (!hs.isOccupied(0, 0)) {
                return true;
            }
        }
        return false;
    }

    public int getGridCount(Launcher launcher) {
        if (launcher == null) {
            Log.d(TAG, "launcher is null, can not get grid count.");
            return INVALID;
        }

        Hotseat hs = launcher.getHotseat();
        return hs.mHasVerticalHotseat ? hs.getCountY() : hs.getCountX();
    }

    public boolean canInsert(Launcher launcher) {
        int gridCount = getGridCount(launcher);
        return gridCount < launcher.getDeviceProfile().inv.numHotseatIcons;
    }

    public void backupDragInfo(CellLayout.CellInfo cellInfo) {
        mBackupDragInfo = cellInfo;
    }

    public CellLayout.CellInfo getBackupDragInfo () {
        return mBackupDragInfo;
    }

    public void setDelayClearEmptyGridFlag(boolean falg) {
        mDelayClearEmptyGridFlag = falg;
    }

    public boolean isNeedInterceptHotseatTouch(MotionEvent event, View view) {
        boolean needIntercept = false;
        if (view != null && view.getTag() instanceof ItemInfo) {
            if (((ItemInfo) view.getTag()).container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                int iconSize = 0;
                if (view instanceof FolderIcon) {
                    iconSize = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext).folderIconSizePx;
                } else if (view instanceof BubbleTextView) {
                    iconSize = ((BubbleTextView) view).getIconSize();
                }
                // we'll intercept touch event when touch in blank area in hotseat.
                needIntercept = !getIconRect(iconSize, view)
                        .contains((int) event.getX() + view.getScrollX(),
                                (int) event.getY() + view.getScrollY());
            }
        }
        return needIntercept;
    }

    public static Rect getIconRect(int iconSize, View view) {
        Point center = new Point(view.getScrollX() + (view.getWidth() >> 1),
                view.getScrollY() + view.getPaddingTop() + (iconSize >> 1));
        Rect iconRect = new Rect();

        iconRect.left = center.x - (iconSize >> 1);
        iconRect.top = center.y - (iconSize >> 1);
        iconRect.right = iconRect.left + iconSize;
        iconRect.bottom = iconRect.top + iconSize;
        return iconRect;
    }

    public int getOrderInHotseat(Launcher launcher, int x, int y) {
        return getOrderInHotseat(launcher, x, y, getGridCount(launcher));
    }

    public int getOrderInHotseat(Launcher launcher, int x, int y, int gridCount) {
        if (launcher == null) {
            Log.d(TAG, "launcher is null, can not get order in hotseat.");
            return INVALID;
        }

        return launcher.getHotseat().mHasVerticalHotseat ? (gridCount - y - 1) : x;
    }

}

在实现Launcher3拖拽图标进入hotseat自适应布局功能实现三中,通过分析得知,
在新增的HotseatController.java的相关源码中,在这个类中,主要就是处理当hotseat图标拖拽出
Hotseat的区域时,或者其他图标拖拽进入hotseat的时候,就会调用clearEmptyGrid(Launcher launcher)
来进行相关的拖拽清理图标功能,在增加或减少hotseat的时候,会先清理掉hotseat的所有图标,
然后在addViewToHotseat(launcher, views.get(i), i)插入hotseat到布局中,就实现了去掉hotseat
重复的功能,

package com.android.launcher3.util;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ShortcutInfo;
import android.database.sqlite.SQLiteDatabase;
import android.os.UserHandle;
import android.util.Log;
import android.text.TextUtils;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LauncherAppMonitor  {
    private static final String TAG = "LauncherAppMonitor";

    // We do not need any synchronization for this variable as its only written on UI thread.
    public static final MainThreadInitializedObject<LauncherAppMonitor> INSTANCE =
            new MainThreadInitializedObject<>(LauncherAppMonitor::new);

    private final ArrayList<WeakReference<LauncherAppMonitorCallback>> mCallbacks = new ArrayList<>();

  
    private HotseatController mHotseatController = null;
    private Launcher mLauncher;

    public static LauncherAppMonitor getInstance(final Context context) {
        return INSTANCE.get(context.getApplicationContext());
    }

    public static LauncherAppMonitor getInstanceNoCreate() {
        return INSTANCE.getNoCreate();
    }

    //return null while launcher activity isn't running
    public Launcher getLauncher() {
        return mLauncher;
    }

    public void unregisterCallback(LauncherAppMonitorCallback callback) {
        for (int i = mCallbacks.size() - 1; i >= 0; i--) {
            if (mCallbacks.get(i).get() == callback) {
                synchronized (mCallbacks) {
                    mCallbacks.remove(i);
                    
                }
            }
        }
    }
    public void registerCallback(LauncherAppMonitorCallback callback) {
        // Prevent adding duplicate callbacks
        unregisterCallback(callback);
        synchronized (mCallbacks) {
            mCallbacks.add(new WeakReference<>(callback));
            
        }
    }
    private LauncherAppMonitor(Context context) {
            mHotseatController = new HotseatController(context, this);
    }

    public void onRecentsActivityCreate(BaseDraggingActivity activity) {
         Log.d(TAG,"onRecentsActivityCreate");
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onRecentsActivityCreate(activity);
            }
        }
    }

    public void onRecentsActivityStart() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onRecentsActivityStart();
            }
        }
    }

    public void onQuickstepLauncherStart() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onQuickstepLauncherStart();
            }
        }
    }
    public void onLauncherPreCreate(Launcher launcher) {
        if (mLauncher != null) {
            onLauncherDestroy(mLauncher);
        }
        mLauncher = launcher;

        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherPreCreate(launcher);
            }
        }
    }

    public void onLauncherCreated() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherCreated();
            }
        }
    }

    public void onLauncherPreResume() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherPreResume();
            }
        }
    }

    public void onLauncherResumed() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherResumed();
            }
        }
    }

    public void onLauncherPrePause() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherPrePaused();
            }
        }
    }

    public void onLauncherPaused() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherPaused();
            }
        }
    }

    public void onLauncherStart() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherStart();
            }
        }
    }

    public void onLauncherStop() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherStop();
            }
        }
    }
    public void onLauncherDestroy(Launcher launcher) {
        if (launcher != mLauncher) {
            return;
        }
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherDestroy();
            }
        }
        mLauncher = null;
    }

    public void onLauncherRequestPermissionsResult(int requestCode, String[] permissions,
                                                   int[] grantResults) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }

    public void onSettingsActivityRequestPermissionsResult(int requestCode, String[] permissions,
                                                           int[] grantResults) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onSettingsActivityRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }

    public void onLauncherFocusChanged(boolean hasFocus) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onLauncherFocusChanged(hasFocus);
            }
        }
    }

    public void onReceiveHomeIntent() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onHomeIntent();
            }
        }
    }

    public void onLauncherWorkspaceBindingFinish() {
        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onBindingWorkspaceFinish();
            }
        }
    }

    public void onLauncherAllAppBindingFinish(AppInfo[] apps) {
        if (true) {
            for (AppInfo app : apps) {
                Log.d("Load app ", app.toComponentKey().toString() + "\n");
            }
        }

        for (int i = 0; i < mCallbacks.size(); i++) {
            LauncherAppMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onBindingAllAppFinish(apps);
            }
        }
    }

    public HotseatController getHotseatController() {
        return mHotseatController;
    }

}

在实现Launcher3拖拽图标进入hotseat自适应布局功能实现三中,通过分析得知,
在新增的util下的LauncherAppMonitor的这个类中,这个类的主要功能就是在
Launcher.java中负责绑定app的功能,在每个生命周期执行不同的方法,实现不同的
功能,作为实现功能的辅助类功能

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 13:14:03       101 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 13:14:03       109 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 13:14:03       87 阅读
  4. Python语言-面向对象

    2024-07-21 13:14:03       96 阅读

热门阅读

  1. Allure 和 JUnit 4结合学习

    2024-07-21 13:14:03       23 阅读
  2. vue3 学习笔记17 -- echarts的使用

    2024-07-21 13:14:03       28 阅读
  3. GPT-5一年半后发布

    2024-07-21 13:14:03       27 阅读
  4. 批量下载网易云音乐歌单的Python脚本

    2024-07-21 13:14:03       28 阅读
  5. 力扣1834.单线程CPU

    2024-07-21 13:14:03       25 阅读
  6. from lxml import etree 的功能

    2024-07-21 13:14:03       22 阅读