You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何将视图置于另一视图位置并实现缩放动画?

解决2048游戏缩放动画视图定位问题

核心问题分析

你把动画视图直接加到了game_grid_layout(垂直LinearLayout)中,LinearLayout会按布局方向将新视图排列在底部,而非悬浮覆盖原视图;同时你使用translationX/Y的方式错误,没有基于父容器的正确坐标计算偏移。

解决方案步骤

1. 调整布局结构

将game_grid_layout包裹在一个全屏FrameLayout中,这样动画视图可以和游戏网格同级,实现悬浮覆盖:

<FrameLayout
    android:id="@+id/animation_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

    <LinearLayout
        android:id="@+id/game_grid_layout"
        android:layout_width="280dp"
        android:layout_height="280dp"
        android:layout_marginTop="16dp"
        android:background="@color/game_container"
        android:orientation="vertical"
        android:layout_gravity="center">

        <!-- 原网格内容保持不变 -->
    </LinearLayout>
</FrameLayout>

2. 正确计算动画视图的位置

获取原视图相对于外层FrameLayout的坐标,设置动画视图的布局参数和位置:

// 外层FrameLayout
val animationContainer = findViewById<FrameLayout>(R.id.animation_container)
// 原视图(示例为第一个单元格,替换为你需要动画的视图)
val originalView = findViewById<FrameLayout>(R.id.grid_container_0_0)

// 创建动画视图(和原视图内容一致,替换为你的单元格布局)
val viewToAnimate = LayoutInflater.from(this).inflate(R.layout.cell_item, animationContainer, false)
// 复制原视图的宽高
val width = originalView.width
val height = originalView.height

// 计算原视图相对于animationContainer的坐标
val originalLoc = IntArray(2)
originalView.getLocationInWindow(originalLoc)
val containerLoc = IntArray(2)
animationContainer.getLocationInWindow(containerLoc)
val offsetX = originalLoc[0] - containerLoc[0]
val offsetY = originalLoc[1] - containerLoc[1]

// 设置动画视图的布局参数
val params = FrameLayout.LayoutParams(width, height)
params.leftMargin = offsetX
params.topMargin = offsetY
animationContainer.addView(viewToAnimate, params)

// 设置层级,确保在网格上方
ViewCompat.setElevation(viewToAnimate, 2f)

3. 修复缩放动画逻辑

确保缩放以视图中心为基准,并在动画结束后移除视图:

val mergeAnim = ValueAnimator.ofFloat(1f, 2f)
mergeAnim.addUpdateListener { animation ->
    viewToAnimate.scaleX = animation.animatedValue as Float
    viewToAnimate.scaleY = animation.animatedValue as Float
}
mergeAnim.repeatCount = 1
mergeAnim.repeatMode = ValueAnimator.REVERSE
mergeAnim.addListener(object : Animator.AnimatorListener {
    override fun onAnimationEnd(animation: Animator?) {
        // 动画结束后移除视图,避免内存泄漏
        animationContainer.removeView(viewToAnimate)
    }
    override fun onAnimationStart(animation: Animator?) {}
    override fun onAnimationCancel(animation: Animator?) {}
    override fun onAnimationRepeat(animation: Animator?) {}
})

val animatorSet = AnimatorSet()
animatorSet.interpolator = DecelerateInterpolator(1f)
animatorSet.duration = 100
animatorSet.play(mergeAnim)
animatorSet.start()

关键注意点

  • 必须将动画视图添加到FrameLayout容器中,而非原LinearLayout网格,否则会被线性布局排列,无法悬浮。
  • 坐标计算要基于动画容器的位置,避免因状态栏、导航栏导致的偏移错误。
  • 动画结束后记得移除视图,避免内存泄漏。

内容的提问来源于stack exchange,提问作者יששכר בן דוד

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 16:05:38