Android Launcher3添加负一屏

1.直接上代码

java 复制代码
import com.android.launcher3.Launcher;
import com.android.launcher3.R;

public class ViewUtils {
    private static final String TAG = "ViewUtils";
    private static boolean isScrollInteraction;//是否从桌面左滑

    public static Launcher.LauncherOverlay addNegativeScreen(Launcher launcher, ViewGroup parent) {
        LogUtils.init(launcher);
        View nsv = LayoutInflater.from(launcher).inflate(R.layout.negative_screen, null);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        nsv.setLayoutParams(lp);

        parent.addView(nsv, 0); // 插入到首位(布局最左边)

        int width = Resources.getSystem().getDisplayMetrics().widthPixels;
        nsv.setTranslationX(-width);//位移到左边屏幕外

        return new Launcher.LauncherOverlay() {
            @Override
            public void onScrollInteractionBegin() {
                isScrollInteraction = launcher.getWorkspace().getCurrentPage() == 0;//是否为第一页开始
                Log.i(TAG, "onScrollInteractionBegin");
            }

            @Override
            public void onScrollInteractionEnd() {
                isScrollInteraction = false;
                Log.i(TAG, "onScrollInteractionEnd");
            }

            @Override
            public void onScrollChange(float progress, boolean rtl) {
                float disW = progress * width;//x轴滑动距离
                Log.i(TAG, "onScrollChange-progress=" + progress + ",rtl=" + rtl + ",width=" + width + ",disW=" + disW);
            }

            @Override
            public void setOverlayCallbacks(Launcher.LauncherOverlayCallbacks callbacks) {
                Log.i(TAG, "setOverlayCallbacks");
            }
        };
    }

    private static float mDx, mDy, disX, disY;//按下坐标值及距离
    private static final float MIN_DIS_X = 4;//最小滑动距离
    private static final float MIN_DIS_Y = 4;//最小滑动距离
    private static int consumedType = 0;//消费事件类型(0-默认滑动,1-左右滑动,2-上下滑动,3-其他滑动)
    static boolean isFling = false;//是否快速滑动
    private static boolean mNsvVisible = false;//负一屏是否可见

