【问题标题】:How to reposition imageview when scaling custom viewgroup?缩放自定义视图组时如何重新定位图像视图?
【发布时间】:2017-03-21 16:08:30
【问题描述】:

目标

我目前正在做一个小项目,它可以有效地做两件事:

  • 使用自定义 ViewGroup 显示可缩放/可拖动的图像(效果很好)
  • 在图像上显示 1-N 个标记/图钉,使用图像内的坐标(x/y 像素)作为锚点。这不应与 ViewGroup 一起缩放,只能重新定位自身。 (这效果不好)

问题(这是我想不通的!)

我做不到的是在缩放时定位标记。启动 Activity 时,我已设法将标记放置在设备屏幕上的正确位置(在 ViewGroup 的图像中),但是当我开始缩放 ViewGroup 时,标记似乎使用与相同的 x/y 相对坐标ViewGroup 本身。这意味着标记将在其“固定”位置移动的时间越长,我缩放视图越多。

更多信息

布局组件树,因为这可能是悲伤的根源,我目前使用的是:

  • 相对布局
    • CustomViewGroup(可缩放/可拖动)
      • 图像视图
    • ImageView(现在只保留一个标记)

我正在使用两个矩阵;一个用于 ViewGroup,一个用于标记。我认为我需要两个矩阵,以便标记可以独立于 ViewGroup 移动(即使它会遵循 ViewGroup 的许多模式)。

ViewGroup 中的代码

// Used to initially position the marker on the screen
public void placeMarker(float x, float y) {
    RelativeLayout parent = (RelativeLayout) this.getParent();
    mMapMarker = (ImageView) parent.findViewById(R.id.map_marker);

    // Set initial placement of marker (redundant if this can be fixed in dispatchDraw)
    mMapMarker.setX(x);
    mMapMarker.setY(y);

    // Setting the marker's matrix to the same initial values as the placement for use when the view translates/scales 
    mMatrixMarker.postTranslate(x, y);
    mMatrixMarker.postScale(1f,1f,mid.x,mid.y);
    mMatrixMarker.invert(mMatrixInverseMarker);
}

@Override
protected void dispatchDraw(Canvas canvas) {
    // Draw the marker using ImageView.setX/Y
    float[] markerValues = new float[9];
    mMatrixMarker.getValues(markerValues);
    mMapMarker.setX(markerValues[Matrix.MTRANS_X]);
    mMapMarker.setY(markerValues[Matrix.MTRANS_Y]);

    float[] values = new float[9];
    matrix.getValues(values);
    canvas.save();
    // Translate the canvas for panning and scaling the view
    canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
    canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
    super.dispatchDraw(canvas);
    canvas.restore();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    ...
    } else if (mode == ZOOM) {
      if (event.getPointerCount() > 1) {
          float newDist = spacing(event);
            if (newDist > 10f * density) {
                matrix.set(savedMatrix);
                float scale = (newDist / oldDist);
                float[] values = new float[9]; matrix.getValues(values);
                if(scale*values[Matrix.MSCALE_X] >= MAX_ZOOM) {
                    scale = MAX_ZOOM/values[Matrix.MSCALE_X];
                }
                if(scale*values[Matrix.MSCALE_X] <= MIN_ZOOM) {
                    scale = MIN_ZOOM/values[Matrix.MSCALE_X];
                }
                // Save the change in scale to the ViewGroup's matrix
                matrix.postScale(scale, scale, mid.x, mid.y);
                matrix.invert(matrixInverse);

                // Save the same change in scale for the marker (redundant if the scale is not supposed to be different)
                mMatrixMarker.set(mSavedMatrixMarker);
                mMatrixMarker.postScale(scale, scale, mid.x, mid.y);
                mMatrixMarker.invert(mMatrixInverseMarker);
            }
        } else {
            // Save the change in scale to the ViewGroup's matrix
            matrix.set(savedMatrix);
            float scale = event.getY() / start.y;
            matrix.postScale(scale, scale, mid.x, mid.y);
            matrix.invert(matrixInverse);

            // Save the same change in scale for the marker (redundant if the scale is not supposed to be different)
            mMatrixMarker.set(mSavedMatrixMarker);
            mMatrixMarker.postScale(scale, scale, mid.x, mid.y);
            mMatrixMarker.invert(mMatrixInverseMarker);
        }
    }
    ...
}

----编辑#1----

我现在对代码进行了更改,我现在在其中使用我认为更合适的矩阵实现,这要感谢 pskinks 提示。我看到了我的代码是如何改进的,我很感激。

但是,现在发生的情况是我处于标记正确放置在 ViewGroup 中的状态,并且发生了以下任一情况:

  • 标记随 ViewGroup 缩放而缩放。这使得标记保持在正确的“固定”位置,但也使它随着 ViewGroup 缩小/增长,这是我试图避免的。
  • 标记不随 ViewGroup 缩放而缩放。这使得标记固定和错位的 ViewGroup 缩放得越多。固定标记大小是正确的,但我需要一种方法来“抵消”缩放期间的偏移量。

ViewGroup 中的新代码

// Used to initially position the marker on the screen
public void placeMarker(float x, float y) {
    RelativeLayout parent = (RelativeLayout) this.getParent();
    mMapMarker = (ImageView) parent.findViewById(R.id.map_marker);
    mMapMarker.setScaleType(ImageView.ScaleType.MATRIX);

    // Setting the marker's matrix to the same initial values as the placement for use when the view translates/scales
    mMatrixMarker.postTranslate(x, y);
    mMatrixMarker.postScale(1f,1f,mid.x,mid.y);
    mMatrixMarker.invert(mMatrixInverseMarker);
}

