Android8.0 SystemUI的Recents(多任务)全部清除按钮修改

需求

最近安卓电视开发中有这样的需求:

1.全部清空移至多任务列表左侧

2.加一个垃圾桶图标

3.手势划出app时,全部清空的图标会暂时隐藏

如下图:

Android8.0 SystemUI的Recents(多任务)全部清除按钮修改_第1张图片

Android8.0 SystemUI的Recents(多任务)全部清除按钮修改_第2张图片

需求明确了,那就开始干活吧

干活

SystemUI的源码实在Android/frameworks/base/packages/SystemUI/

1.首先要先找到全部清除按钮:

xml:SystemUI/res/layout/recents_stack_action_button.xml

对象:com.android.systemui.recents.views.RecentsView中的mStackActionButton

2.全部清除移到移至多任务列表左侧

RecentsView是继承FrameLayout的,在其构造方法中找到mStackActionButton的对象获取:

    if (RecentsDebugFlags.Static.EnableStackActionButton) {
        mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
                   this, false);
           mStackActionButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    EventBus.getDefault().send(new DismissAllTaskViewsEvent());
                }
            });
        addView(mStackActionButton);
    }

上上面代码可以看出,mStackActionButton是通过addView直接添加到recentView的,但是没有看到坐标位置相关信息,继续找,于是在onLayout方法里找到如下代码:

    if (RecentsDebugFlags.Static.EnableStackActionButton) {
        // Layout the stack action button such that its drawable is start-aligned with the
        // stack, vertically centered in the available space above the stack
        Rect buttonBounds = getStackActionButtonBoundsFromStackLayout();
        mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right,
                    buttonBounds.bottom);
    }

 从代码可以看出mStackActionButton是通过buttonBounds确定位置的,继续跟踪getStackActionButtonBoundsFromStackLayout

     /**
     * @return the bounds of the stack action button.
     */
    private Rect getStackActionButtonBoundsFromStackLayout() {
        Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
        int left = isLayoutRtl()
                ? actionButtonRect.left - mStackActionButton.getPaddingLeft()
                : actionButtonRect.right + mStackActionButton.getPaddingRight()
                        - mStackActionButton.getMeasuredWidth();
        int top = actionButtonRect.top +
                (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2;
        actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
                top + mStackActionButton.getMeasuredHeight());
        return actionButtonRect;
    }

从这里可以看到最终返回actionButtonRect的left、top、right、bottom的来源,根据UI给的坐标,于是我修改如下:

/**
     * @return the bounds of the stack action button.
     */
    private Rect getStackActionButtonBoundsFromStackLayout() {
        Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect());
        int left = 261;
        int top = getMeasuredHeight()/2 - mStackActionButton.getMeasuredHeight() / 2;
        actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(),
                top + mStackActionButton.getMeasuredHeight());

        return actionButtonRect;
    }

left:靠左向右261px

top:是RecentView高度的1/2减去mStackActionButton高度的1/2,也就是垂直居中

right和bottom保持不变

于是需求1搞定,下一个

3.加一个垃圾桶图标

这个就简单了,打开SystemUI/res/layout/recents_stack_action_button.xml修改如下:

文字右侧添加图标android:drawableRight="@drawable/icon_delete_l",及间距android:drawablePadding="15px",搞定,mm编译push再reboot跑起来看看,效果如下:

Android8.0 SystemUI的Recents(多任务)全部清除按钮修改_第3张图片

好了,剩下随后一个需求了

4.手势划出app时,全部清空的图标会暂时隐藏

原生的mStackActionButton在任务列表往上滑的时候会隐藏,往下滑到顶部时再显示出来。而我们的需求是,左右滑动某个app时或者清除app时把它隐藏,没有这些操作时再显示出来。

首先要先找到mStackActionButton隐藏和显示的地方

首先在RecentsView中找到显示mStackActionButton的地方

    public final void onBusEvent(ShowStackActionButtonEvent event) {
        if (!RecentsDebugFlags.Static.EnableStackActionButton) {
            return;
        }

        showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate);
    }

然后在RecentsView中找到隐藏mStackActionButton的地方

    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
            int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
        if (RecentsDebugFlags.Static.EnableStackActionButton) {
            // Hide the stack action button
            hideStackActionButton(taskViewExitToHomeDuration, false /* translate */);
        }
        animateBackgroundScrim(0f, taskViewExitToHomeDuration);
    }

    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
        hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
    }


    public final void onBusEvent(HideStackActionButtonEvent event) {
        if (!RecentsDebugFlags.Static.EnableStackActionButton) {
            return;
        }
        hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */);
    }

showStackActionButtonhideStackActionButton大家可以去看看,主要是一些动画的实现

ShowStackActionButtonEvent类是继承EventBus.Event的,EventBus继承BroadcastReceiver

搜索ShowStackActionButtonEvent在哪里使用

TaskStackView中有如下代码:

    @Override
    public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
        mUIDozeTrigger.poke();
        if (animation != null) {
            relayoutTaskViewsOnNextFrame(animation);
        }

        // In grid layout, the stack action button always remains visible.
        if (mEnterAnimationComplete && !useGridLayout()) {
            if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
                    curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
                    mStack.getTaskCount() > 0) {
                EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
            } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
                    curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
                EventBus.getDefault().send(new HideStackActionButtonEvent());
            }
        }
    }
    private void updateStackActionButtonVisibility() {
        // Always show the button in grid layout.
        if (useGridLayout() ||
                (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
                        mStack.getTaskCount() > 0)) {
            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
        } else {
            EventBus.getDefault().send(new HideStackActionButtonEvent());
        }
    }

