类似微信的图片预览(上)

其实这种效果Android 5.0之后就支持了,大家可以自行查阅一下 android 共享动画

为了简单化,这里只使用一张图片来展示效果

首先分一下步骤:
1.在 IndexActivity 中显示小图片或者图片列表
2.在 ImageActivity 中显示效果之后的样式
3.动画样式以及在哪个页面执行改动画

先说一下动画:这里的动画是在 ImageActivity中执行的,动画有位移,缩放

(1)在 IndexActivity中主要获得改图片所在的位置信息

Rect rect = new Rect();
  //获取视图在屏幕中的位置(包括状态栏和标题栏)
  imageView.getGlobalVisibleRect(rect);

(2)在 ImageActivity中初始化该位置信息
(3)执行动画

现在开始上代码:

1.自定义产生动画的ImageView

public class TransFromImageView extends ImageView {

   public TransFromImageView(Context context) {
        super(context);
    }

    public TransFromImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {

        //初始化画笔,方便画背景
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mBgColor);

        //对图片进行操作
        matrix = new Matrix();
    }
}
  1. 设置动画的 起始值——结束值
//因为实在onGlobalLayout中之后调用的invalidate();图片已经加载完毕了
        if (mBitmap == null) {
            mBitmap = ((BitmapDrawable) getDrawable()).getBitmap();
        }

        int bitmapWidth = mBitmap.getWidth();
        int bitmapHeight = mBitmap.getHeight();
          /*
          刚开时进入时,通过IndexActivity 中到的位置信息,拷贝到ImageActivity窗口的位置,再进行图片的变化
         */

        // 第一步----根据前一个IndexActivity 图片的位置,完美复制到当前页面

        startTransform = new Transform();
        //刚开始进去的时候,背景色为0
        startTransform.alpha = 0;
        //图片顶部距离顶部的距离(除去状态栏)
        startTransform.top = startBounds.top - getStatusBarHeight(getContext());
        startTransform.left = startBounds.left;
        startTransform.width = startBounds.width();
        startTransform.height = startBounds.height();


        //第二部----计算图片在新页面显示后的位置(即初始位置)

        //计算显示后的  长和宽的比例
        float startScaleX = ((float) startBounds.width()) / ((float) bitmapWidth);
        float startScaleY = ((float) startBounds.height()) / ((float) bitmapHeight);
        //这是执行放大的操作,比例大的那边完整放大,比例小的根据比例大的scale放大
        //这个比例放大是放大到图片真实尺寸的大小,因为是   startBounds.width() / bitmapWidth  哪个比例大哪个先达到图片真实的储存,所以取比例大的
        startTransform.scale = startScaleX > startScaleX ? startScaleX : startScaleY;

        //以上是进入时开始,参数的配置


        /*
        * 结束终点时:参数的配置
        */
        endTransform = new Transform();
        //因为设置的  自定义的view 的长宽是match-parent,图片会自动按比例放大,直到一边达到getWidth()或者getHeight()
        endTransform.alpha = 255;
        //得出是长、宽哪个边达到屏幕的极限,
        float endScaleX = (float) getWidth() / bitmapWidth;
        float endScaleY = (float) getHeight() / bitmapHeight;
        //这个比例放大是真实图片尺寸放大到屏幕的尺寸的大小,以为是 getWidth() / bitmapWidth  哪个比例小哪个首先达到屏幕的尺寸,所以取小的
        endTransform.scale = endScaleX > endScaleY ? endScaleY : endScaleX;

        //这样的 startTransform.scale--->endTransform.scale 就可以达到屏幕的过渡了
        int endBitmapWidth = (int) (endTransform.scale * bitmapWidth);
        int endBitmapHeight = (int) (endTransform.scale * bitmapHeight);

        //图片在中间显示
        endTransform.left = (getWidth() - endBitmapWidth) / 2;
        endTransform.top = (getHeight() - endBitmapHeight) / 2;
        endTransform.width = endBitmapWidth;
        endTransform.height = endBitmapHeight;

3.确定 起始值之后 在操作onDraw() 方法

@Override
    protected void onDraw(Canvas canvas) {
        // super.onDraw(canvas);//这里一定不能执行父类的super,如果执行,会先出现大图后再慢慢执行动画

        if (getDrawable() == null) {
            return;
        }
        if (mStatus == Status.STATE_OUT || mStatus == Status.STATE_IN) {
            if (startTransform == null || endTransform == null || animTransform == null) {
                initTransform();
            }

            if (animTransform == null) {
               //再这里触发操作
                super.onDraw(canvas);
                return;
            }
            //初始化transform执行

            mPaint.setAlpha(animTransform.alpha);
            //画背景  类似canvas.drawColor(Color.BLUE)这样的用法
            canvas.drawPaint(mPaint);

            //对画布和图像进行操作
            int saveCount = canvas.getSaveCount();
            matrix.setScale(animTransform.scale, animTransform.scale);
            float translateX = -(mBitmap.getWidth() * animTransform.scale - animTransform.width) / 2;
            float translateY = -(mBitmap.getHeight() * animTransform.scale - animTransform.height) / 2;
            matrix.postTranslate(translateX, translateY);//matrix 的后乘

            canvas.translate(animTransform.left, animTransform.top);//画布的移动,起始位置,避免从顶部执行动画
            canvas.clipRect(0, 0, animTransform.width, animTransform.height);//可有可无
            canvas.concat(matrix);//画布关联matrix
            getDrawable().draw(canvas);
            canvas.restoreToCount(saveCount);

            if (transformStart) {
                startTransform();
            }


        } else {
            mPaint.setAlpha(255);
            canvas.drawPaint(mPaint);
            super.onDraw(canvas);
        }
    }