@Override
protected void dispatchDraw(Canvas canvas) {

    // Draw the marker using a Matrix
    mMapMarker.setImageMatrix(mMatrixMarker);

    canvas.save();
    // Translate the canvas for panning and scaling the view
    canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
    canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
    super.dispatchDraw(canvas);
    canvas.restore();
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    ...
    } else if (mode == ZOOM) {
      if (event.getPointerCount() > 1) {
          float newDist = spacing(event);
            if (newDist > 10f * density) {
                matrix.set(savedMatrix);
                float scale = (newDist / oldDist);
                float[] values = new float[9]; matrix.getValues(values);
                if(scale*values[Matrix.MSCALE_X] >= MAX_ZOOM) {
                    scale = MAX_ZOOM/values[Matrix.MSCALE_X];
                }
                if(scale*values[Matrix.MSCALE_X] <= MIN_ZOOM) {
                    scale = MIN_ZOOM/values[Matrix.MSCALE_X];
                }
                // Save the change in scale to the ViewGroup's matrix
                matrix.postScale(scale, scale, mid.x, mid.y);
                matrix.invert(matrixInverse);

                // If this is included, the marker shrink/grows with the scaling of the ViewGroup
                // If this is not included, the marker stays in a fixed position, making it become misaligned during scaling
                mMatrixMarker.set(mSavedMatrixMarker);
                mMatrixMarker.postScale(scale, scale, mid.x, mid.y);
                mMatrixMarker.invert(mMatrixInverseMarker);
            }
        } else {
            // Save the change in scale to the ViewGroup's matrix
            matrix.set(savedMatrix);
            float scale = event.getY() / start.y;
            matrix.postScale(scale, scale, mid.x, mid.y);
            matrix.invert(matrixInverse);

            // If this is included, the marker shrink/grows with the scaling of the ViewGroup
            // If this is not included, the marker stays in a fixed position, making it become misaligned during scaling
            mMatrixMarker.set(mSavedMatrixMarker);
            mMatrixMarker.postScale(scale, scale, mid.x, mid.y);
            mMatrixMarker.invert(mMatrixInverseMarker);
        }
    }
    ...
}

【问题讨论】:

  • 使用 scaleType="matrixImageView#setImageMatrix() / ImageView#getImageMatrix() 并通过 Matrix 应用您的积分
  • 这听起来确实很容易解决!以前没有使用过矩阵比例类型,您是否怀疑我必须在我粘贴的任何一种方法中对代码进行额外的修改,或者它应该在进行您提出的更改时“正常工作”?

标签: android android-custom-view viewgroup android-viewgroup


【解决方案1】:

我设法弄清楚了这一点,它归结为跟踪标记的初始位置和缩放,并在拖动和缩放时重用这些值来计算其新位置。

标记的初始位置代码

public void placeMarker(float x, float y) {
    RelativeLayout parent = (RelativeLayout) this.getParent();
    mMapMarker = (ImageView) parent.findViewById(R.id.map_marker);
    mMapMarker.setScaleType(ImageView.ScaleType.MATRIX);

    mMatrixMarker.postTranslate(x, y);
    mMatrixMarker.postScale(1f,1f,mid.x,mid.y);
    mMatrixMarker.invert(mMatrixInverseMarker);

    mMatrixMarkerSource.postTranslate(x,y);
    mMatrixMarkerSource.postScale(1f,1f,mid.x,mid.y);
    mMatrixMarkerSource.invert(mMatrixInverseMarker);
    float[] valuesMatrixSource = new float[9];
    mMatrixMarkerSource.getValues(valuesMatrixSource);
}

标记初始位置的使用

/*
Draw the requested changes taking pan and zoom into account
*/
@Override
protected void dispatchDraw(Canvas canvas) {
    // Keep a reference to the marker's initial location and scale for comparison
    float[] valuesMarkerSource = new float[9];
    mMatrixMarkerSource.getValues(valuesMarkerSource);
    float sx = valuesMarkerSource[Matrix.MTRANS_X];
    float sy = valuesMarkerSource[Matrix.MTRANS_Y];

    // Keep a reference to the current location and scale of out ViewGroup
    float[] values = new float[9];
    matrix.getValues(values);

    Drawable d = getResources().getDrawable(R.drawable.ic_map_marker_150);
    // Adjust marker image position based on its measured size
    float vX = ((sx * values[Matrix.MSCALE_X]) + values[Matrix.MTRANS_X]) - (d.getIntrinsicWidth()/2);
    float vY = ((sy * values[Matrix.MSCALE_Y]) + values[Matrix.MTRANS_Y]) - (d.getIntrinsicHeight());
    mMatrixMarker.reset();
    mMatrixMarker.postTranslate(vX, vY);


    float[] valuesMarker = new float[9];
    mMatrixMarker.getValues(valuesMarker);
if(mMapMarker != null) {
  // We want to use a marker, so save the translated matrix to it
  mMapMarker.setImageMatrix(mMatrixMarker);
} else {
  /*
  TODO This is suboptimal. We really should just hide the marker once and not deal with it any more

  We don't want to use a marker, so hide it
   */
  RelativeLayout parent = (RelativeLayout) this.getParent();
  mMapMarker = (ImageView) parent.findViewById(R.id.map_marker);
  mMapMarker.setVisibility(ImageView.INVISIBLE);
}

我还把整个项目上传到了GotHub:https://github.com/shellstrom/ffa

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多