如何设置矩阵动画以"裁剪"图像?

Thi*_*Roy 8 graphics android transformation matrix objectanimator

我正在尝试做一个图像查看器,当用户点击图像时,图像被"裁剪掉"并显示完整的图像.

例如,在下面的屏幕截图中,用户最初只能看到小狗的一部分.但是在用户点击图像后,整个小狗都被揭露了.在第一个后面褪色的图像显示了动画的结果.

截图

最初,ImageView在X和Y中缩放到50%.当用户点击图像时,ImageView将缩放回100%,并重新计算ImageView矩阵.

我尝试了各种方法来计算矩阵.但是我似乎无法找到适用于所有类型的裁剪和图像的裁剪:裁剪景观到肖像,裁剪景观到横向,裁剪纵向到纵向和裁剪纵向到横向.这甚至可能吗?

这是我现在的代码.我正试着找些什么setImageCrop().

public class MainActivity extends Activity {

private ImageView img;
private float translateCropX;
private float translateCropY;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    img = (ImageView) findViewById(R.id.img);
    Drawable drawable = img.getDrawable();

    translateCropX = -drawable.getIntrinsicWidth() / 2F;
    translateCropY = -drawable.getIntrinsicHeight() / 2F;

    img.setScaleX(0.5F);
    img.setScaleY(0.5F);
    img.setScaleType(ScaleType.MATRIX);

    Matrix matrix = new Matrix();
    matrix.postScale(2F, 2F); //zoom in 2X
    matrix.postTranslate(translateCropX, translateCropY); //translate to the center of the image
    img.setImageMatrix(matrix);

    img.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            final PropertyValuesHolder animScaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1F);
            final PropertyValuesHolder animScaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1F);

            final ObjectAnimator objectAnim = ObjectAnimator.ofPropertyValuesHolder(img, animScaleX, animScaleY);

            final PropertyValuesHolder animMatrixCrop = PropertyValuesHolder.ofFloat("imageCrop", 0F, 1F);

            final ObjectAnimator cropAnim = ObjectAnimator.ofPropertyValuesHolder(MainActivity.this, animMatrixCrop);

            final AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.play(objectAnim).with(cropAnim);

            animatorSet.start();

        }
    });
}

public void setImageCrop(float value) {
    // No idea how to calculate the matrix depending on the scale

    Matrix matrix = new Matrix();
    matrix.postScale(2F, 2F);
    matrix.postTranslate(translateCropX, translateCropY);
    img.setImageMatrix(matrix);
}

}
Run Code Online (Sandbox Code Playgroud)

编辑:值得一提的是,线性缩放矩阵是行不通的.ImageView线性缩放(0.5到1).但是如果我在动画期间线性缩放矩阵,则在动画期间视图会变窄.最终结果看起来很好,但在动画期间图像看起来很丑陋.

MH.*_*MH. 7

我意识到你(以及其他答案)一直试图使用矩阵操作来解决这个问题,但是我想提出一种不同的方法,它具有你问题中提到的相同的视觉效果.

而不是使用矩阵来操纵图像的可见区域(视图),为什么不在剪切方面定义这个可见区域?这是一个相当简单的问题需要解决:我们需要做的就是定义可见矩形并简单地忽略任何超出其边界的内容.如果我们然后为这些边界设置动画,则视觉效果就像裁剪边界向上和向下缩放一样.

幸运的是,Canvas通过各种clip*()方法支持剪辑,以帮助我们在这里.动画剪辑边界很简单,可以使用与您自己的代码片段类似的方式完成.

如果你把所有东西放在一个普通的简单扩展中ImageView(为了封装),你会得到一些看起来像这样的东西:

public class ClippingImageView extends ImageView {

    private final Rect mClipRect = new Rect();

