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
相关产品推荐
相关产品推荐

