Android贴纸视图切换时尺寸未更新问题求助
问题:切换Sticker View时尺寸不更新且位置偏移
开发图片编辑项目的简易Sticker View,调用sticker.select(view)在不同View间切换贴纸时,首次切换正常,但第二次切换后贴纸位置偏移,选中框尺寸仍停留在第一个View的大小。
原始代码
Java类代码
class Sticker extends LinearLayout { public Sticker(Context ctx) { super(ctx); LayoutInflater thisLL = getLayoutInflater(); View thisVV = thisLL.inflate(R.layout.sticker, null); this.removeAllViews(); ((LinearLayout) this).addView(thisVV); this.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); selector = (LinearLayout) thisVV.findViewById(R.id.selector); rotate = (ImageView) thisVV.findViewById(R.id.rotate); scale = (ImageView) thisVV.findViewById(R.id.scale); final Sticker stick = this; rotate.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int ev = event.getAction(); switch (ev) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_MOVE: double r = (90 + (Math.atan2(event.getRawY() - (stick.getY() + (stick.getMeasuredHeight() / 2)), event.getRawX() - (stick.getX() + (stick.getMeasuredWidth() / 2))) * 180 / Math.PI)); if (-360 > r) { r += 360; } r += 45;//to get smooth rotate stick.setRotation((float) r); selected.setRotation((float) r); break; } return true; } }); selector.setBackground(new GradientDrawable() { public GradientDrawable getIns(int a, int b, int c, int d) { this.setCornerRadius(a); this.setStroke(b, c); this.setColor(d); return this; } }.getIns(0, 2, 0xFF607D8B, Color.TRANSPARENT)); scale.setOnTouchListener(new OnTouchListener() { PointF DivPT = new PointF(0, 0); PointF StartPT = new PointF(); private float initialTouchX; float initialTouchY; private int initialX; private int initialY; private int initialX2; private int initialY2; private int ex; private int ey; private int dw; private int dh; private float xx; private float yy; @Override public boolean onTouch(View _view, MotionEvent event) { int n = event.getAction(); if (n == MotionEvent.ACTION_UP) drag = false; if (n == MotionEvent.ACTION_DOWN) { drag = true; xx = selected.getX(); yy = selected.getY(); } if (n != 0) { if (n != 2) { return true; } // 原代码错误:用触摸偏移修改LayoutParams的宽高,这是逻辑错误 float deltaX = event.getRawX() - this.initialTouchX; float deltaY = event.getRawY() - this.initialTouchY; // 正确的缩放逻辑:基于初始尺寸计算缩放比例 float newWidth = initialX + deltaX; float newHeight = initialY + deltaY; // 避免尺寸过小 newWidth = Math.max(50, newWidth); newHeight = Math.max(50, newHeight); selected.setLayoutParams(new FrameLayout.LayoutParams((int) newWidth, (int) newHeight)); select(selected); } this.initialX = selected.getMeasuredWidth(); this.initialY = selected.getMeasuredHeight(); this.initialTouchX = event.getRawX(); this.initialTouchY = event.getRawY(); return true; } }); } LinearLayout selector; ImageView rotate; ImageView scale; public View selected; public View previous; boolean drag = false; public void select(View v) { this.selected = v; try { if (!v.equals(previous)) { // 安全移除当前Sticker容器 ViewGroup parent = (ViewGroup) this.getParent(); if (parent != null) { parent.removeView(this); } // 添加到目标View的父容器 ((ViewGroup) v.getParent()).addView(this); } // 更新选中框尺寸 ViewGroup.LayoutParams params = selector.getLayoutParams(); params.width = v.getMeasuredWidth(); params.height = v.getMeasuredHeight(); selector.setLayoutParams(params); // 强制重新布局,确保尺寸生效 selector.requestLayout(); previous = v; // 同步旋转状态 setRotation(v.getRotation()); selected.setRotation(v.getRotation()); // 修正位置计算:基于目标View的位置,减去rotate的宽度和自身顶部偏移 float rotateWidth = rotate.getMeasuredWidth(); setX(v.getX() - rotateWidth); setY(v.getY()); // 通知父容器刷新 ((View) v.getParent()).invalidate(); } catch (Exception e) { SketchwareUtil.showMessage(getApplicationContext(), e.toString()); } } private PointF DownPT = new PointF(); private PointF StartPT = new PointF(); private float SX; private float SY; private float rotation; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y); float newX = StartPT.x + mv.x; float newY = StartPT.y + mv.y; setX(newX); setY(newY); // 同步目标View的位置,修正偏移 selected.setX(newX + SX); selected.setY(newY); StartPT = new PointF(getX(), getY()); select(selected); break; case MotionEvent.ACTION_DOWN: DownPT.x = event.getX(); DownPT.y = event.getY(); rotation = getRotation(); StartPT = new PointF(getX(), getY()); SX = rotate.getMeasuredWidth(); SY = getY() - selected.getY(); break; case MotionEvent.ACTION_UP: setRotation(rotation); selected.setRotation(rotation); select(selected); drag = false; break; default: break; } return true; } }
XML布局代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:id="@+id/linear" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="right|bottom" android:orientation="horizontal"> <ImageView android:id="@+id/rotate" android:focusable="false" android:layout_width="20dp" android:layout_height="match_parent" android:src="@drawable/rotate" android:scaleType="fitStart" /> <LinearLayout android:id="@+id/selector" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" /> <ImageView android:id="@+id/scale" android:focusable="false" android:layout_width="20dp" android:layout_height="match_parent" android:src="@drawable/scale" android:scaleType="fitEnd" android:layout_gravity="bottom" /> </LinearLayout> </LinearLayout>
问题原因与修复说明
- 尺寸不更新:原代码修改
selector的LayoutParams后未调用requestLayout(),View不会重新测量布局,导致尺寸始终停留在第一次设置的值。修复后添加selector.requestLayout()强制刷新。 - 位置偏移:原代码错误引用
selector的topMargin计算位置,实际上selector的margin是父布局的参数,和目标View的位置无关。修复后直接基于目标View的getX()/getY()计算Sticker容器的位置。 - 缩放逻辑错误:原代码中
scale触摸监听里用触摸偏移直接设置目标View的LayoutParams宽高,逻辑错误,修复后改为基于初始尺寸加上偏移量,同时限制最小尺寸避免异常。 - 容器移除逻辑:原代码移除容器时未判断父容器是否为空,可能导致异常,修复后先判断父容器存在再移除。
内容的提问来源于stack exchange,提问作者ANDROID PRIME
相关产品推荐
相关产品推荐

