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

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>

问题原因与修复说明

  1. 尺寸不更新:原代码修改selector的LayoutParams后未调用requestLayout(),View不会重新测量布局,导致尺寸始终停留在第一次设置的值。修复后添加selector.requestLayout()强制刷新。
  2. 位置偏移:原代码错误引用selector的topMargin计算位置,实际上selector的margin是父布局的参数,和目标View的位置无关。修复后直接基于目标View的getX()/getY()计算Sticker容器的位置。
  3. 缩放逻辑错误:原代码中scale触摸监听里用触摸偏移直接设置目标View的LayoutParams宽高,逻辑错误,修复后改为基于初始尺寸加上偏移量,同时限制最小尺寸避免异常。
  4. 容器移除逻辑:原代码移除容器时未判断父容器是否为空,可能导致异常,修复后先判断父容器存在再移除。

内容的提问来源于stack exchange,提问作者ANDROID PRIME

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 16:07:09