如何animationmatrix“剪出”一个图像?

我试图做一个图像查看器,当用户点击图像时,图像被“裁剪”,并显示完整的图像。

例如,在下面的截图中,用户最初只能看到小狗的一部分。 但是用户点击图片后,整个小狗就显露出来了。 第一个图像后面的图像显示了animation的结果。

截图

最初,ImageView在X和Y中缩放为50%。当用户单击图像时,ImageView缩小到100%,并重新计算ImageViewmatrix。

我尝试了各种方法来计算matrix。 但我似乎无法find一种适用于所有types作物和图像的作品:将肖像裁剪为肖像,将风景裁剪为风景,将肖像裁剪为肖像,将裁剪后的肖像裁剪为风景。 这甚至有可能吗?

这是我的代码现在。 我试图find什么要放在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); } } 

编辑:值得一提的是,matrix线性缩放不会。 ImageView线性缩放(0.5到1)。 但是如果我在animation期间线性地缩放matrix,那么在animation期间视图是挤压的。 最终结果看起来不错,但在animation过程中图像看起来很丑。

Solutions Collecting From Web of "如何animationmatrix“剪出”一个图像?"

我意识到你(和其他答案)一直试图解决这个问题,使用matrix操纵,但我想提出一个不同的方法,与你的问题中概述的相同的视觉效果。

为了不使用matrix来操纵图像的可见区域(view),为什么不用裁剪来定义这个可见区域呢? 这是一个相当直接的问题要解决:我们所要做的就是定义可见的矩形,并简单地忽略任何超出范围的内容。 如果我们制作这些界限,视觉效果就好像作物的边界放大和缩小一样。

幸运的是, Canvas支持通过各种clip*()方法clip*()来帮助我们在这里。 animation剪辑边界很容易,可以用与你自己的代码片段类似的方式来完成。

如果你把所有的东西放在一个普通的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(); } } 

真正的“魔术”是重写的onDraw()方法的添加行,其中给定的Canvas被剪裁到由mClipRect定义的矩形区域。 所有其他的代码和方法主要是帮助计算剪辑边界,确定裁减是否合理,以及animation。

从一个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(); } }); } } 

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

 <mh.so.img.ClippingImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/dog" /> 

给你一个视觉转换的想法:

在这里输入图像说明

这可能有助于… Android图像视图matrix比例+翻译

似乎需要两个操作:

  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); 

要从图像的中心缩放图像,您可能需要这样做(也许交换“ – ”命令)…

 matrix.postTranslate(translateCropX, translateCropY); matrix.postScale(zoom, zoom); matrix.postTranslate(-translateCropX, -translateCropY); 

我不确定这是否会animation( 为什么在缩放手势时调用setScaleX会导致闪烁? )。 也许这将有助于… http://developer.android.com/training/animation/zoom.html

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

不过,你也提到肖像/风景的差异,所以这变得更复杂一点。 在这种情况下,我发现最困难的事情是在所有情况下牢固地定义你想要发生的事情。

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

希望Android可以为你做很多这个… 如何在ImageView中缩放图像以保持纵横比

您可以让ViewGroup的子View超出父View的范围。 所以,举个例子,假设上面图像的清晰部分是父视图,而ImageView是子视图,那么我们只需要放大或缩小图像。 要做到这一点,从具有特定边界的FrameLayout开始。 例如在XML

 <FrameLayout android:id="@+id/image_parent" android:layout_width="200dip" android:layout_height="200dip" /> 

或以编程方式:

 FrameLayout parent = new FrameLayout(this); DisplayMetrics dm = getResources().getDisplayMetrics(); //specify 1/5 screen height and 1/5 screen width ViewGroup.LayoutParams params = new FrameLayout.LayoutParams(dm.widthPixles/5, dm.heightPixles/5); parent.setLayoutParams(params); //get a reference to your layout, then add this view: myViewGroup.addView(parent); 

接下来,添加ImageView子项。 你可以用XML声明它:

 <FrameLayout android:id="@+id/image_parent" android:layout_width="200dip" android:layout_height="200dip" > <ImageView android:id="@+id/image" android:layout_width="200dip" android:layout_height="200dip" android:scaleType="fitXY" /> </FrameLayout> 

并检索它使用

 ImageView image = (ImageView) findViewById(R.id.image); 

或者,您可以通过编程来创build它:

 ImageView image = new ImageView(this); image.setScaleType(ScaleType.FIT_XY); parent.addView(image); 

无论哪种方式,您将需要操纵它。 要将ImageView边界设置为超过其父项,可以这样做:

 //set the bounds to twice the size of the parent FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(parent.getWidth()*2, parent.getHeight()*2); image.setLayoutParams(params); //Then set the origin (top left corner). For API 11+, you can use setX() or setY(). //To support older APIs, its simple to use NineOldAndroids (http://nineoldandroids.com) //This is how to use NineOldAndroids: final float x = (float) (parent.getWidth()-params.width)/2; final float y = (float) (parent.getHeight()-params.height)/2; ViewHelper.setX(image, x); ViewHelper.setY(image, y); 

最后,你可以使用droidQuery库来轻松的处理你的Viewanimation(而且NineOldAndroids包含在droidQuery ,所以你可以直接将droidquery.jar添加到你的libs目录,并且能够完成目前为止所讨论的所有内容import self.philbrown.droidQuery.* 。自动导入可能搞砸了),你可以处理点击和animation,如下所示:

 $.with(image).click(new Function() { @Override public void invoke($ droidQuery, Object... params) { //scale down if (image.getWidth() != parent.getWidth()) { droidQuery.animate("{ width: " + parent.getWidth() + ", " + "height: " + parent.getHeight() + ", " + "x: " + Float.valueOf(ViewHelper.getX(parent)) + ", " +//needs to include a decimal in the string so droidQuery knows its a float. "y: " + Float.valueOf(ViewHelper.getY(parent)) + //needs to include a decimal in the string so droidQuery knows its a float. "}", 400,//or some other millisecond value for duration $.Easing.LINEAR,//specify the interpolator $.noop());//don't do anything when complete. } //scale up else { droidQuery.animate("{ width: " + parent.getWidth()*2 + ", " + "height: " + parent.getHeight()*2 + ", " + "x: " + x + ", " +//needs to include a decimal in the string so droidQuery knows its a float. "y: " + y + //needs to include a decimal in the string so droidQuery knows its a float. "}", 400,//or some other millisecond value for duration $.Easing.LINEAR,//specify the interpolator $.noop());//don't do anything when complete. } } }); 

试试这个

 public void setImageCrop(float value) { Matrix matrix = new Matrix(); matrix.postScale(Math.max(1, 2 - (1 * value)), Math.max(1, 2 - (1 * value))); matrix.postTranslate(Math.min(0, translateCropX - (translateCropX * value)), Math.min(0, translateCropY - (translateCropY * value))); img.setImageMatrix(matrix); }