    public ClippingImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initClip();
    }

    public ClippingImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initClip();
    }

    public ClippingImageView(Context context) {
        super(context);
        initClip();
    }

    private void initClip() {
        // post to message queue, so it gets run after measuring & layout
        // sets initial crop area to half of the view's width & height
        post(new Runnable() {
            @Override public void run() {
                setImageCrop(0.5f);
            }
        });
    }

    @Override protected void onDraw(Canvas canvas) {
        // clip if needed and let super take care of the drawing
        if (clip()) canvas.clipRect(mClipRect);
        super.onDraw(canvas);
    }

    private boolean clip() {
        // true if clip bounds have been set aren't equal to the view's bounds
        return !mClipRect.isEmpty() && !clipEqualsBounds();
    }

    private boolean clipEqualsBounds() {
        final int width = getWidth();
        final int height = getHeight();
        // whether the clip bounds are identical to this view's bounds (which effectively means no clip)
        return mClipRect.width() == width && mClipRect.height() == height;
    }

    public void toggle() {
        // toggle between [0...0.5] and [0.5...0]
        final float[] values = clipEqualsBounds() ? new float[] { 0f, 0.5f } : new float[] { 0.5f, 0f };
        ObjectAnimator.ofFloat(this, "imageCrop", values).start();
    }

    public void setImageCrop(float value) {
        // nothing to do if there's no drawable set
        final Drawable drawable = getDrawable();
        if (drawable == null) return;

        // nothing to do if no dimensions are known yet
        final int width = getWidth();
        final int height = getHeight();
        if (width <= 0 || height <= 0) return;

        // construct the clip bounds based on the supplied 'value' (which is assumed to be within the range [0...1])
        final int clipWidth = (int) (value * width);
        final int clipHeight = (int) (value * height);
        final int left = clipWidth / 2;
        final int top = clipHeight / 2;
        final int right = width - left;
        final int bottom = height - top;

        // set clipping bounds
        mClipRect.set(left, top, right, bottom);
        // schedule a draw pass for the new clipping bounds to take effect visually
        invalidate();
    }

}
Run Code Online (Sandbox Code Playgroud)

真正的"魔法"是重写onDraw()方法的附加行,其中给定Canvas被剪切为由定义的矩形区域mClipRect.所有其他代码和方法主要用于帮助计算剪辑边界,确定裁剪是否合理以及动画.

从a中使用它Activity现在简化为以下内容:

public class MainActivity extends Activity {

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_activity);

        final ClippingImageView img = (ClippingImageView) findViewById(R.id.img);
        img.setOnClickListener(new OnClickListener() {
            @Override public void onClick(View v) {
                img.toggle();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

布局文件将指向我们的自定义,ClippingImageView如下所示:

<mh.so.img.ClippingImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/dog" />
Run Code Online (Sandbox Code Playgroud)

为了让您了解视觉过渡:

在此输入图像描述


joz*_*yqk 3

这可能会有所帮助... Android 图像视图矩阵缩放 + 翻译

似乎需要两个操作:

  1. 将容器从 0.5 缩放到 1.0
  2. 将内部图像从 2.0 缩放到 1.0

我认为这很简单

float zoom = 1.0F + value;
float scale = 1.0F / zoom;
img.setScaleX(scale);
img.setScaleY(scale);
...
matrix.postScale(zoom, zoom);
Run Code Online (Sandbox Code Playgroud)

要从图像中心缩放/缩放,您可能需要执行类似的操作(可能交换“-”顺序)...

matrix.postTranslate(translateCropX, translateCropY);
matrix.postScale(zoom, zoom);
matrix.postTranslate(-translateCropX, -translateCropY);
Run Code Online (Sandbox Code Playgroud)

我不确定这是否会产生动画(为什么在捏缩放手势期间调用 setScaleX 会导致闪烁?)。也许这会有所帮助... http://developer.android.com/training/animation/zoom.html

结果是内部图像始终保持相同的分辨率。

然而,您还提到了纵向/横向差异,因此这会变得更加复杂。我发现在这种情况下最困难的事情是明确定义您希望在所有情况下发生的事情。

我猜您正在寻找一个小缩略图网格,所有缩略图都具有相同的大小(这意味着它们都具有相同的纵横比)。您想要在缩略图容器中显示不同纵横比和大小的图像,但要放大。当您选择图像时,边框会扩展(与其他图像重叠?)。存在一个限制,即显示的图像必须保持相同的分辨率。您还提到,选择完整图像后必须可见,并且缩略图大小必须是缩略图的两倍。最后一点提出了另一个问题 - 缩略图高度、宽度或两者的两倍(在这种情况下,如果图像长宽比不匹配,您会裁剪或缩放)。另一个可能出现的问题是图像分辨率不足的情况。

希望 android 可以为你做很多这样的事情...如何在 ImageView 中缩放图像以保持宽高比