    /**
     * 触摸事件处理
     *
     * @param launcher
     * @param viewGroup
     * @param ev
     * @return
     */
    public static boolean setDispatchTouchEvent(Launcher launcher, ViewGroup viewGroup, MotionEvent ev) {
        View nsv = viewGroup.getChildAt(0);
        View rightView = viewGroup.getChildAt(1);
        int width = viewGroup.getWidth();
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                isScrollInteraction = false;
                isFling = false;
                consumedType = 0;
                mDx = ev.getX();
                mDy = ev.getY();
                disX = 0;
                disY = 0;
                Log.i(TAG, "setDispatchTouchEvent-down-width=" + width + ",mDx=" + mDx + ",mDy=" + mDy);
                break;
            case MotionEvent.ACTION_MOVE:
                float mx = ev.getX();
                float my = ev.getY();
                disX = mx - mDx;//x轴滑动距离
                disY = my - mDy;//y轴滑动距离
                if (Math.abs(disX) > Math.abs(disY) && consumedType == 0 && Math.abs(disX) > MIN_DIS_X) {//是否为-左右滑动
                    if (isScrollInteraction || mNsvVisible ) {//从桌面左滑判断 或 已显示负一屏判断
                        consumedType = 1;
                    }
                } else if (Math.abs(disY) > Math.abs(disX) && consumedType == 0 && Math.abs(disY) > MIN_DIS_Y) {//是否为-上下滑动
                    consumedType = 2;
                }
                Log.i(TAG, "setDispatchTouchEvent-move-width=" + width + ",mx=" + mx + ",disX=" + disX + ",disY=" + disY +
                        ",consumedType=" + consumedType + ",isScrollInteraction=" + isScrollInteraction + ",mNsvVisible=" + mNsvVisible);

                float nsTx = nsv.getTranslationX() + disX;
                float rvTx = rightView.getTranslationX() + disX;
                if (consumedType == 1) {
                    if (disX > 0) {//往右滑
                        //你的逻辑...
                        nsv.setTranslationX(Math.min(nsTx, 0));
                        rightView.setTranslationX(Math.min(rvTx, width));

                        //negativeScreen.setTranslationX(nsTx);
                        //rightView.setTranslationX(rvTx);

                        float alpha = rightView.getTranslationX() / width;
                        nsv.setAlpha(alpha);
                        rightView.setAlpha(1 - nsv.getAlpha());

                        Log.i(TAG, "move=往右滑=mDx=" + mDx + ",mx=" + mx + ",disX=" + disX + ",alpha=" + alpha);
                    } else {//往左滑
                        //你的逻辑...
                        nsv.setTranslationX(Math.max(nsTx, -width));
                        rightView.setTranslationX(Math.max(rvTx, 0));

                        //negativeScreen.setTranslationX(nsTx);
                        //rightView.setTranslationX(rvTx);

                        float alpha = rightView.getTranslationX() / width;
                        rightView.setAlpha(1 - alpha);
                        nsv.setAlpha(1 - rightView.getAlpha());
                        Log.i(TAG, "move=往左滑=mDx=" + mDx + ",mx=" + mx + ",disX=" + disX + ",alpha=" + alpha);
                    }

                    if (Math.abs(disX) > 100) {//判断快速滑动值
                        isFling = true;
                    }
                }
                mDx = mx;//替换x轴坐标值
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mNsvVisible) {//负一屏可见时
                    if (isFling && disX < 0) {//快速右滑
                        playAnim(nsv, rightView, 80, width, false);//执行动画,不显示负一屏
                    } else {
                        playAnim(nsv, rightView, isFling ? 80 : 200, width, width - rightView.getTranslationX() <= width / 4);//执行动画,负一屏是否可见
                    }
                } else {
                    playAnim(nsv, rightView, isFling ? 80 : 200, width, (isFling && disX > 0) || rightView.getTranslationX() >= width / 4);//执行动画,负一屏是否可见
                }
                Log.i(TAG, "setDispatchTouchEvent-up-cancel-x=" + ev.getX() + ",mNsvVisible=" + mNsvVisible + ",Fling-disX=" + disX + ",isFling=" + isFling);
                break;
        }
        return consumedType == 1;
    }

    /**
     * 执行动画
     *
     * @param nsv
     * @param rightView
     * @param duration
     * @param width
     * @param isShowNsv 是否显示负一屏
     */
    private static void playAnim(View nsv, View rightView, long duration, int width, boolean isShowNsv) {
        // nsv 两个独立动画
        ObjectAnimator alpha1 = ObjectAnimator.ofFloat(nsv, "alpha", nsv.getAlpha(), isShowNsv ? 1f : 0);
        ObjectAnimator translationX1 = ObjectAnimator.ofFloat(nsv, "translationX", nsv.getTranslationX(), isShowNsv ? 0 : -width);
        AnimatorSet set1 = new AnimatorSet();
        set1.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                mNsvVisible = isShowNsv;//修改负一屏状态为可见
                Log.i(TAG, "playAnim-set1-onAnimationEnd-mNsvVisible=" + mNsvVisible + ",isShowNsv=" + isShowNsv);
            }
        });
        set1.playTogether(alpha1, translationX1); // 同步执行
        set1.setDuration(duration);
        set1.start();

        // rightView 两个独立动画
        ObjectAnimator alpha2 = ObjectAnimator.ofFloat(rightView, "alpha", rightView.getAlpha(), isShowNsv ? 0 : 1f);
        ObjectAnimator translationX2 = ObjectAnimator.ofFloat(rightView, "translationX", rightView.getTranslationX(), isShowNsv ? width : 0f);
        AnimatorSet set2 = new AnimatorSet();
        set2.playTogether(alpha2, translationX2); // 同步执行
        set2.setDuration(duration);
        set2.start();
    }
}

2.com.android.launcher3.Launcher类添加两个方法

java 复制代码
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(mLauncherView);
        
        //在onCreate方法的最后-添加负一屏
        setLauncherOverlay(ViewUtils.addNegativeScreen(this, (ViewGroup) mLauncherView));
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //负一屏和DragLayer手势监听处理
        if(ViewUtils.setDispatchTouchEvent(this,(ViewGroup) mLauncherView,ev)){
            return true;
        }
        return super.dispatchTouchEvent(ev);
    }
相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui