Android Launcher3 gms 负一屏滑动困难的问题

Google Feed
Android 14为例
要想桌面带有Google feed负一屏,需要编译vendor/partner_gms/apps/SearchLauncher
SearchLauncher结构
1、libs/launcher_client.jar Google和feed apk通讯的工具
2、com/android/searchlauncher/OverlayCallbackImpl.java 初始化LauncherClient对象和实现LauncherClientCallbacks回调方法,并且在launcher各个生命周期通过LauncherClient同步给负一屏
3、负一屏的具体实现应该是被封装了,只能通过谷歌提供的接口,对负一屏进行操作

问题1:负一屏滚动一半的时候才会被唤出,缓慢滑动唤出负一屏需要在屏幕上滑动屏幕一半的距离,这在大屏上比较困难,不利于用户体验。

有关负一屏的操控方法都在一下这个类中实现(安卓14):
launcher3上的滑动的距离通过onScrollChang()这个接口传入负一屏,负一屏根据这个滑动距离来移动,原来是1:1的关系,一下代码修改为1:5的关系:

 public class OverlayCallbackImpl
    implements LauncherOverlay, LauncherClientCallbacks, LauncherOverlayManager,
    OnSharedPreferenceChangeListener {

@Override
public void onScrollChange(float progress, boolean rtl) {
    //modified by xu-24 at 2024/03/18 :When the large screen touch slides 0.1,
    // the negative screen component needs to be updated to slide five times the large screen,
    // which makes sliding the negative screen easier
    //此处是我的代码修改点,传入的progress是在pagedview中获取到的滑动距离,原生代码中负一屏滚动的距离跟滑动距离是1:1的关系,故*5是的宽度较大的大屏也能轻松的滚动出负一屏
    mClient.updateMove(progress*5);
}

}

一下是这个接口的部分调用堆栈信息:

03-18 04:38:28.985 13135 13135 I : 调用堆栈信息:
03-18 04:38:28.986 13135 13135 I : dalvik.system.VMStack.getThreadStackTrace(Native Method)
03-18 04:38:28.986 13135 13135 I : java.lang.Thread.getStackTrace(Thread.java:1841)
03-18 04:38:28.986 13135 13135 I : com.android.searchlauncher.OverlayCallbackImpl.onScrollChange(OverlayCallbackImpl.java:173)
03-18 04:38:28.986 13135 13135 I : com.android.launcher3.util.OverlayEdgeEffect.onPullDistance(OverlayEdgeEffect.java:52)
03-18 04:38:28.986 13135 13135 I : com.android.launcher3.PagedView.onTouchEvent(PagedView.java:1330)
03-18 04:38:28.987 13135 13135 I : com.android.launcher3.Workspace.onTouchEvent(Workspace.java:1095)
03-18 04:38:28.987 13135 13135 I : android.view.View.dispatchTouchEvent(View.java:15655)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3114)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2787)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-18 04:38:28.987 13135 13135 I : com.android.launcher3.views.BaseDragLayer.dispatchTouchEvent(BaseDragLayer.java:303)
03-18 04:38:28.987 13135 13135 I : com.android.launcher3.dragndrop.DragLayer.dispatchTouchEvent(DragLayer.java:225)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3142)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
03-18 04:38:28.987 13135 13135 I : android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
03-18 04:38:28.987 13135 13135 I : com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:490)
03-18 04:38:28.987 13135 13135 I : com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1904)
03-18 04:38:28.987 13135 13135 I : android.app.Activity.dispatchTouchEvent(Activity.java:4377)
03-18 04:38:28.987 13135 13135 I : com.android.launcher3.Launcher.dispatchTouchEvent(Launcher.java:2145)
03-18 04:38:28.987 13135 13135 I : com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:448)
03-18 04:38:28.987 13135 13135 I : android.view.View.dispatchPointerEvent(View.java:15919)

问题2:快速滑动的时候滑动速度很快才能进入负一屏,不友好用户使用体验