4.执行动画这里就不说了

5.下面给出完整的代码,没有写 退出的操作(道理一样)

public class TransFromImageView extends ImageView {
    public enum Status {
        STATE_NORMAL,
        STATE_IN,
        STATE_OUT
    }

    private boolean transformStart;
    Status mStatus = Status.STATE_NORMAL;
    private Transform animTransform;
    private Transform startTransform;
    private Transform endTransform;
    private Paint mPaint;
    private int mBgColor = 0xFF000000;
    private Matrix matrix;

    //开始进入时上一个activity传过来范围
    private Rect startBounds;
    private Bitmap mBitmap;

    //存储 进入 和 退出 时的参数
    private class Transform implements Cloneable {
        float left, top, width, height;
        int alpha;
        float scale;

        public Transform clone() {
            Transform obj = null;
            try {
                obj = (Transform) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return obj;
        }

        @Override
        public String toString() {
            return "Transform{" +
                    "left=" + left +
                    ", top=" + top +
                    ", width=" + width +
                    ", height=" + height +
                    ", alpha=" + alpha +
                    ", scale=" + scale +
                    '}';
        }
    }

    public TransFromImageView(Context context) {
        super(context);
    }

    public TransFromImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {

        //初始化画笔,方便画背景
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mBgColor);

        //对图片进行操作
        matrix = new Matrix();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (getDrawable() == null) {
            return;
        }
        if (mStatus == Status.STATE_OUT || mStatus == Status.STATE_IN) {
            if (startTransform == null || endTransform == null || animTransform == null) {
                initTransform();
            }

            if (animTransform == null) {
                super.onDraw(canvas);
                return;
            }
            //初始化transform执行

            mPaint.setAlpha(animTransform.alpha);
            //画背景  类似canvas.drawColor(Color.BLUE)这样的用法
            canvas.drawPaint(mPaint);

            //对画布和图像进行操作
            int saveCount = canvas.getSaveCount();
            matrix.setScale(animTransform.scale, animTransform.scale);
            float translateX = -(mBitmap.getWidth() * animTransform.scale - animTransform.width) / 2;
            float translateY = -(mBitmap.getHeight() * animTransform.scale - animTransform.height) / 2;
            matrix.postTranslate(translateX, translateY);

            canvas.translate(animTransform.left, animTransform.top);
            canvas.clipRect(0, 0, animTransform.width, animTransform.height);
            canvas.concat(matrix);
            getDrawable().draw(canvas);
            canvas.restoreToCount(saveCount);

            if (transformStart) {
                startTransform();
            }


        } else {
            mPaint.setAlpha(255);
            canvas.drawPaint(mPaint);
            super.onDraw(canvas);
        }
    }

