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 小时前
【安卓】开发一个读取文件信息的简易apk
android
LcVong4 小时前
老版本Android源码在新版本IDE打开的常规报错及解决方案
android·ide
别退4 小时前
flutter_gradle_android
android·flutter
2501_944424124 小时前
Flutter for OpenHarmony游戏集合App实战之黑白棋落子翻转
android·开发语言·windows·flutter·游戏·harmonyos
zhangphil4 小时前
Android adb shell抓取trace(二)
android
2501_944424125 小时前
Flutter for OpenHarmony游戏集合App实战之消消乐下落填充
android·开发语言·flutter·游戏·harmonyos
min1811234565 小时前
软件升级全流程步骤详解
android·java·服务器
JeffDingAI6 小时前
【Datawhale学习笔记】参数高效微调
android·笔记·学习
江西省遂川县常驻深圳大使6 小时前
【免费】Android App加固,Android应用加固,安卓加固,Apk加固
android