例如,在下面的屏幕截图中,用户最初只能看到小狗的一部分.但是在用户点击图像后,整个小狗都被揭露了.在第一个后面褪色的图像显示了动画的结果.
最初,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);
}
}
编辑:值得一提的是,线性缩放矩阵是行不通的. ImageView按比例线性缩放(0.5到1).但是如果我在动画期间线性缩放矩阵,则在动画期间视图会变窄.最终结果看起来很好,但在动画期间图像看起来很丑.
解决方法
而不是使用矩阵来操纵图像的可见区域(视图),为什么不在剪切方面定义这个可见区域?这是一个相当简单的问题需要解决:我们需要做的就是定义可见的矩形,并忽略任何超出其边界的内容.如果我们然后为这些边界设置动画,则视觉效果就像裁剪边界向上和向下缩放一样.
幸运的是,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();
}
}
真正的“魔法”是重写的onDraw()方法的附加行,其中给定的Canvas被剪切为由mClipRect定义的矩形区域.所有其他代码和方法主要用于帮助计算剪辑边界,确定裁剪是否合理以及动画.
从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" />
为了让您了解视觉过渡: