我现在的逻辑是,对于单个view生成固定大小的色块覆盖然后使用addView(view,sourceindex)的方式添加并把原来的sourcevview设置为gone,但是如果parent是约束布局的时候就会失效,所以需要get具体位置然后add,目前代码如下private fun createSingleViewSkeleton(view: View, config: SkeletonConfig): View {
return when (view) {
is ImageView -> {
Log.e("createSingleView","pic.width=${view.width},pic.height=${view.height}")
ViewUtils.createRoundedRectDrawable(
view.context, view.width, view.height, config.cornerRadius, config.maskColor
)
}
is TextView -> ViewUtils.createTextSkeleton(
view.context, view.width, view.height, config.cornerRadius, config.maskColor
)
else -> ViewUtils.createRoundedRectDrawable(
view.context, view.width, view.height, config.cornerRadius, config.maskColor
)
}
},private fun createAutoSkeleton(view: View, config: SkeletonConfig): Skeleton {
val skeletonView = when (view) {
is ViewGroup -> createViewGroupSkeleton(view, config)
else -> createSingleViewSkeleton(view, config)
}
return object : Skeleton {
override val view = skeletonView
override fun show(parent: ViewGroup, x: Float, y: Float) {
val oldParent = skeletonView.parent as? ViewGroup
oldParent?.removeView(skeletonView)
parent.addView(skeletonView)
skeletonView.x = x
skeletonView.y = y
}
override fun destroy() {
(skeletonView.parent as? ViewGroup)?.removeView(skeletonView)
Log.e("SkeletonFactory", "destroy called")
}
}
}
private fun createViewGroupSkeleton(group: ViewGroup, config: SkeletonConfig): FrameLayout {
val context = group.context
val skeletonContainer = FrameLayout(context)
// 测量整个 group 以确保 layout 已完成
if (group.width == 0 || group.height == 0) {
group.measure(
View.MeasureSpec.makeMeasureSpec(group.layoutParams.width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(group.layoutParams.height, View.MeasureSpec.EXACTLY)
)
group.layout(group.left, group.top, group.right, group.bottom)
}
// 获取父容器在窗口中的位置(用于坐标转换)
val parentLocation = IntArray(2)
group.getLocationInWindow(parentLocation)
val parentX = parentLocation[0]
val parentY = parentLocation[1]
for (i in 0 until group.childCount) {
val child = group.getChildAt(i)
// 跳过占位视图如 Space
if (child is Space) continue
// 确保子 view 已测量
if (child.measuredWidth == 0 || child.measuredHeight == 0) {
val widthSpec = if (child.layoutParams.width >= 0)
View.MeasureSpec.makeMeasureSpec(child.layoutParams.width, View.MeasureSpec.EXACTLY)
else
View.MeasureSpec.makeMeasureSpec(group.measuredWidth, View.MeasureSpec.AT_MOST)
val heightSpec = if (child.layoutParams.height >= 0)
View.MeasureSpec.makeMeasureSpec(child.layoutParams.height, View.MeasureSpec.EXACTLY)
else
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
child.measure(widthSpec, heightSpec)
}
// 创建骨架色块
val childSkeleton = createAutoSkeleton(child, config).view
// 获取子 view 在屏幕中的绝对坐标
val childLocation = IntArray(2)
child.getLocationInWindow(childLocation)
val childGlobalLeft = childLocation[0]
val childGlobalTop = childLocation[1]
// 计算相对于 skeletonContainer 的左上角偏移(因为 skeletonContainer 将作为 overlay 显示在同一位置)
val leftInParent = childGlobalLeft - parentX
val topInParent = childGlobalTop - parentY
// 设置 FrameLayout.LayoutParams 并用 margin 定位
val lp = FrameLayout.LayoutParams(child.measuredWidth, child.measuredHeight)
lp.leftMargin = leftInParent
lp.topMargin = topInParent
skeletonContainer.addView(childSkeleton, lp)
}
return skeletonContainer
}fun replace(sourceView: View, targetView2: View): Boolean {
if(sourceView is ImageView) {
// 1. 检查父容器
sourceParentView = sourceView.parent as? ViewGroup ?: run {
Log.e(TAG, "原视图没有父容器,无法替换")
return false
}
val index = sourceParentView!!.indexOfChild(sourceView)
// ✅ 获取原 view 的真实宽高(已 layout 完成)
val finalWidth = sourceView.width
val finalHeight = sourceView.height
if (finalWidth <= 0 || finalHeight <= 0) {
Log.w(TAG, "原视图尺寸为 0,可能尚未 layout,等待下一帧...")
targetView2.post { replace(sourceView, targetView2) }
return false
}
// ✅ ★ 所有 targetView2 都走“精确匹配”逻辑(含色块、ImageView、自定义 View)
targetView2.measure(
View.MeasureSpec.makeMeasureSpec(finalWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(finalHeight, View.MeasureSpec.EXACTLY)
)
// 布局自身
targetView2.layout(0, 0, targetView2.measuredWidth, targetView2.measuredHeight)
Log.d(TAG, "目标视图已设置尺寸: ${targetView2.measuredWidth}x${targetView2.measuredHeight}")
// ✅ 配置 layoutParams 并保持 margin 和 translation
configureLayoutParams(sourceView, targetView2, targetView2.measuredWidth, targetView2.measuredHeight)
// ✅ 插入到原位置
sourceParentView!!.addView(targetView2, index)
sourceView.visibility = View.GONE
currentView = targetView2
targetView = targetView2
Log.d(TAG, "视图替换完成,索引: $index")
return true
}else{
val parent = sourceView.parent as? ViewGroup ?: return false
val index = parent.indexOfChild(sourceView)
// 隐藏原始 view
sourceView.visibility = View.GONE
// 使用原 layoutParams 测量新视图
val lp = sourceView.layoutParams
targetView2.layoutParams = ViewGroup.LayoutParams(lp.width, lp.height)
// 确保父容器宽高有效
val parentWidth = parent.width
val parentHeight = parent.height
val widthSpec = when (lp.width) {
ViewGroup.LayoutParams.MATCH_PARENT ->
View.MeasureSpec.makeMeasureSpec(parentWidth, View.MeasureSpec.EXACTLY)
ViewGroup.LayoutParams.WRAP_CONTENT ->
View.MeasureSpec.makeMeasureSpec(parentWidth, View.MeasureSpec.AT_MOST)
else -> View.MeasureSpec.makeMeasureSpec(lp.width, View.MeasureSpec.EXACTLY)
}
val heightSpec = when (lp.height) {
ViewGroup.LayoutParams.MATCH_PARENT ->
View.MeasureSpec.makeMeasureSpec(parentHeight, View.MeasureSpec.EXACTLY)
ViewGroup.LayoutParams.WRAP_CONTENT ->
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.AT_MOST)
else -> View.MeasureSpec.makeMeasureSpec(lp.height, View.MeasureSpec.EXACTLY)
}
// 测量和布局
targetView2.measure(widthSpec, heightSpec)
targetView2.layout(
sourceView.left,
sourceView.top,
sourceView.left + targetView2.measuredWidth,
sourceView.top + targetView2.measuredHeight
)
// 插入父容器
parent.addView(targetView2, index)
currentView = targetView2
return true
}
return false
}
private fun configureLayoutParams(sourceView: View, targetView: View, width: Int, height: Int) {
val layoutParams = when (val lp = sourceView.layoutParams) {
is ViewGroup.MarginLayoutParams -> {
ViewGroup.MarginLayoutParams(width, height).apply {
leftMargin = lp.leftMargin
topMargin = lp.topMargin
rightMargin = lp.rightMargin
bottomMargin = lp.bottomMargin
}
}
else -> {
ViewGroup.LayoutParams(width, height)
}
}
targetView.layoutParams = layoutParams
targetView.translationX = sourceView.translationX
targetView.translationY = sourceView.translationY
}应该怎么修改呢