    private void startTransform() {
        transformStart = false;
        if (animTransform == null) {
            return;
        }

        ValueAnimator animator = new ValueAnimator();
        animator.setDuration(300);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        if (mStatus == Status.STATE_IN) {
            PropertyValuesHolder scaleHolder = PropertyValuesHolder.ofFloat("animScale", startTransform.scale, endTransform.scale);
            PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofInt("animAlpha", startTransform.alpha, endTransform.alpha);
            PropertyValuesHolder leftHolder = PropertyValuesHolder.ofFloat("animLeft", startTransform.left, endTransform.left);
            PropertyValuesHolder topHolder = PropertyValuesHolder.ofFloat("animTop", startTransform.top, endTransform.top);
            PropertyValuesHolder widthHolder = PropertyValuesHolder.ofFloat("animWidth", startTransform.width, endTransform.width);
            PropertyValuesHolder heightHolder = PropertyValuesHolder.ofFloat("animHeight", startTransform.height, endTransform.height);
            animator.setValues(scaleHolder, alphaHolder, leftHolder, topHolder, widthHolder, heightHolder);
        } else if (mStatus == Status.STATE_OUT) {
            PropertyValuesHolder scaleHolder = PropertyValuesHolder.ofFloat("animScale", endTransform.scale, startTransform.scale);
            PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofInt("animAlpha", endTransform.alpha, startTransform.alpha);
            PropertyValuesHolder leftHolder = PropertyValuesHolder.ofFloat("animLeft", endTransform.left, startTransform.left);
            PropertyValuesHolder topHolder = PropertyValuesHolder.ofFloat("animTop", endTransform.top, startTransform.top);
            PropertyValuesHolder widthHolder = PropertyValuesHolder.ofFloat("animWidth", endTransform.width, startTransform.width);
            PropertyValuesHolder heightHolder = PropertyValuesHolder.ofFloat("animHeight", endTransform.height, startTransform.height);
            animator.setValues(scaleHolder, alphaHolder, leftHolder, topHolder, widthHolder, heightHolder);
        }
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animTransform.alpha = (Integer) animation.getAnimatedValue("animAlpha");
                animTransform.scale = (float) animation.getAnimatedValue("animScale");
                animTransform.left = (float) animation.getAnimatedValue("animLeft");
                animTransform.top = (float) animation.getAnimatedValue("animTop");
                animTransform.width = (float) animation.getAnimatedValue("animWidth");
                animTransform.height = (float) animation.getAnimatedValue("animHeight");
                invalidate();
            }
        });

        animator.start();

    }

    private void initTransform() {

        if (getDrawable() == null) {
            return;
        }
        if (startTransform != null && endTransform != null && animTransform != null) {
            return;
        }
        if (getWidth() == 0 || getHeight() == 0) {
            return;
        }
        if (mBitmap == null) {
            mBitmap = ((BitmapDrawable) getDrawable()).getBitmap();
        }

        //因为实在onGlobalLayout中之后调用的invalidate();图片已经加载完毕了
        if (mBitmap == null) {
            mBitmap = ((BitmapDrawable) getDrawable()).getBitmap();
        }

        int bitmapWidth = mBitmap.getWidth();
        int bitmapHeight = mBitmap.getHeight();
          /*
          刚开时进入时,前一个activity 中centerCrop的图片的位置,拷贝到现在页面的位置,再进行图片的变化
         */

        // 第一步----根据前一个activity图片的位置,完美复制到当前页面

        startTransform = new Transform();
        //刚开始进去的时候,背景色为0
        startTransform.alpha = 0;
        //图片顶部距离顶部的距离(除去状态栏)
        startTransform.top = startBounds.top - getStatusBarHeight(getContext());
        startTransform.left = startBounds.left;
        startTransform.width = startBounds.width();
        startTransform.height = startBounds.height();


        //第二部----计算图片在新页面显示后的位置

        //计算显示后的  长和宽的比例
        float startScaleX = ((float) startBounds.width()) / ((float) bitmapWidth);
        float startScaleY = ((float) startBounds.height()) / ((float) bitmapHeight);
        //这是执行放大的操作,比例大的那边完整放大,比例小的根据比例大的scale放大
        //这个比例放大是放大到图片真实尺寸的大小,因为是   startBounds.width() / bitmapWidth  哪个比例大哪个先达到图片真实的储存,所以取比例大的
        startTransform.scale = startScaleX > startScaleX ? startScaleX : startScaleY;

        //以上是进入时开始,参数的配置


        /*
        * 结束终点时:参数的配置
        */
        endTransform = new Transform();
        //因为设置的改view 的长宽是match-parent,图片会自动按比例放大,直到一边达到getWidth()或者getHeight()
        endTransform.alpha = 255;
        //得出是长、宽哪个边达到屏幕的极限,
        float endScaleX = (float) getWidth() / bitmapWidth;
        float endScaleY = (float) getHeight() / bitmapHeight;
        //这个比例放大是真实图片尺寸放大到屏幕的尺寸的大小,以为是 getWidth() / bitmapWidth  哪个比例小哪个首先达到屏幕的尺寸,所以取小的
        endTransform.scale = endScaleX > endScaleY ? endScaleY : endScaleX;

        //这样的 startTransform.scale--->endTransform.scale 就可以达到屏幕的过渡了
        int endBitmapWidth = (int) (endTransform.scale * bitmapWidth);
        int endBitmapHeight = (int) (endTransform.scale * bitmapHeight);

        //图片在中间显示
        endTransform.left = (getWidth() - endBitmapWidth) / 2;
        endTransform.top = (getHeight() - endBitmapHeight) / 2;
        endTransform.width = endBitmapWidth;
        endTransform.height = endBitmapHeight;

        //以上初始化参数完成

        Log.i("TransFromImageView", "initTransform:startTransform: " + startTransform.toString() + "\n\nendTransform:" + endTransform.toString());
        //判断是进入还是推出
        if (mStatus == Status.STATE_IN) {
            animTransform = startTransform.clone();
        }
        if (mStatus == Status.STATE_OUT) {
            animTransform = endTransform.clone();

        }
    }

    public void transformIn(Rect thunmbRect) {
        this.startBounds = thunmbRect;
        transformStart = true;
        mStatus = Status.STATE_IN;
        invalidate();
    }

    public static int getStatusBarHeight(Context context) {
        Class<?> c = null;
        Object obj = null;
        java.lang.reflect.Field field = null;
        int x = 0;
        int statusBarHeight = context.getResources().getDimensionPixelSize(R.dimen.default_status_bar_height);
        try {
            c = Class.forName("com.android.internal.R$dimen");
            obj = c.newInstance();
            field = c.getField("status_bar_height");
            x = Integer.parseInt(field.get(obj).toString());
            statusBarHeight = context.getResources().getDimensionPixelSize(x);
            return statusBarHeight;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statusBarHeight;
    }


}