1. 程式人生 > >仿微信圖片查看器入場退場動畫

仿微信圖片查看器入場退場動畫

要去 block getheight available 新的 坐標 scale epo inf

引子

看微信朋友圈的時候,當我們點擊圖片,圖片會從點擊的圖片位置,逐漸放大,移動到正中間。退出圖片查看器的時候,圖片會逐漸變小,移到初始位置。對於用戶來說,這是一種非常好的體驗效果。

於是,自己手動擼了一個進場退場的動畫。具體參見下圖

技術分享圖片

下面,開始介紹實現原理。

原理解析

先對上面的動畫進行分析:

  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,圖片查看器中圖片可放大縮小,拖拽等。

仿微信圖片查看器入場退場動畫