android framework13-launcher3【02recents】

关注个人简介,技术不迷路~

1.简介

这里主要介绍下点击导航栏的recents按钮,跳转的页面。我以前以为这东西是个单独的app,看了代码才发现,它是launcher的一部分。我们打开源码/packages/apps/Launcher3 目录下,发现除了res,src,清单文件外,还有2个目录,go目录是给低配置的机器用的简化版的代码,quickstep是我们要看的东西。

2.Android.bp

打开Launcher3目录下的这个文件,搜索下android_app,可以发现有4个,也就是说这个工程可以编译4种apk

android_app {
name: "Launcher3",

android_app {
name: "Launcher3Go",

// Build rule for Quickstep app.
android_app {
name: "Launcher3QuickStep",

// Build rule for Launcher3 Go app with quickstep for Android Go devices.
android_app {
name: "Launcher3QuickStepGo",

那么image里用的哪种?这就要看配置了【build/target/product/handheld_system_ext.mk】,官方源码默认的是这个,额,我这编译的是phone用的,可以看到下边用的Launcher3QuickStep

# /system_ext packages
PRODUCT_PACKAGES += \
    Launcher3QuickStep \
    Provision \
    Settings \
    StorageManager \
    SystemUI \
    WallpaperCropper \

3.layout

底层布局还是上一篇里用到launcher.xml,里边有个这样的include,在默认的res下是空的,在quickstep里重写了


        

    

image.png

上图的1和2分别是下边的3.1和3.2布局

3.1.overview_panel.xml




    

    



3.2.overview_actions_container.xml



    

        

        

4.LauncherState

顾名思义,就是桌面的状态,有10种,这里就注释下我了解的几种

private static final LauncherState[] sAllStates = new LauncherState[10];

    public static String stateOrdinalToString(int ordinal) {
        switch (ordinal) {
            case NORMAL_STATE_ORDINAL:
                return "Normal";//点击home键回到的页面就是,默认启动就是这个
            case SPRING_LOADED_STATE_ORDINAL:
                return "SpringLoaded";//拖动图标的时候,顶部功能按钮(removed)出现的时候
            case OVERVIEW_STATE_ORDINAL:
                return "Overview";//recents要显示的
            case OVERVIEW_MODAL_TASK_STATE_ORDINAL:
                return "OverviewModal";
            case QUICK_SWITCH_STATE_ORDINAL:
                return "QuickSwitch";
            case ALL_APPS_STATE_ORDINAL:
                return "AllApps";//桌面上划显示所有app图标的那个
            case BACKGROUND_APP_STATE_ORDINAL:
            //launcher在后台的时候,点击recents,先变成这个状态,再变成overview转改
                return "Background";
            case HINT_STATE_ORDINAL:
                return "Hint";
            case HINT_STATE_TWO_BUTTON_ORDINAL:
                return "Hint2Button";
            case OVERVIEW_SPLIT_SELECT_ORDINAL:
                return "OverviewSplitSelect";
            default:
                return "Unknown";
        }
    }

5.LauncherRecentsView

点击recents按钮显示的页面就是这个容器了,这里通过在构造方法里添加监听的方法,获取state状态,同步修改自己的可见性。

public class LauncherRecentsView extends RecentsView
        implements StateListener {
        
public LauncherRecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
    //这里添加了state改变的监听
    mActivity.getStateManager().addStateListener(this);
}

public void startHome() {
//home页就是normal状态
    mActivity.getStateManager().goToState(NORMAL);
    AbstractFloatingView.closeAllOpenViews(mActivity, mActivity.isStarted());
}
//这个就是回调,状态改变开始
public void onStateTransitionStart(LauncherState toState) {
    setOverviewStateEnabled(toState.overviewUi);
    setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
    setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
    if (toState == OVERVIEW_MODAL_TASK) {
        setOverviewSelectEnabled(true);
    }
    setFreezeViewVisibility(true);
}

//这个就是回调,状态改变结束
public void onStateTransitionComplete(LauncherState finalState) {
    if (finalState == NORMAL || finalState == SPRING_LOADED) {
        reset();
    }
    //点击recent的话状态就是overview
    boolean isOverlayEnabled = finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK;
    setOverlayEnabled(isOverlayEnabled);
    setFreezeViewVisibility(false);
    if (finalState != OVERVIEW_MODAL_TASK) {
        setOverviewSelectEnabled(false);
    }

    if (isOverlayEnabled) {
        runActionOnRemoteHandles(remoteTargetHandle ->
                remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
    }
}    

6.RecentsView

可以看到,又是继承的PagedView,添加child就可以左右滑动了。

public abstract class RecentsView,
        STATE_TYPE extends BaseState> extends PagedView implements Insettable,
        TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
        TaskVisualsChangeListener {
        
public RecentsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
        BaseActivityInterface sizeStrategy) {
    super(context, attrs, defStyleAttr);
    setEnableFreeScroll(true);
    mSizeStrategy = sizeStrategy;
    mActivity = BaseActivity.fromContext(context);
   //..数据的获取都在这model里
    mModel = RecentsModel.INSTANCE.get(context);
    mIdp = InvariantDeviceProfile.INSTANCE.get(context);
    //加载clearButton布局
    mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
            .inflate(R.layout.overview_clear_all_button, this, false);
    mClearAllButton.setOnClickListener(this::dismissAllTasks);
    //缓存一些我们要用到的view
    mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
            10 /* initial size */);
    mGroupedTaskViewPool = new ViewPool<>(context, this,
            R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
    mDesktopTaskViewPool = new ViewPool<>(context, this, R.layout.task_desktop,
            5 /* max size */, 1 /* initial size */);

    //初始化empty相关的图标和文字
    mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
    mEmptyIcon.setCallback(this);
    mEmptyMessage = context.getText(R.string.recents_empty_message);
    mEmptyMessagePaint = new TextPaint();
    mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
    mEmptyMessagePaint.setTextSize(getResources()
            .getDimension(R.dimen.recents_empty_message_text_size));
    mEmptyMessagePaint.setTypeface(Typeface.create(Themes.getDefaultBodyFont(context),
            Typeface.NORMAL));
    mEmptyMessagePaint.setAntiAlias(true);
    mEmptyMessagePadding = getResources()
            .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
    setWillNotDraw(false);
    updateEmptyMessage();
    //...
}

6.1.init

初始化action view

    public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
        mActionsView = actionsView;
        mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
        mSplitSelectStateController = splitController;
    }

QuickstepLauncher.java里会调用

    protected void setupViews() {
        super.setupViews();

        mActionsView = findViewById(R.id.overview_actions_view);
        RecentsView overviewPanel = getOverviewPanel();
        mSplitSelectStateController =
                new SplitSelectStateController(this, mHandler, getStateManager(),
                        getDepthController(), getStatsLogManager());
        //这调用
        overviewPanel.init(mActionsView, mSplitSelectStateController);
    //..
        mActionsView.updateDimension(getDeviceProfile(), overviewPanel.getLastComputedTaskSize());
        mActionsView.updateVerticalMargin(DisplayController.getNavigationMode(this));

6.2.onAttachedToWindow

主要是添加一堆监听

    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        updateTaskStackListenerState();
        mModel.getThumbnailCache().getHighResLoadingState().addCallback(this);
        mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
        mSyncTransactionApplier = new SurfaceTransactionApplier(this);
        runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle.getTransformParams()
                .setSyncTransactionApplier(mSyncTransactionApplier));
        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
        mIPipAnimationListener.setActivityAndRecentsView(mActivity, this);
        SystemUiProxy.INSTANCE.get(getContext()).setPipAnimationListener(
                mIPipAnimationListener);
        mOrientationState.initListeners();
        mTaskOverlayFactory.initListeners();
    }

6.3.maybeDrawEmptyMessage

public void draw(Canvas canvas) {
    maybeDrawEmptyMessage(canvas);
    super.draw(canvas);
}

//

    protected void maybeDrawEmptyMessage(Canvas canvas) {
        if (mShowEmptyMessage && mEmptyTextLayout != null) {
            // Offset to center in the visible (non-padded) part of RecentsView
            mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
                    mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
            canvas.save();
            canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
                    (mTempRect.top - mTempRect.bottom) / 2);
            mEmptyIcon.draw(canvas);
            canvas.translate(mEmptyMessagePadding,
                    mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
            mEmptyTextLayout.draw(canvas);
            canvas.restore();
        }
    }

6.4.taskView可见性

看效果图可以知道,最多可以显示3个taskView,中间一个完整显示的,以及两边部分显示的。

    public boolean isTaskViewVisible(TaskView tv) {
        if (showAsGrid()) {
            int screenStart = mOrientationHandler.getPrimaryScroll(this);
            int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
            return isTaskViewWithinBounds(tv, screenStart, screenEnd);
        } else {
            // For now, just check if it's the active task or an adjacent task
            return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
        }
    }

    public boolean isTaskViewFullyVisible(TaskView tv) {
        if (showAsGrid()) {
            int screenStart = mOrientationHandler.getPrimaryScroll(this);
            int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
            return isTaskViewFullyWithinBounds(tv, screenStart, screenEnd);
        } else {
            // For now, just check if it's the active task
            return indexOfChild(tv) == getNextPage();
        }
    }

6.5.resetTaskVisuals

重新设置下taskView的属性,在重新add child以后会执行

    public void resetTaskVisuals() {
        for (int i = getTaskViewCount() - 1; i >= 0; i--) {
            TaskView taskView = requireTaskViewAt(i);
            if (mIgnoreResetTaskId != taskView.getTaskIds()[0]) {
                taskView.resetViewTransforms();
                taskView.setIconScaleAndDim(mTaskIconScaledDown ? 0 : 1);
                taskView.setStableAlpha(mContentAlpha);
                taskView.setFullscreenProgress(mFullscreenProgress);
                taskView.setModalness(mTaskModalness);
                taskView.setTaskThumbnailSplashAlpha(mTaskThumbnailSplashAlpha);
            }
        }
        // resetTaskVisuals is called at the end of dismiss animation which could update
        // primary and secondary translation of the live tile cut out. We will need to do so
        // here accordingly.
        runActionOnRemoteHandles(remoteTargetHandle -> {
            TaskViewSimulator simulator = remoteTargetHandle.getTaskViewSimulator();
            simulator.taskPrimaryTranslation.value = 0;
            simulator.taskSecondaryTranslation.value = 0;
            simulator.fullScreenProgress.value = 0;
            simulator.recentsViewScale.value = 1;
        });
        // Similar to setRunningTaskHidden below, reapply the state before runningTaskView is
        // null.
        if (!mRunningTaskShowScreenshot) {
            setRunningTaskViewShowScreenshot(mRunningTaskShowScreenshot);
        }
        if (mRunningTaskTileHidden) {
            setRunningTaskHidden(mRunningTaskTileHidden);
        }

        updateCurveProperties();
        // Update the set of visible task's data
        loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
        setTaskModalness(0);
        setColorTint(0);
    }

6.6.setVisibility

设置自己的可见性,并同步修改action view的可见性

    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
        if (mActionsView != null) {
            mActionsView.updateHiddenFlags(HIDDEN_NO_RECENTS, visibility != VISIBLE);
            if (visibility != VISIBLE) {
                mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
            }
        }
    }

>调用的地方

  • 设置透明度,为0的时候就不可见了。

      public void setContentAlpha(float alpha) {
          if (alpha == mContentAlpha) {
              return;
          }
          alpha = Utilities.boundToRange(alpha, 0, 1);
          mContentAlpha = alpha;
          int runningTaskId = getTaskIdsForRunningTaskView()[0];
          for (int i = getTaskViewCount() - 1; i >= 0; i--) {
              TaskView child = requireTaskViewAt(i);
              int[] childTaskIds = child.getTaskIds();
              if (!mRunningTaskTileHidden ||
                      (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
                  child.setStableAlpha(alpha);
              }
          }
          mClearAllButton.setContentAlpha(mContentAlpha);
          int alphaInt = Math.round(alpha * 255);
          mEmptyMessagePaint.setAlpha(alphaInt);
          mEmptyIcon.setAlpha(alphaInt);
          mActionsView.getContentAlpha().setValue(mContentAlpha);
    
          if (alpha > 0) {
              setVisibility(VISIBLE);
          } else if (!mFreezeViewVisibility) {
          //alpha 为0的时候就不可见了。
              setVisibility(INVISIBLE);
          }
      }
    
    

初始化的时候透明度为0,也就是默认启动的时候是不可见的

    public void init(OverviewActionsView actionsView,
            SplitSelectStateController splitPlaceholderView) {
        super.init(actionsView, splitPlaceholderView);
        setContentAlpha(0);
    }

上边的方法是在QuickstepLauncher.java的setupViews的方法里调用的

        overviewPanel.init(mActionsView, mSplitSelectStateController);

  • 下边这个方法是动画开始的时候会冻结view的可见性,结束的时候会恢复为false

      public void setFreezeViewVisibility(boolean freezeViewVisibility) {
          if (mFreezeViewVisibility != freezeViewVisibility) {
              mFreezeViewVisibility = freezeViewVisibility;
              if (!mFreezeViewVisibility) {
                  setVisibility(mContentAlpha > 0 ? VISIBLE : INVISIBLE);
              }
          }
      }
    
    

>透明度的改变

前边有讲过,launcher的view显示有10种状态,我们也简单分析了几种常见的。overview的可见性也是通过改变state来实现的。有专门的StateManager类来修改state,还有state的处理类。这里简单贴下非动画的改变状态的情况,动画的麻烦,还有listener,就不看了。

BaseRecentsViewStateController.java

    public void setState(@NonNull LauncherState state) {
        float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
        RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
        TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);

//透明度就是这个了。overview状态,透明度为1可见,非overview状态,透明度为0,不可见
        getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
        getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
        RECENTS_GRID_PROGRESS.set(mRecentsView,
                state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
        TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, state.showTaskThumbnailSplash() ? 1f : 0f);
    }

RecentsViewStateController.java

    FloatProperty getContentAlphaProperty() {
        return CONTENT_ALPHA;
    }

//看下set方法,可以看到,就是设置recentsView的透明度,上边有贴代码,透明度为0就不可见了。

    public static final FloatProperty CONTENT_ALPHA =
            new FloatProperty("contentAlpha") {
                @Override
                public void setValue(RecentsView view, float v) {
                    view.setContentAlpha(v);
                }

                @Override
                public Float get(RecentsView view) {
                    return view.getContentAlpha();
                }
            };

6.7.数据的获取

state改变的回调哪里调用的后边再分析。

>onStateTransitionStart

    public void onStateTransitionStart(LauncherState toState) {
    //overview状态的话这个为true
        setOverviewStateEnabled(toState.overviewUi);
        setOverviewGridEnabled(toState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
        setOverviewFullscreenEnabled(toState.getOverviewFullscreenProgress() == 1);
        if (toState == OVERVIEW_MODAL_TASK) {
            setOverviewSelectEnabled(true);
        }
        setFreezeViewVisibility(true);
    }

>setOverviewStateEnabled

overview状态的话这个为true,非overview的时候又成了false

    public void setOverviewStateEnabled(boolean enabled) {
        mOverviewStateEnabled = enabled;
        //更新数据
        updateTaskStackListenerState();
        mOrientationState.setRotationWatcherEnabled(enabled);
        if (!enabled) {
            // Reset the running task when leaving overview since it can still have a reference to
            // its thumbnail
            mTmpRunningTasks = null;
            mSplitBoundsConfig = null;
        }
        updateLocusId();
    }

>updateTaskStackListenerState

mHandleTaskStackChanges默认是false的,当我们点击recent以后就要显示overview了,这时候handleTaskStackChanges为true,后边几个条件都是true的。当退出overview的时候这里的值又成了false,下次再次进入overview的时候又成了true

    private void updateTaskStackListenerState() {
        boolean handleTaskStackChanges = mOverviewStateEnabled && isAttachedToWindow()
                && getWindowVisibility() == VISIBLE;
        if (handleTaskStackChanges != mHandleTaskStackChanges) {
            mHandleTaskStackChanges = handleTaskStackChanges;
            if (handleTaskStackChanges) {
                reloadIfNeeded();
            }
        }
    }

//reloadIfNeeded调用的是TaskList的方法获取数据

    public int getTasks(Consumer> callback, Predicate filter) {
        return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
    }


>reloadIfNeeded

这个方法在每次overview显示的时候都会调用,

    public void reloadIfNeeded() {
        if (!mModel.isTaskListValid(mTaskListChangeId)) {
            mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
                    .getFilter(mFilterState.getPackageNameToFilter()));
        }
    }

#onStateTransitionComplete

可以看到,每次退出overview回到normal状态的时候,会调用reset

    public void onStateTransitionComplete(LauncherState finalState) {
        if (finalState == NORMAL || finalState == SPRING_LOADED) {
            // Clean-up logic that occurs when recents is no longer in use/visible.
            reset();
        }

#reset

id又改成-1了

    public void reset() {
        setCurrentTask(-1);
        mCurrentPageScrollDiff = 0;
        mIgnoreResetTaskId = -1;
        mTaskListChangeId = -1;
        mFocusedTaskViewId = -1;


6.8.applyLoadPlan

这个就是获取数据以后的回调了,主要处理child的添加

    protected void applyLoadPlan(ArrayList taskGroups) {
        if (mPendingAnimation != null) {
            mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
            return;
        }

        mLoadPlanEverApplied = true;
        //数据为null的情况
        if (taskGroups == null || taskGroups.isEmpty()) {
        //移除所有相关的view
            removeTasksViewsAndClearAllButton();
            //显示no recent的提示
            onTaskStackUpdated();
            // With all tasks removed, touch handling in PagedView is disabled and we need to reset
            // touch state or otherwise values will be obsolete.
            resetTouchState();
            if (isPageScrollsInitialized()) {
                onPageScrollsInitialized();
            }
            return;
        }

    //..中间一堆代码就是获取容器旧的状态,方便后边恢复,因为下边会移除所有的child
        removeAllViews();

    //..

        // update the map of instance counts
        mFilterState.updateInstanceCountMap(taskGroups);
    //根据数据,循环添加child,主要有两种,single,group,其实还有个desktop不知道啥玩意
        for (int i = taskGroups.size() - 1; i >= 0; i--) {
            GroupTask groupTask = taskGroups.get(i);
            boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null
                    && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id);

          
            TaskView taskView;
            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
                // If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
                // to be a temporary container for the remaining task.
                taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
            } else {
                taskView = getTaskViewFromPool(groupTask.taskViewType);
            }
            //添加到容器
            addView(taskView);

    //下边就是绑定数据了
            if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
                if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
                    taskView.bind(groupTask.task2, mOrientationState);
                } else {
                    taskView.bind(groupTask.task1, mOrientationState);
                }
            } else if (isRemovalNeeded) {
                // If the task we need to remove is not part of a pair, bind it to the TaskView
                // first (to prevent problems), then remove the whole thing.
                taskView.bind(groupTask.task1, mOrientationState);
                removeView(taskView);
            } else if (taskView instanceof GroupedTaskView) {
                boolean firstTaskIsLeftTopTask =
                        groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
                Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
                Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;

                ((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
                        groupTask.mSplitBounds);
            } else if (taskView instanceof DesktopTaskView) {
                ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
                        mOrientationState);
            } else {
                taskView.bind(groupTask.task1, mOrientationState);
            }

            // enables instance filtering if the feature flag for it is on
            if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
                taskView.setUpShowAllInstancesListener();
            }
        }
        //添加clearAll button
        if (!taskGroups.isEmpty()) {
            addView(mClearAllButton);
        }

        // Keep same previous focused task
        TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
        // If the list changed, maybe the focused task doesn't exist anymore
        if (newFocusedTaskView == null && getTaskViewCount() > 0) {
            newFocusedTaskView = getTaskViewAt(0);
        }
        mFocusedTaskViewId = newFocusedTaskView != null ?
                newFocusedTaskView.getTaskViewId() : -1;
        updateTaskSize();
        updateChildTaskOrientations();

        TaskView newRunningTaskView = null;
        if (runningTaskId != -1) {
            // Update mRunningTaskViewId to be the new TaskView that was assigned by binding
            // the full list of tasks to taskViews
            newRunningTaskView = getTaskViewByTaskId(runningTaskId);
            if (newRunningTaskView != null) {
                mRunningTaskViewId = newRunningTaskView.getTaskViewId();
            } else {
                mRunningTaskViewId = -1;
            }
        }

        int targetPage = -1;
        if (mNextPage != INVALID_PAGE) {
            // Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
            mCurrentPage = previousCurrentPage;
            if (currentTaskId != -1) {
                currentTaskView = getTaskViewByTaskId(currentTaskId);
                if (currentTaskView != null) {
                    targetPage = indexOfChild(currentTaskView);
                }
            }
        } else {
            // Set the current page to the running task, but not if settling on new task.
            if (runningTaskId != -1) {
                targetPage = indexOfChild(newRunningTaskView);
            } else if (getTaskViewCount() > 0) {
                targetPage = indexOfChild(requireTaskViewAt(0));
            }
        }
        if (targetPage != -1 && mCurrentPage != targetPage) {
            int finalTargetPage = targetPage;
            runOnPageScrollsInitialized(() -> {
                setCurrentPage(finalTargetPage);
            });
        }

        if (mIgnoreResetTaskId != -1 &&
                getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
            mIgnoreResetTaskId = -1;
        }
        //taskView 属性设置
        resetTaskVisuals();
        onTaskStackUpdated();
        updateEnabledOverlays();
        if (isPageScrollsInitialized()) {
            onPageScrollsInitialized();
        }
    }

7.TaskView

这个就是recent里边显示的控件了,加载的布局是这个

7.1.task.xml



    

    

    


7.2.TaskView

public class TaskView extends FrameLayout implements Reusable {

>onClick

    private void onClick(View view) {
        if (getTask() == null) {
            return;
        }
        if (confirmSecondSplitSelectApp()) {
            return;
        }
        launchTasks();
    }

>bind

这个就是绑定数据了,6.8里有用到

    public void bind(Task task, RecentsOrientedState orientedState) {
        cancelPendingLoadTasks();
        mTask = task;
        mTaskIdContainer[0] = mTask.key.id;
        mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
                mIconView, STAGE_POSITION_UNDEFINED);
        mSnapshotView.bind(task);
        setOrientationState(orientedState);
    }

8.GroupedTaskView

就是一个屏幕同时显示2个app的情况。 我们可以在recents列表里,点击某个app上边的图标,有个弹框,选择split top,完事这个app就显示在顶部了,这时候再选一个要同时显示的app,在其缩略图上点一下,就能看到这种效果了。

public class GroupedTaskView extends TaskView {

[图片上传失败...(image-94666b-1687158554079)]

8.1.task_group.xml

可以看到,有2个用来显示icon,2个用来显示缩略图



    

    
        
    
    

    

    


9.RecentsModel.java

