androidandroid-photoview

Animate Android PhotoView height


I am using https://github.com/chrisbanes/PhotoView and trying to animate its height.

I use ValueAnimator and update the layout height, so that triggers the internal PhotoViewAttacher and onGlobalLayoutwhich transforms the matrix.

Is there any workaround to prevent scale and y position to be unchanged, like could I somehow update the matrix myself to keep the image Y position and scaleX/scaleY unchanged? Now those are reset to scale 1.0 and y position center of image.

Animation code:

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

Solution

  • This was more tricky than first thought. The library doesn't seem to have public API's to get this to work, so here is a workaround using private methods and fields. Following solution seems to work with my cases where I can change the actual height of PhotoView and scale + y position keeps unchanged during the animations.

    First extend PhotoView and write following code:

    // 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;
    }