最终走

EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));

继续跟踪 EventBus

    public void send(Event event) {
        ......
        queueEvent(event);
    }

继续跟踪queueEvent

    private void queueEvent(final Event event) {
        ArrayList eventHandlers = mEventTypeMap.get(event.getClass());
        if (eventHandlers == null) {
            // This is just an optimization to return early if there are no handlers. However, we
            // should still ensure that we call pre/post dispatch callbacks so that AnimatedEvents
            // are still cleaned up correctly if a listener has not been registered to handle them
            event.onPreDispatch();
            event.onPostDispatch();
            return;
        }

        // Prepare this event
        boolean hasPostedEvent = false;
        event.onPreDispatch();

        // We need to clone the list in case a subscriber unregisters itself during traversal
        // TODO: Investigate whether we can skip the object creation here
        eventHandlers = (ArrayList) eventHandlers.clone();
        int eventHandlerCount = eventHandlers.size();
        for (int i = 0; i < eventHandlerCount; i++) {
            final EventHandler eventHandler = eventHandlers.get(i);
            if (eventHandler.subscriber.getReference() != null) {
                if (event.requiresPost) {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            processEvent(eventHandler, event);
                        }
                    });
                    hasPostedEvent = true;
                } else {
                    processEvent(eventHandler, event);
                }
            }
        }

        // Clean up after this event, deferring until all subscribers have been called
        if (hasPostedEvent) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    event.onPostDispatch();
                }
            });
        } else {
            event.onPostDispatch();
        }
    }

这里最终走的是processEvent,继续跟踪

    private void processEvent(final EventHandler eventHandler, final Event event) {
        // Skip if the event was already cancelled
        if (event.cancelled) {
            if (event.trace || DEBUG_TRACE_ALL) {
                logWithPid("Event dispatch cancelled");
            }
            return;
        }

        try {
            if (event.trace || DEBUG_TRACE_ALL) {
                logWithPid(" -> " + eventHandler.toString());
            }
            Object sub = eventHandler.subscriber.getReference();
            if (sub != null) {
                long t1 = 0;
                if (DEBUG_TRACE_ALL) {
                    t1 = SystemClock.currentTimeMicro();
                }
                eventHandler.method.invoke(sub, event);
                if (DEBUG_TRACE_ALL) {
                    long duration = (SystemClock.currentTimeMicro() - t1);
                    mCallDurationMicros += duration;
                    mCallCount++;
                    logWithPid(eventHandler.method.toString() + " duration: " + duration +
                            " microseconds, avg: " + (mCallDurationMicros / mCallCount));
                }
            } else {
                Log.e(TAG, "Failed to deliver event to null subscriber");
            }
        } catch (IllegalAccessException e) {
            Log.e(TAG, "Failed to invoke method", e.getCause());
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e.getCause());
        }
    }

这里最终走的是eventHandler.method.invoke(sub, event);通过反射调用

subcom.android.systemui.recents.views.RecentsView

eventcom.android.systemui.recents.events.activity.ShowStackActionButtonEvent

最后执行RecentsView的onBusEvent(ShowStackActionButtonEvent event)方法

com.android.systemui.recents.views.RecentsView-> onBusEvent();

最后调用showStackActionButtonhideStackActionButton方法控制全部清除按钮的显示与隐藏

onStackScrollChangedTaskStackView上下滑动的回调,我们的需求是上下滑动不用改变全部清除按钮的的状态,我们可以这么改:

    @Override
    public void onStackScrollChanged(float prevScroll, float curScroll, AnimationProps animation) {
        mUIDozeTrigger.poke();
        if (animation != null) {
            relayoutTaskViewsOnNextFrame(animation);
        }

        // In grid layout, the stack action button always remains visible.
//        if (mEnterAnimationComplete && !useGridLayout()) {
//            if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
//                    curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
//                    mStack.getTaskCount() > 0) {
//                EventBus.getDefault().send(new ShowStackActionButtonEvent(true /* translate */));
//            } else if (prevScroll < HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
//                    curScroll >= HIDE_STACK_ACTION_BUTTON_SCROLL_THRESHOLD) {
//                EventBus.getDefault().send(new HideStackActionButtonEvent());
//            }
//        }

    }

updateStackActionButtonVisibility可以这么改,一直显示全部清除按钮:

    private void updateStackActionButtonVisibility() {
        // Always show the button in grid layout.
        if (useGridLayout() ||
                (mStackScroller.getStackScroll() < SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD &&
                        mStack.getTaskCount() > 0)) {
            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
        } else {
            //EventBus.getDefault().send(new HideStackActionButtonEvent());
            EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
        }
    }

接下来去找左右滑动

com.android.systemui.recents.views.TaskStackViewTouchHandler

1.->onBeginDrag 开始拖拽的时候隐藏

    @Override
    public void onBeginDrag(View v) {
        ......
        EventBus.getDefault().send(new HideStackActionButtonEvent());
        ......
    }

2.->onChildDismissed 清除任务后显示

    @Override
    public void onChildDismissed(View v) {
        ......
        EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
        ......
    }

3.->onChildSnappedBack 拖拽放开回弹后显示

    @Override
    public void onChildSnappedBack(View v, float targetLeft) {
        ......
        EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */));
        ......
    }

 

 

你可能感兴趣的:(Android)