   private RecentsModel(Context context) {
        mContext = context;
        //app列表
        mTaskList = new RecentTasksList(MAIN_EXECUTOR,
                context.getSystemService(KeyguardManager.class),
                SystemUiProxy.INSTANCE.get(context));

        IconProvider iconProvider = new IconProvider(context);
        //app的图标
        mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
        mIconCache.registerTaskVisualsChangeListener(this);
        //app的缩略图
        mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);

        //这个很重要,监听task改变的回调设置
        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
        iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
    }

10.recent启动的流程

在03NavigationBar的4.7小节的recents点击事件里有讲到 最终的事件处理是交给一个服务了,再简单贴下相关的代码

OverviewProxyService.java

private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
    private void internalConnectToCurrentUser() {
        //...
        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
                .setPackage(mRecentsComponentName.getPackageName());
        try {
            mBound = mContext.bindServiceAsUser(launcherServiceIntent,
                    mOverviewServiceConnection,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                    UserHandle.of(mUserTracker.getUserId()));
        } catch (SecurityException e) {
//...
    }

相关的服务

10.1.TouchInteractionService.java

        
            
                
            
        

TouchInteractionService里的binder如下

    private final TISBinder mTISBinder = new TISBinder();

//TISBinder

    public class TISBinder extends IOverviewProxy.Stub {
    //...
    //这个就是recents按钮点击以后调用的代码
    public void onOverviewToggle() {
    //...
        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
        //这里
        mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
    }

>onUserUnlocked

解锁屏幕以后的回调

    public void onUserUnlocked() {
        mTaskAnimationManager = new TaskAnimationManager(this);
        //oberver类
        mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
        //初始化了helper类
        mOverviewCommandHelper = new OverviewCommandHelper(this,
                mOverviewComponentObserver, mTaskAnimationManager);
                
        mResetGestureInputConsumer = new ResetGestureInputConsumer(
                mTaskAnimationManager, mTaskbarManager::getCurrentActivityContext);
        mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
        mInputConsumer.registerInputConsumer();
        onSystemUiFlagsChanged(mDeviceState.getSystemUiStateFlags());
        onAssistantVisibilityChanged();

        // Initialize the task tracker
        TopTaskTracker.INSTANCE.get(this);

        // Temporarily disable model preload
        // new ModelPreload().start(this);
        resetHomeBounceSeenOnQuickstepEnabledFirstTime();

        mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
        onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
    }

10.2.OverviewCommandHelper.java

    private > boolean executeCommand(CommandInfo cmd) {
        BaseActivityInterface activityInterface =
                mOverviewComponentObserver.getActivityInterface();
                //只有overview在前台的话不为null,
        RecentsView recents = activityInterface.getVisibleRecentsView();
        //这里只分析下点击recents按钮的逻辑,其他type不研究
        if (recents == null) {
    //..
        } else {
            switch (cmd.type) {
            //..
                case TYPE_TOGGLE://recents已经显示的情况,再点击就会执行这个
                    return launchTask(recents, getNextTask(recents), cmd);
                case TYPE_HOME:
                    recents.startHome();
                    return true;
            }
        }        
        //第一种情况,launcher在前台,这里基本会返回true
      if (activityInterface.switchToRecentsIfVisible(completeCallback)) {
        // If successfully switched, wait until animation finishes
        return false;
    }          
    //第二种,launcher不在前台,上边的if结果是false,会继续往下走
        final T activity = activityInterface.getCreatedActivity();
        if (activity != null) {
            InteractionJankMonitorWrapper.begin(
                    activity.getRootView(),
                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
        }
    //..
    //最终一般会走else这里,启动intent
        } else {
            Intent intent = new Intent(interactionHandler.getLaunchIntent());
            intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
            cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
                    gestureState, intent, interactionHandler);
            interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
            cmd.mActiveCallbacks.addListener(recentAnimListener);
        }
        

>activityInterface

这里贴下activityinterface的代码,

            mActivityInterface = LauncherActivityInterface.INSTANCE;

//LauncherActivityInterface.java

    @Override
    public RecentsView getVisibleRecentsView() {
        Launcher launcher = getVisibleLauncher();
        //非overview状态的话返回null,overview状态就返回对应的recentView
        RecentsView recentsView =
                launcher != null && launcher.getStateManager().getState().overviewUi
                        ? launcher.getOverviewPanel() : null;
        if (recentsView == null || (!launcher.hasBeenResumed()
                && recentsView.getRunningTaskViewId() == -1)) {
            // If live tile has ended, return null.
            return null;
        }
        return recentsView;
    }

>launchTask

recentView显示的时候,再次点击recents按钮,就会打开某个历史app,具体打开哪个看下边的逻辑代码

    private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
        RunnableList callbackList = null;
        if (taskView != null) {
            taskView.setEndQuickswitchCuj(true);
            callbackList = taskView.launchTasks();
        }

        if (callbackList != null) {
            callbackList.add(() -> scheduleNextTask(cmd));
            return false;
        } else {
        //可以看到taskView为null的话,就直接显示桌面了。
            recents.startHome();
            return true;
        }
    }

#getNextTask

看下上边的taskView的获取逻辑,先查找有没有runningTaskView,没有的话直接返回第一个,有的话,taskid加1查找nextTaskView,有的话返回,没有的话就还用runningtaskView

    private TaskView getNextTask(RecentsView view) {
        final TaskView runningTaskView = view.getRunningTaskView();

        if (runningTaskView == null) {
            return view.getTaskViewAt(0);
        } else {
            final TaskView nextTask = view.getNextTaskView();
            return nextTask != null ? nextTask : runningTaskView;
        }
    }

>情况一launcher在前台

测试步骤: 点击home键回到桌面,然后点击recents按钮

launcher在前台的逻辑 LauncherActivityInterface.java

    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
        Launcher launcher = getVisibleLauncher();
        if (launcher == null) {
            return false;//在前台的话,这里不会为null的,所以会走到下边
        }
        closeOverlay();
        //会走这里
        launcher.getStateManager().goToState(OVERVIEW,
                launcher.getStateManager().shouldAnimateStateChange(),
                onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
        return true;
    }

goToState里会会调listener里的方法,我们的RecentsView里就有添加listener,所以会做出对应的操作,显示或者隐藏自己。

>情况二launcher在后台?

测试步骤:随便打开一个app,然后点击recents按钮。

启动的intent内容

Intent { act=android.intent.action.MAIN 
cat=[android.intent.category.HOME] 
flg=0x10000000 
pkg=com.android.launcher3 cmp=com.android.launcher3/.uioverrides.QuickstepLauncher 
(has extras) }

代码的话是这个

    private static Intent createHomeIntent() {
        return new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_HOME)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }

对应的是launcher3目录下的quickstep目录下的AndroidManifest-launcher.xml文件里

        
            
                
                
                
                
                
                
            
        

看完intent的内容,想当然的以为这个就是跳转到QuickstepLauncher,结果我在onResume里添加日志啥都没打印,可recentView页面确实出来了,这个页面也是launcher页面的view。后边一路顺着和intent有关的代码看了下,啥也看不出来,先简单记录下相关的.

后边有看到SurfaceController,感觉是把launcher绘制出来了,顶层那个activity没有绘制或者绘制到底层去了,到底咋弄的期待高手了。

#1 else

        // ----------#2
     cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
                    gestureState, intent, interactionHandler);
        interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
      //总共加了4个listener,随便注释一个,overview页面都出不来,里边的回调太复杂了,啥也看不懂。
        cmd.mActiveCallbacks.addListener(recentAnimListener);

#2 startRecentsAnimation

TaskAnimationManager.java

    final BaseActivityInterface activityInterface = gestureState.getActivityInterface();
    mLastGestureState = gestureState;
    mCallbacks = new RecentsAnimationCallbacks(SystemUiProxy.INSTANCE.get(mCtx),
            activityInterface.allowMinimizeSplitScreen());

    mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
            @Override
            public void onRecentsAnimationStart(RecentsAnimationController controller,
                    RecentsAnimationTargets targets) {
                activityInterface.runOnInitBackgroundStateUI(() ->
                        interactionHandler.onGestureEnded(0, new PointF(), new PointF()));
                cmd.removeListener(this);
            }        
    //...
    mCallbacks.addListener(gestureState);
    mCallbacks.addListener(listener);
     //...
    } else {
        UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
                .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
    }
    gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
    return mCallbacks;
}

#3 RecentsAnimationCallbacks

4里的回调会调用这个方法,这个方法里可以看到,最终主要是调用listener的方法,我们在#1#2里添加了4个listener的。

    public final void onAnimationStart(RecentsAnimationControllerCompat animationController,
            RemoteAnimationTarget[] appTargets,
            RemoteAnimationTarget[] wallpaperTargets,
            Rect homeContentInsets, Rect minimizedHomeBounds) {
        mController = new RecentsAnimationController(animationController,
                mAllowMinimizeSplitScreen, this::onAnimationFinished);

        if (mCancelled) {
            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
                    mController::finishAnimationToApp);
        } else {
            RemoteAnimationTarget[] nonAppTargets =
                    mSystemUiProxy.onGoingToRecentsLegacy(appTargets);
            if (nonAppTargets == null) {
                nonAppTargets = new RemoteAnimationTarget[0];
            }
            final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
                    wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds);

            Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {

                for (RecentsAnimationListener listener : getListeners()) {
                    listener.onRecentsAnimationStart(mController, targets);
                }
            });
        }
    }

#4 ActivityManagerWrapper.java

    public boolean startRecentsActivity(
            Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
        try {
            IRecentsAnimationRunner runner = null;
            if (animationHandler != null) {
                runner = new IRecentsAnimationRunner.Stub() {
                    @Override
                    public void onAnimationStart(IRecentsAnimationController controller,
                            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
                            Rect homeContentInsets, Rect minimizedHomeBounds) {
                        final RecentsAnimationControllerCompat controllerCompat =
                                new RecentsAnimationControllerCompat(controller);
                                //这里的handler就是#2里的mCallbacks,会调用mCallbacks添加的listener
                        animationHandler.onAnimationStart(controllerCompat, apps,
                                wallpapers, homeContentInsets, minimizedHomeBounds);
                    }

                    @Override
                    public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
                        animationHandler.onAnimationCanceled(
                                ThumbnailData.wrap(taskIds, taskSnapshots));
                    }

                    @Override
                    public void onTasksAppeared(RemoteAnimationTarget[] apps) {
                        animationHandler.onTasksAppeared(apps);
                    }
                };
            }
            getService().startRecentsActivity(intent, eventTime, runner);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

#5 getService()

ActivityTaskManager.java

    public static IActivityTaskManager getService() {
        return IActivityTaskManagerSingleton.get();
    }

    @UnsupportedAppUsage(trackingBug = 129726065)
    private static final Singleton IActivityTaskManagerSingleton =
            new Singleton() {
                @Override
                protected IActivityTaskManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
                    return IActivityTaskManager.Stub.asInterface(b);
                }
            };

最终实现类是下边这个类

#6 ActivityTaskManagerService.java

    public void startRecentsActivity(Intent intent, long eventTime,
            @Nullable IRecentsAnimationRunner recentsAnimationRunner) {
        enforceTaskPermission("startRecentsActivity()");
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
                final String recentsFeatureId = mRecentTasks.getRecentsComponentFeatureId();
                final int recentsUid = mRecentTasks.getRecentsComponentUid();
                final WindowProcessController caller = getProcessController(callingPid, callingUid);

                // Start a new recents animation
                final RecentsAnimation anim = new RecentsAnimation(this, mTaskSupervisor,
                        getActivityStartController(), mWindowManager, intent, recentsComponent,
                        recentsFeatureId, recentsUid, caller);
                if (recentsAnimationRunner == null) {
                } else {//-----------走的else,#7
                    anim.startRecentsActivity(recentsAnimationRunner, eventTime);
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

#7 RecentsAnimation.java

    void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) {

    //...
        // If the activity is associated with the root recents task, then try and get that first
        Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
                mTargetActivityType);
        ActivityRecord targetActivity = getTargetActivity(targetRootTask);
        final boolean hasExistingActivity = targetActivity != null;
        if (hasExistingActivity) {
            mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask);
           
            if (mRestoreTargetBehindRootTask == null
                    && targetRootTask.getTopMostTask() == targetActivity.getTask()) {
                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
                return;
            }
        }

        if (targetActivity == null || !targetActivity.isVisibleRequested()) {
            mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(
                    true /* forceSend */, targetActivity);
        }

        final LaunchingState launchingState =
                mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);

        setProcessAnimating(true);
        mService.deferWindowLayout(); // 延迟窗口布局
        try {
            if (hasExistingActivity) {
                // Move the recents activity into place for the animation if it is not top most
                mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask);
//...
            } else {
        //...
            }


            targetActivity.mLaunchTaskBehind = true;
            mLaunchedTargetActivity = targetActivity;

            targetActivity.intent.replaceExtras(mTargetIntent);

            // ---------#8
            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
                    this, mDefaultTaskDisplayArea.getDisplayId(),
                    mTaskSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity);

            mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);

    //..
            // Register for root task order changes
            mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this);
        } catch (Exception e) {
           
        } finally {
            mService.continueWindowLayout();//恢复窗口布局
           
        }
    }

#8 initializeRecentsAnimation

    void initializeRecentsAnimation(int targetActivityType,
            IRecentsAnimationRunner recentsAnimationRunner,
            RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
            SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) {
        mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
                callbacks, displayId);
        mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
        mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity);
    }

//RecentsAnimationController

    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds,
            ActivityRecord targetActivity) {
        mTargetActivityType = targetActivityType;
        mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);


        final ArrayList visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
                .getVisibleTasks();
        final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
                .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
        if (targetRootTask != null) {
            final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
                { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
                    visibleTasks);
            targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
            c.recycle();
        }

        final int taskCount = visibleTasks.size();
        for (int i = taskCount - 1; i >= 0; i--) {
            final Task task = visibleTasks.get(i);
            if (skipAnimation(task)) {
                continue;
            }
            // --------------------#9
            addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
                    (type, anim) -> task.forAllWindows(win -> {
                        win.onAnimationFinished(type, anim);
                    }, true /* traverseTopToBottom */));
        }

        // Skip the animation if there is nothing to animate
        if (mPendingAnimations.isEmpty()) {
            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
            return;
        }

        try {
            linkToDeathOfRunner();
        } catch (RemoteException e) {
            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
            return;
        }

        attachNavigationBarToApp();

        // Adjust the wallpaper visibility for the showing target activity

        mTargetActivityRecord = targetActivity;
        if (targetActivity.windowsCanBeWallpaperTarget()) {
            mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            mDisplayContent.setLayoutNeeded();
        }

        mService.mWindowPlacerLocked.performSurfacePlacement();

        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);

        // Notify that the animation has started
        if (mStatusBar != null) {
            mStatusBar.onRecentsAnimationStateChanged(true /* running */);
        }
    }

