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

Android中点击按钮交换多个ImageView及条形图实现求助

解决方案:Android 条形图设计与交换动画实现

一、快速实现条形图(替代ImageView)

方案1:基础View组合快速搭建

不用复杂第三方库,直接用LinearLayout嵌套View就能做简单条形图,上手快还方便加动画:

  • 布局里横向放一个LinearLayout,设置horizontal方向、底部对齐(让条形从底部往上延伸)
  • 每个条形就是一个View,通过layout_weight控制宽度比例,background设为条形颜色,layout_height对应条形高度
  • 示例XML代码:
<LinearLayout
    android:id="@+id/barContainer"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:orientation="horizontal"
    android:gravity="bottom"
    android:padding="8dp">

    <View
        android:id="@+id/bar1"
        android:layout_width="0dp"
        android:layout_height="60dp"
        android:layout_weight="1"
        android:background="#FF5722"
        android:layout_marginHorizontal="2dp"/>

    <View
        android:id="@+id/bar2"
        android:layout_width="0dp"
        android:layout_height="80dp"
        android:layout_weight="1"
        android:background="#2196F3"
        android:layout_marginHorizontal="2dp"/>

    <!-- 按需添加更多条形View -->
</LinearLayout>

方案2:极简自定义条形图(适配动态数据)

如果需要根据算法数据动态生成条形,写个极简自定义View就行,逻辑简单好懂:

class SimpleBarChart @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    private var barHeights = listOf(60, 80, 40, 90) // 条形高度数据(单位dp)
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val barWidth = 40f
    private val barMargin = 10f

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.color = Color.parseColor("#FF5722")
        // 遍历数据绘制每个条形
        barHeights.forEachIndexed { index, height ->
            val left = index * (barWidth + barMargin)
            val top = height.toFloat()
            val right = left + barWidth
            val bottom = height.toFloat() + (height.toFloat() * 0.5f) // 可调整高度计算逻辑
            canvas.drawRect(left, height.toFloat(), right, height.toFloat() + (height.toFloat() * 0.5f), paint)
        }
    }

    // 提供更新数据的方法,数据变化时刷新视图
    fun updateBarData(data: List<Int>) {
        barHeights = data
        invalidate()
    }
}

二、实现点击按钮交换条形(或ImageView)的动画

核心是用ValueAnimator做位移动画,动画结束后更新View在父容器中的实际位置,避免后续操作出错。

步骤1:封装交换动画工具方法

把交换逻辑写成可复用的方法,任意View都能调用:

fun swapViewsWithAnimation(view1: View, view2: View) {
    val parent = view1.parent as ViewGroup
    // 获取两个View在父容器中的初始坐标
    val location1 = IntArray(2)
    view1.getLocationInWindow(location1)
    val x1 = location1[0] - parent.getLocationInWindow(IntArray(2))[0]
    val y1 = location1[1] - parent.getLocationInWindow(IntArray(2))[1]

    val location2 = IntArray(2)
    view2.getLocationInWindow(location2)
    val x2 = location2[0] - parent.getLocationInWindow(IntArray(2))[0]
    val y2 = location2[1] - parent.getLocationInWindow(IntArray(2))[1]

    // 创建位移动画
    val animator = ValueAnimator.ofFloat(0f, 1f)
    animator.duration = 500 // 动画时长(毫秒)
    animator.addUpdateListener { animation ->
        val fraction = animation.animatedValue as Float
        // 计算当前帧的位置,更新View的偏移量
        view1.translationX = x1 + (x2 - x1) * fraction - x1
        view1.translationY = y1 + (y2 - y1) * fraction - y1
        view2.translationX = x2 + (x1 - x2) * fraction - x2
        view2.translationY = y2 + (y1 - y2) * fraction - y2
    }
    // 动画结束后重置偏移量,交换View在父容器中的实际位置
    animator.addListener(object : Animator.AnimatorListener {
        override fun onAnimationStart(animation: Animator?) {}
        override fun onAnimationEnd(animation: Animator?) {
            view1.translationX = 0f
            view1.translationY = 0f
            view2.translationX = 0f
            view2.translationY = 0f

            val index1 = parent.indexOfChild(view1)
            val index2 = parent.indexOfChild(view2)
            parent.removeViews(index1, 2)
            // 按原位置顺序重新添加View,完成实际位置交换
            if (index1 < index2) {
                parent.addView(view2, index1)
                parent.addView(view1, index2)
            } else {
                parent.addView(view1, index2)
                parent.addView(view2, index1)
            }
        }
        override fun onAnimationCancel(animation: Animator?) {}
        override fun onAnimationRepeat(animation: Animator?) {}
    })
    animator.start()
}

步骤2:按钮点击触发交换

在按钮的点击事件里直接调用工具方法即可:

val swapBtn = findViewById<Button>(R.id.swapBtn)
val bar1 = findViewById<View>(R.id.bar1)
val bar2 = findViewById<View>(R.id.bar2)

swapBtn.setOnClickListener {
    swapViewsWithAnimation(bar1, bar2)
}

内容的提问来源于stack exchange,提问作者Sakib X Hossain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 00:25:22