动画 Android PhotoView 高度
Animate Android PhotoView height
我正在使用 https://github.com/chrisbanes/PhotoView 并尝试设置其高度的动画。
我使用 ValueAnimator
并更新布局高度,从而触发内部 PhotoViewAttacher
和 onGlobalLayout
转换矩阵。
是否有任何解决方法来防止比例和 y 位置不变,比如我可以以某种方式自己更新矩阵以保持图像 Y 位置和 scaleX/scaleY 不变吗?现在这些被重置为缩放 1.0 和图像的 y 位置中心。
动画代码:
ValueAnimator animator = ValueAnimator.ofInt(start, end).setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mImageView.getLayoutParams().height = (int) animation.getAnimatedValue();
mImageView.requestLayout();
}
});
animator.start();
我查看了源代码,但还没有测试下面的代码。据我所知,您想在动画期间阻止对 onGlobalLayout()
的调用。以下应该实现:
onAnimationStart():
mPhotoView.getViewTreeObserver()
.removeOnGlobalLayoutListener((PhotoViewAttacher)mPhotoView.getIPhotoViewImplementation());
onAnimationEnd():
mPhotoView.getViewTreeObserver()
.addOnGlobalLayoutListener((PhotoViewAttacher)mPhotoView.getIPhotoViewImplementation());
请注意,removeOnGlobalLayoutListener(OnGlobalLayoutListener)
适用于 API 版本 >=16。在此之前,您将使用 removeGlobalOnLayoutListener(OnGlobalLayoutListener)
.
onAnimationStart()
和 onAnimationEnd()
回调可以通过添加 ValueAnimator.AnimatorUpdateListener
到您的 ValueAnimator
.
同样,我不知道这是否可行 - 看起来应该可行。
编辑:
以下与上面的代码无关。
您可以为 top
和 bottom
属性设置动画,而不是为 PhotoView
的 height
设置动画。在我的测试中,动画这些属性并没有重置 y
位置,或更改 scaleX/scaleY 值:
int mOrigImageViewTop, mOrigImageViewBottom;
void crunchImageView() {
// Hold on to original values
if (mOrigImageViewTop == 0) {
mOrigImageViewTop = mImageView.getTop();
mOrigImageViewBottom = mImageView.getBottom();
}
// Top
ObjectAnimator objectAnimatorTop = ObjectAnimator.ofInt(mImageView,
"top", mOrigImageViewTop,
mOrigImageViewTop + 200 /*should be calculated dynamically*/);
// Bottom
ObjectAnimator objectAnimatorBottom = ObjectAnimator.ofInt(mImageView,
"bottom", mOrigImageViewBottom,
mOrigImageViewBottom - 200 /*should be calculated dynamically*/);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(objectAnimatorTop, objectAnimatorBottom);
animatorSet.setDuration(5000L);
animatorSet.start();
}
如果您正在设置 height
动画以便为 PhotoView
上方或下方的其他视图腾出空间,设置 top
/bottom
动画将无济于事。在这种情况下,使用 FrameLayout
托管 PhotoView
和其他 Views
,并控制它们的可见性可能是一种选择。
这比最初想象的要棘手。该库似乎没有 public API 来让它工作,所以这里有一个使用私有方法和字段的解决方法。以下解决方案似乎适用于我可以更改 PhotoView
的实际高度并且在动画期间缩放 + y 位置保持不变的情况。
首先扩展PhotoView
并编写如下代码:
// Private fields and methods
private Matrix mBaseMatrix;
private Field mAttacher;
private Method mGetDrawMatrix;
private Method mSetImageViewMatrix;
private Method mCheckMatrixBounds;
...
@Override
protected void init() {
super.init();
getViewTreeObserver().removeOnGlobalLayoutListener(
(ViewTreeObserver.OnGlobalLayoutListener) getIPhotoViewImplementation());
getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateBaseMatrix();
}
});
}
private void updateBaseMatrix() {
try {
if (mBaseMatrix == null) {
mBaseMatrix = getMatrix("mBaseMatrix");
}
if (mBaseMatrix != null) {
mBaseMatrix.setValues(new float[]{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
});
}
if (mAttacher == null) {
mAttacher = PhotoView.class.getDeclaredField("mAttacher");
mAttacher.setAccessible(true);
}
if (mGetDrawMatrix == null) {
mGetDrawMatrix = PhotoViewAttacher.class.getDeclaredMethod("getDrawMatrix");
mGetDrawMatrix.setAccessible(true);
}
Matrix drawMatrix = (Matrix) mGetDrawMatrix.invoke(mAttacher.get(this));
if (mSetImageViewMatrix == null) {
mSetImageViewMatrix = PhotoViewAttacher.class
.getDeclaredMethod("setImageViewMatrix", Matrix.class);
mSetImageViewMatrix.setAccessible(true);
}
mSetImageViewMatrix.invoke(mAttacher.get(this), drawMatrix);
if (mCheckMatrixBounds == null) {
mCheckMatrixBounds = PhotoViewAttacher.class.getDeclaredMethod("checkMatrixBounds");
mCheckMatrixBounds.setAccessible(true);
}
mCheckMatrixBounds.invoke(mAttacher.get(this));
} catch (Exception e) {
e.printStackTrace();
}
}
private Matrix getMatrix(String fieldName) {
try {
Field f = PhotoView.class.getDeclaredField("mAttacher");
f.setAccessible(true);
PhotoViewAttacher a = (PhotoViewAttacher) f.get(this);
f = PhotoViewAttacher.class.getDeclaredField(fieldName);
f.setAccessible(true);
return (Matrix) f.get(a);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
我正在使用 https://github.com/chrisbanes/PhotoView 并尝试设置其高度的动画。
我使用 ValueAnimator
并更新布局高度,从而触发内部 PhotoViewAttacher
和 onGlobalLayout
转换矩阵。
是否有任何解决方法来防止比例和 y 位置不变,比如我可以以某种方式自己更新矩阵以保持图像 Y 位置和 scaleX/scaleY 不变吗?现在这些被重置为缩放 1.0 和图像的 y 位置中心。
动画代码:
ValueAnimator animator = ValueAnimator.ofInt(start, end).setDuration(300);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mImageView.getLayoutParams().height = (int) animation.getAnimatedValue();
mImageView.requestLayout();
}
});
animator.start();
我查看了源代码,但还没有测试下面的代码。据我所知,您想在动画期间阻止对 onGlobalLayout()
的调用。以下应该实现:
onAnimationStart():
mPhotoView.getViewTreeObserver()
.removeOnGlobalLayoutListener((PhotoViewAttacher)mPhotoView.getIPhotoViewImplementation());
onAnimationEnd():
mPhotoView.getViewTreeObserver()
.addOnGlobalLayoutListener((PhotoViewAttacher)mPhotoView.getIPhotoViewImplementation());
请注意,removeOnGlobalLayoutListener(OnGlobalLayoutListener)
适用于 API 版本 >=16。在此之前,您将使用 removeGlobalOnLayoutListener(OnGlobalLayoutListener)
.
onAnimationStart()
和 onAnimationEnd()
回调可以通过添加 ValueAnimator.AnimatorUpdateListener
到您的 ValueAnimator
.
同样,我不知道这是否可行 - 看起来应该可行。
编辑:
以下与上面的代码无关。
您可以为 top
和 bottom
属性设置动画,而不是为 PhotoView
的 height
设置动画。在我的测试中,动画这些属性并没有重置 y
位置,或更改 scaleX/scaleY 值:
int mOrigImageViewTop, mOrigImageViewBottom;
void crunchImageView() {
// Hold on to original values
if (mOrigImageViewTop == 0) {
mOrigImageViewTop = mImageView.getTop();
mOrigImageViewBottom = mImageView.getBottom();
}
// Top
ObjectAnimator objectAnimatorTop = ObjectAnimator.ofInt(mImageView,
"top", mOrigImageViewTop,
mOrigImageViewTop + 200 /*should be calculated dynamically*/);
// Bottom
ObjectAnimator objectAnimatorBottom = ObjectAnimator.ofInt(mImageView,
"bottom", mOrigImageViewBottom,
mOrigImageViewBottom - 200 /*should be calculated dynamically*/);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(objectAnimatorTop, objectAnimatorBottom);
animatorSet.setDuration(5000L);
animatorSet.start();
}
如果您正在设置 height
动画以便为 PhotoView
上方或下方的其他视图腾出空间,设置 top
/bottom
动画将无济于事。在这种情况下,使用 FrameLayout
托管 PhotoView
和其他 Views
,并控制它们的可见性可能是一种选择。
这比最初想象的要棘手。该库似乎没有 public API 来让它工作,所以这里有一个使用私有方法和字段的解决方法。以下解决方案似乎适用于我可以更改 PhotoView
的实际高度并且在动画期间缩放 + y 位置保持不变的情况。
首先扩展PhotoView
并编写如下代码:
// Private fields and methods
private Matrix mBaseMatrix;
private Field mAttacher;
private Method mGetDrawMatrix;
private Method mSetImageViewMatrix;
private Method mCheckMatrixBounds;
...
@Override
protected void init() {
super.init();
getViewTreeObserver().removeOnGlobalLayoutListener(
(ViewTreeObserver.OnGlobalLayoutListener) getIPhotoViewImplementation());
getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
updateBaseMatrix();
}
});
}
private void updateBaseMatrix() {
try {
if (mBaseMatrix == null) {
mBaseMatrix = getMatrix("mBaseMatrix");
}
if (mBaseMatrix != null) {
mBaseMatrix.setValues(new float[]{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
});
}
if (mAttacher == null) {
mAttacher = PhotoView.class.getDeclaredField("mAttacher");
mAttacher.setAccessible(true);
}
if (mGetDrawMatrix == null) {
mGetDrawMatrix = PhotoViewAttacher.class.getDeclaredMethod("getDrawMatrix");
mGetDrawMatrix.setAccessible(true);
}
Matrix drawMatrix = (Matrix) mGetDrawMatrix.invoke(mAttacher.get(this));
if (mSetImageViewMatrix == null) {
mSetImageViewMatrix = PhotoViewAttacher.class
.getDeclaredMethod("setImageViewMatrix", Matrix.class);
mSetImageViewMatrix.setAccessible(true);
}
mSetImageViewMatrix.invoke(mAttacher.get(this), drawMatrix);
if (mCheckMatrixBounds == null) {
mCheckMatrixBounds = PhotoViewAttacher.class.getDeclaredMethod("checkMatrixBounds");
mCheckMatrixBounds.setAccessible(true);
}
mCheckMatrixBounds.invoke(mAttacher.get(this));
} catch (Exception e) {
e.printStackTrace();
}
}
private Matrix getMatrix(String fieldName) {
try {
Field f = PhotoView.class.getDeclaredField("mAttacher");
f.setAccessible(true);
PhotoViewAttacher a = (PhotoViewAttacher) f.get(this);
f = PhotoViewAttacher.class.getDeclaredField(fieldName);
f.setAccessible(true);
return (Matrix) f.get(a);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}