#9 RecentsAnimationController.java

    TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
            OnAnimationFinishedCallback finishedCallback) {
        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
        final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
                isRecentTaskInvisible);
                //----- #11
        task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden,
                ANIMATION_TYPE_RECENTS, finishedCallback);
        task.commitPendingTransaction();
        mPendingAnimations.add(taskAdapter);
        return taskAdapter;
    }

#10 task type

记录下常见的task的类型

    public static String activityTypeToString(@ActivityType int applicationType) {
        switch (applicationType) {
            case ACTIVITY_TYPE_UNDEFINED: return "undefined";//0
            case ACTIVITY_TYPE_STANDARD: return "standard";//1
            case ACTIVITY_TYPE_HOME: return "home";//2
            case ACTIVITY_TYPE_RECENTS: return "recents";//3
            case ACTIVITY_TYPE_ASSISTANT: return "assistant";//4
            case ACTIVITY_TYPE_DREAM: return "dream";//5
        }
        return String.valueOf(applicationType);
    }

#11 startAnimation

Task类的父类的父类 WindowContainer.java

    protected final SurfaceAnimator mSurfaceAnimator;
    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
            @AnimationType int type,
            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
            @Nullable Runnable animationCancelledCallback,
            @Nullable AnimationAdapter snapshotAnim) {

        mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
                animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
    }

SurfaceAnimator里有用到SurfaceController类,这个看起来像是我们要的东西,不细究了,太复杂了。

10.3.StateManager.java

>addStateListener

LauncherRecentsView里用到这个,监听状态的改变好更改自己的可见性。

    public void addStateListener(StateListener listener) {
        mListeners.add(listener);
    }

>goToState

    private void goToState(
            STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
        animated &= areAnimatorsEnabled();
        //要改变的状态和activity当前状态一样,直接回调即可
        if (mActivity.isInState(state)) {
            if (mConfig.currentAnimation == null) {
                // Run any queued runnable
                if (listener != null) {
                    listener.onAnimationEnd(null);
                }
                return;
            } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
                // We are running the same animation as requested
                if (listener != null) {
                    mConfig.currentAnimation.addListener(listener);
                }
                return;
            }
        }
        //不需要动画
        if (!animated) {
            mAtomicAnimationFactory.cancelAllStateElementAnimation();
            //回调listener
            onStateTransitionStart(state);
            //有handler的话调用下对应的方法
            for (StateHandler handler : getStateHandlers()) {
                handler.setState(state);
            }
            //回调listener
            onStateTransitionEnd(state);

            // Run any queued runnable
            if (listener != null) {
                listener.onAnimationEnd(null);
            }
            return;
        }

        if (delay > 0) {
        //延迟后继续调用下边的方法
        } else {
        //带动画过程的,不看了
            goToStateAnimated(state, fromState, listener);
        }
    }

11. 总结

  • 主要介绍了,我们点击recents按钮以launcher3里边服务如何启动对应的页面
  • recent view的布局结构
  • 自定义的RecentsView逻辑,监听launcher的状态变化,隐藏显示自己
  • 子控件TaskView

你可能感兴趣的:(android framework13-launcher3【02recents】)