找到pagedview中的public boolean onTouchEvent(MotionEvent ev) 方法,我们只需要关注手指离开屏幕后的代码,找到shouldFlingForVelocity()这个方法的调用位置:

    case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
    final int activePointerId = mActivePointerId;
    final int pointerIndex = ev.findPointerIndex(activePointerId);
    if (pointerIndex == -1) return true;
    final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev,
        pointerIndex);
    final VelocityTracker velocityTracker = mVelocityTracker;
    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);

    int velocity = (int) mOrientationHandler.getPrimaryVelocity(velocityTracker,
            mActivePointerId);
    float delta = primaryDirection - mDownMotionPrimary;

    View current = getPageAt(mCurrentPage);
    if (current == null) {
        Log.e(TAG, "current page was null. this should not happen.");
        return true;
    }

    int pageOrientedSize = (int) (mOrientationHandler.getMeasuredSize(current)
            * mOrientationHandler.getPrimaryScale(this));
    boolean isSignificantMove = isSignificantMove(Math.abs(delta), pageOrientedSize);

    mTotalMotion += Math.abs(mLastMotion - primaryDirection);
    boolean passedSlop = mAllowEasyFling || mTotalMotion > mPageSlop;
    boolean isFling = passedSlop && shouldFlingForVelocity(velocity);//shouldFlingForVelocity,此方法是根据速度判断滑动相关的方法,此处调用的是子类中的同名方法
    boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0;
    boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0;
    if (DEBUG_FAILED_QUICKSWITCH && !isFling && mAllowEasyFling) {
        Log.d("Quickswitch", "isFling=false vel=" + velocity
                + " threshold=" + mEasyFlingThresholdVelocity);
    }

    if (!mFreeScroll) {
        // In the case that the page is moved far to one direction and then is flung
        // in the opposite direction, we use a threshold to determine whether we should
        // just return to the starting page, or if we should skip one further.
        boolean returnToOriginalPage = false;
        if (Math.abs(delta) > pageOrientedSize * RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&
                Math.signum(velocity) != Math.signum(delta) && isFling) {
            returnToOriginalPage = true;
        }

        int finalPage;
        // We give flings precedence over large moves, which is why we short-circuit our
        // test for a large move if a fling has been registered. That is, a large
        // move to the left and fling to the right will register as a fling to the right.

        if (((isSignificantMove && !isDeltaLeft && !isFling) ||
                (isFling && !isVelocityLeft)) && mCurrentPage > 0) {
            finalPage = returnToOriginalPage
                    ? mCurrentPage : mCurrentPage - getPanelCount();
            runOnPageScrollsInitialized(
                    () -> snapToPageWithVelocity(finalPage, velocity));
        } else if (((isSignificantMove && isDeltaLeft && !isFling) ||
                (isFling && isVelocityLeft)) &&
                mCurrentPage < getChildCount() - 1) {
            finalPage = returnToOriginalPage
                    ? mCurrentPage : mCurrentPage + getPanelCount();
            runOnPageScrollsInitialized(
                    () -> snapToPageWithVelocity(finalPage, velocity));
        } else {
            runOnPageScrollsInitialized(this::snapToDestination);
        }
    } else {
        if (!mScroller.isFinished()) {
            abortScrollerAnimation(true);
        }

        int initialScroll = mOrientationHandler.getPrimaryScroll(this);
        int maxScroll = mMaxScroll;
        int minScroll = mMinScroll;

        if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) ||
            ((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) {
            mScroller.springBack(initialScroll, 0, minScroll, maxScroll, 0, 0);
            mNextPage = getDestinationPage();
        } else {
            int velocity1 = -velocity;
            // Continue a scroll or fling in progress
            mScroller.fling(initialScroll, 0, velocity1, 0, minScroll, maxScroll, 0, 0,
                    Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR), 0);

            int finalPos = mScroller.getFinalX();
            mNextPage = getDestinationPage(finalPos);
            runOnPageScrollsInitialized(this::onNotSnappingToPageInFreeScroll);
        }
        invalidate();
    }
}

mEdgeGlowLeft.onRelease();
mEdgeGlowRight.onRelease();
// End any intermediate reordering states
resetTouchState();
break;

找到Workspace.java,中同名方法shouldFlingForVelocity的实现如下:

@Override
protected boolean shouldFlingForVelocity(int velocityX) {
    //start add by fj 2024/03/14:
    //If the negative screen has a scroll
    // and the slide reaches the specified speed,
    // and the slide is to the right, the negative screen is opened
    //为解决这个问题,我选择在这个方法中添加我的修改代码,如下
    if(velocityX >100 && Float.compare(Math.abs(mOverlayProgress), 0) > 0 && velocityX > 0){
        mLauncher.openOverlay();//调用我在launcher.java中添加的方法打开负一屏
    }
    //end by fj
    // When the overlay is moving, the fling or settle transition is controlled by the overlay.
    //fj:此处return返回的结果,Float.compare(Math.abs(mOverlayProgress), 0) == 0是判断mOverlayProgress也就是负一屏滚动值,为0说明不是关于负一屏的滑动,原生代码在此处也对负一屏处的滑动做了判断处理
    return Float.compare(Math.abs(mOverlayProgress), 0) == 0
            && super.shouldFlingForVelocity(velocityX);
}

ps:
launcher.java中的打开负一屏的方法实现:
/**
* Add by fj at 2024.03.13
* Open negative screen
*/
public void openOverlay(){
mOverlayManager.openOverlay(); //mOverlayManager是一个接口,其中所有的方法都在前文所述的OverlayCallbackImpl中被实现
}

问题三:Laucher3中在何处获取到的负一屏的滚动值呢?

Workspace.java中有着onOverlayScrollChanged实现:

/**
 * The overlay scroll is being controlled locally, just update our overlay effect
 */
@Override
public void onOverlayScrollChanged(float scroll) {
    mOverlayProgress = Utilities.boundToRange(scroll, 0, 1);
    //mOverlayProgress即为负一屏的滚动值,在0到1的区间表示占屏幕宽度的比例
    if (Float.compare(mOverlayProgress, 1f) == 0) {
        if (!mOverlayShown) {
            mOverlayShown = true;
            mLauncher.onOverlayVisibilityChanged(true);
        }
    } else if (Float.compare(mOverlayProgress, 0f) == 0) {
        if (mOverlayShown) {
            mOverlayShown = false;
            mLauncher.onOverlayVisibilityChanged(false);
        }
    }
    int count = mOverlayCallbacks.size();
    for (int i = 0; i < count; i++) {
        mOverlayCallbacks.get(i).onOverlayScrollChanged(mOverlayProgress);
    }
}
  • 31
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值