Android 13 Launcher3 实现 FolderIcon 的摇晃动画

1,660 阅读2分钟

为 FolderIcon 添加摇晃动画

为 FolderIcon 添加摇晃动画可以分为三种情况:

  1. FolderIcon 整体摇晃:folder icon + folder name 都摇晃
  2. FolderIcon 局部摇晃: a. 仅 folder 内的所有 icon 摇晃,folder icon 背景和 folder name 不摇晃 b. 仅 folder icon 摇晃,folder name 不摇晃

1. 为 FolderIcon 整体添加摇晃动画

  1. 修改 com/android/launcher3/folder/FolderIcon.java 文件:
private ValueAnimator shakeAnim = null;
// current shaking angle
float shakeAngle = 0F;

@Override
public void startShake() {
    cancelShake();
    shakeAnim = ObjectAnimator.ofFloat(view, "rotation", -4F, 4F).apply {
        duration = 400L
        repeatCount = ObjectAnimator.INFINITE
        repeatMode = ObjectAnimator.REVERSE
        interpolator = CycleInterpolator(0.5f)
    }
    shakeAnim.start();
}

@Override
public void cancelShake() {
    if (shakeAnim != null) {
        shakeAnim.cancel();
        setRotation(0F);
    }
}

看看效果:

foldericon_anim.gif

2. 为 FolderIcon 局部添加摇晃动画

a. 先定义摇晃动画

object AnimatorHelper{
    @JvmStatic
    fun createShakeAnimator(): ValueAnimator {
        return ValueAnimator.ofFloat(-15F, 15F).apply {
            duration = 600L
            repeatCount = ObjectAnimator.INFINITE
            repeatMode = ObjectAnimator.REVERSE
            interpolator = CycleInterpolator(0.5f)
        }
    }
}

b. 在 FolderIcon 内添加摇晃动画

修改 com/android/launcher3/folder/FolderIcon.java 文件:

private ValueAnimator shakeAnim = null;
// current shaking angle
float shakeAngle = 0F;

@Override
public void startShake() {
    cancelShake();
    shakeAnim = AnimatorHelper.createShakeAnimator();
    shakeAnim.addUpdateListener(animation -> {
        shakeAngle = (float) animation.getAnimatedValue();
        invalidate();
    });
    shakeAnim.start();
}

@Override
public void cancelShake() {
    if (shakeAnim != null) {
        shakeAnim.cancel();
        shakeAngle = 0F;
        invalidate();
    }
}

c. 为 Folder 内所有 icon 添加摇晃动画

  • 仅 folder 内所有 icon 摇晃,folder 背景和 folder name 不摇晃

修改 com/android/launcher3/folder/PreviewItemManager.java 文件:

/**
 * Draws each preview item.
 *
 * @param offset The offset needed to draw the preview items.
 * @param shouldClipPath Iff true, clip path using {@param clipPath}.
 * @param clipPath The clip path of the folder icon.
 */
private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params, PointF offset,
        boolean shouldClipPath, Path clipPath) {
    canvas.save();
    if (shouldClipPath) {
        canvas.clipPath(clipPath);
    }
    canvas.translate(offset.x + params.transX, offset.y + params.transY);
    canvas.scale(params.scale, params.scale);
    Drawable d = params.drawable;

    if (d != null) {
        Rect bounds = d.getBounds();
        canvas.save();
        canvas.translate(-bounds.left, -bounds.top);
        canvas.scale(mIntrinsicIconSize / bounds.width(), mIntrinsicIconSize / bounds.height());
        // add folder icons shaking animation
        canvas.rotate(mIcon.shakeAngle, bounds.width() / 2F, bounds.height() / 2F);
        d.draw(canvas);
        canvas.restore();
    }
    canvas.restore();
}

abc 的效果:

folder_icons_anim.gif

d. 为 FolderIcon 添加摇晃动画

  • folder icon 摇晃,folder name 不摇晃

修改 com/android/launcher3/folder/PreviewBackground.java 文件:

// update mInvalidateDelegate type to FolderIcon
private FolderIcon mInvalidateDelegate;

public void drawBackground(Canvas canvas) {
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setColor(getBgColor());

    // add folder icon shaking animation
    if (mInvalidateDelegate != null && mInvalidateDelegate.shakeAngle != 0F) {
        float cx = getScaledRadius() + getOffsetX();
        float cy = getScaledRadius() + getOffsetY();
        canvas.rotate(mInvalidateDelegate.shakeAngle, cx, cy);
    }

    getShape().drawShape(canvas, getOffsetX(), getOffsetY(), getScaledRadius(), mPaint);
    drawShadow(canvas);
}

abd 的效果:

folder_icon_anim.gif

通过以上修改,我们可以为 Launcher3 FolderIcon 添加类似 iOS 的图标摇晃效果。