• 仿微信图片查看器入场退场动画


    引子

    看微信朋友圈的时候,当我们点击图片,图片会从点击的图片位置,逐渐放大,移动到正中间。退出图片查看器的时候,图片会逐渐变小,移到初始位置。对于用户来说,这是一种非常好的体验效果。

    于是,自己手动撸了一个进场退场的动画。具体参见下图 

     下面,开始介绍实现原理。

    原理解析

     先对上面的动画进行分析:

    1. 点击后,是重新打开了一个 activity,当然也可以是一个 fragment,都可以。
    2. 图片是从原始位置逐渐放大移到那个到中间位置的。这个过程都发生在打开后的页面中。

    3. 再次点击图片,退出的时候,先做动画,在移除原有的acticity.

    通过上面的分析,我们已经知道粗略的技术方案了,接下去就是解决一些细节问题。

    从第一个页面打开到第二个页面,需要传递什么参数?

    首先是,图片的位置信息,只有这样我们才可以将图片从原始位置逐步放大。下面我们开始获取图片的坐标参数:

       /**
         * 获取图片的位置信息
         */
        public ArrayList getImagePos(View view) {
            int[] locat = new int[2];
            view.getLocationOnScreen(locat);
            int width = view.getWidth();
            int height = view.getHeight();
            ArrayList<Integer> posArr = new ArrayList<>();
            posArr.add(locat[0]);
            posArr.add(locat[0] + width);
            posArr.add(locat[1]);
            posArr.add(locat[1] + height);
            return posArr;
        }

    将图片的位置信息传给新的 actvity :

            // 点击事件
            View.OnClickListener onClickListener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int pos = mRecyclerView.getChildAdapterPosition(v);
                    Intent intent = new Intent();
                    intent.setClass(mContext, Main2Activity.class);
                    intent.putIntegerArrayListExtra(POS_ARRAY, getImagePos(v));
                    intent.putExtra(IMAGE_POS, pos);
                    startActivity(intent);
                    overridePendingTransition(0, 0);
                }
            };

    注意需要去除原有的 activity 的默认动画。避免影响图片的动画效果。

    如何让图片从原始位置动起来?

     在做动画前,需要做一些准备工作,比如计算可显示图片的屏幕宽高,放大系数,移动系数等。

    /**
         * 计算动画位移和缩放
         */
        private void computerAnimationParams() {
            if (mPosArr.size() != 4) {
                return;
            }
            int screenHigh = mMetrics.heightPixels;
            int screenWidth = mMetrics.widthPixels;
            // 计算可用于显示图片的屏幕高度
            int availableScreenHeight = screenHigh - getStatusBarHeight();
    
            int imageHeight = mPosArr.get(3) - mPosArr.get(2);
            mOriginWidth = mPosArr.get(1) - mPosArr.get(0);
    
            float radioWidth = screenWidth * 1.0f / mOriginWidth;
            float radioHeight = availableScreenHeight * 1.0f / imageHeight;
            // 获取放大系数
            mRadio = Math.min(radioHeight, radioWidth);
    
            // 计算位移距离,屏幕中心与图片中心的距离,
            mTransition[0] = (float) (screenWidth / 2 - (mPosArr.get(0)
                    + (mPosArr.get(1) - mPosArr.get(0)) / 2));
            mTransition[1] = (float) ((availableScreenHeight) / 2
                    - (mPosArr.get(2) - getStatusBarHeight() + (mPosArr.get(3) - mPosArr.get(2)) / 2));
        }

     具体可以参考上面的代码,记得要考虑状态栏对于动画的影响。计算好了之后,我们得将现有的图片先设置成原始大小:

    /**
         * 动画前的准备工作
         **/
        private void doReadyJob() {
            // 需要对图片的宽度进行调整
            FrameLayout.LayoutParams params
                    = new FrameLayout.LayoutParams(mOriginWidth, FrameLayout.LayoutParams.MATCH_PARENT);
            params.setMargins(mPosArr.get(0), 0, mPosArr.get(1), 0);
            mImageView.setLayoutParams(params);
        }

    一切准备就绪后,我们得将图片从原始位置移动到正中间。下面就是动画实现细节:

    /**
         * 模拟入场动画
         */
        private void showEnterAnim() {
            final ValueAnimator enterAnimator = ValueAnimator.ofFloat(0f, 1f).setDuration(DURATION_IN);
            enterAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            enterAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    mImageView.setAlpha(1f);
                    mImageView.setScaleX(1 + (mRadio - 1) * value);
                    mImageView.setScaleY(1 + (mRadio - 1) * value);
                    mImageView.setTranslationX(mTransition[0] * value);
                    mImageView.setTranslationY(-mTransition[1] * (1f - value));
                    mImageView.invalidate();
                }
            });
            enterAnimator.start();
        }

     这里我们通过 ValueAnimator 来控制更新动画进度。到这里,图片的进场动画就完成了。

    对于退场动画,原理是一样的:

    /**
         * 模拟退场动画
         */
        private void showOutAnim() {
            final ValueAnimator exitAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(DURATION_IN);
            mImageView.setAlpha(1f);
            exitAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            exitAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (float) animation.getAnimatedValue();
                    mImageView.setScaleX((mRadio - 1) * value + 1);
                    mImageView.setScaleY((mRadio - 1) * value + 1);
                    mImageView.setTranslationX(mTransition[0] * value);
                    mImageView.setTranslationY(-mTransition[1] * (1f - value));
                }
            });
            exitAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    quitActivityWithoutAnimation();
                }
            });
            exitAnimator.start();
    
        }

    到这里,动画的主要原理已经剖析完成。

    源码: https://github.com/huanshen/PictureViewer

    目前这个比较简单,真的在项目实现起来的时候,代码量还是蛮多的,并且考虑的也要更多。

    比如,你还得传递图片的 url,图片查看器中图片可放大缩小,拖拽等。

  • 相关阅读:
    判断是否声明了某个特性
    如何月份英文缩写字符串转日期
    要捕捉System.Exception异常吗
    jenkins发布到nuget
    C# – Record, Class, Struct
    Google Maps Embed API & JavaScript API
    图片综合知识
    ASP.NET Core – ADO.NET
    CSS – 管理
    JavaScript Library – PhotoSwipe
  • 原文地址:https://www.cnblogs.com/huansky/p/10426278.html
Copyright © 2020-2023  润新知