Android 書本開啟和關閉動畫

天星技術團隊發表於2018-12-08

作者: Jooyer, 時間: 2018.12.08

Android 書本開啟和關閉動畫

Github地址,歡迎點贊,fork

我偶爾一次發現掌閱的開啟書本動畫不錯,然後度娘了一下,發現一個連結:download.csdn.net/download/we… , 下載下來學習膜拜了一下,發現有些動畫過度和掌閱有點不一樣,所以我就拷貝原始碼並略微修改了下,如果想看原著,上面已經貼了,這個效果是GIF,真機更流暢一些.

一共就是2個類+2個跳轉動畫

首先,看看 BookOpenView

public class BookOpenView extends FrameLayout implements
        Animator.AnimatorListener {

    private BookOpenViewValue mStartValue;
    private AnimatorSet mAnimatorSet1, mAnimatorSet2;
    private TextView tv_book_name;
    private ImageView iv_book_content;
    private View iv_book_cover;
    private ConstraintLayout cl_book_container;
    // 預設開啟書本動畫是關閉的
    private AtomicBoolean isOpened = new AtomicBoolean(false);

    public BookOpenView(@NonNull Context context) {
        super(context);
        initView(context);
    }

    public BookOpenView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public BookOpenView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    public void initView(Context context) {

        LayoutInflater.from(context).inflate(R.layout.book_open_close, this);
        setBackgroundColor(Color.TRANSPARENT);
        tv_book_name = (TextView) findViewById(R.id.tv_book_name);
        iv_book_content = (ImageView) findViewById(R.id.iv_book_content);
        iv_book_cover = (View) findViewById(R.id.iv_book_cover);
        cl_book_container = (ConstraintLayout) findViewById(R.id.cl_book_container);

    }

    /**
     * 開啟動畫
     */
    public synchronized void startAnim(final BookOpenViewValue startValue, BookOpenViewValue endValue) {
        if (!isOpened.get()) {

            if (!TextUtils.isEmpty(startValue.getBookName())){
                tv_book_name.setText(startValue.getBookName());
            }

            // 如果有圖片,可以在此給 iv_book_cover 設定,圖解儘量不要太大,10K以內為佳

            mStartValue = startValue;
            System.out.println("startAnim===========mStartValue: " + mStartValue.toString() + " ========startValue: " + startValue.toString());
            LayoutParams layoutParams = (LayoutParams) cl_book_container.getLayoutParams();
            layoutParams.width = startValue.getRight() - startValue.getLeft();
            layoutParams.height = startValue.getBottom() - startValue.getTop();
            cl_book_container.setLayoutParams(layoutParams);
            iv_book_content.setLayoutParams(layoutParams);

            cl_book_container.setTranslationX(startValue.getLeft());
            cl_book_container.setTranslationY(startValue.getTop());

            // 計算縮放的起始位置和縮放中心點
            final int x1 = ((layoutParams.width * (endValue.getRight() - layoutParams.width))
                    - layoutParams.width * (endValue.getRight() - startValue.getRight()))
                    / (endValue.getRight() - layoutParams.width);

            final float sX1 = (layoutParams.width - x1 + endValue.getRight() - startValue.getRight()) * 1.0f / (layoutParams.width - x1);
            final float sX2 = (x1 + startValue.getLeft()) * 1.0f / x1;

            final float sX = Math.max(sX1, sX2);
            final int y1 = ((layoutParams.height * (endValue.getBottom() - layoutParams.height))
                    - layoutParams.height * (endValue.getBottom() - startValue.getBottom()))
                    / (endValue.getBottom() - layoutParams.height);
            final float sY1 = (layoutParams.height - y1 + endValue.getBottom() - startValue.getBottom()) * 1.0f / (layoutParams.height - y1);
            final float sY2 = (y1 + startValue.getTop()) * 1.0f / y1;
            final float sY = Math.max(sY1, sY2);
            mStartValue.setX(x1);
            mStartValue.setsX(sX);
            mStartValue.setY(y1);
            mStartValue.setsY(sY);

            cl_book_container.setPivotX(0);
            cl_book_container.setPivotY(y1);

            // 對 封面進行縮放
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(cl_book_container,
                    "scaleX", 1.0f, sX * 0.8f);
            scaleX.setDuration(1000);
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(cl_book_container,
                    "scaleY", 1.0f, sY);
            scaleY.setDuration(1000);

            // 對 封面進行平移
            ObjectAnimator translationLine = ObjectAnimator.ofFloat(cl_book_container,
                    "translationX", startValue.getLeft(), 0);
            translationLine.setDuration(1000);
            // 對封面進行旋轉
            ObjectAnimator rotationY = ObjectAnimator.ofFloat(cl_book_container,
                    "rotationY", 0, -150);
            rotationY.setDuration(600);
            mAnimatorSet1 = new AnimatorSet();
            mAnimatorSet1.playTogether(scaleX, scaleY, translationLine, rotationY);
            mAnimatorSet1.start();

            /* ------------------------------------------------ */
            // 對 內部內容進行縮放,必須和封面保持一致的動作
            iv_book_content.setPivotX(x1);
            iv_book_content.setPivotY(y1);
            iv_book_content.setTranslationX(startValue.getLeft());
            iv_book_content.setTranslationY(startValue.getTop());
            ObjectAnimator scaleX2 = ObjectAnimator.ofFloat(iv_book_content,
                    "scaleX", 1.0f, sX);
            scaleX2.setDuration(1000);
            ObjectAnimator scaleY2 = ObjectAnimator.ofFloat(iv_book_content,
                    "scaleY", 1.0f, sY);
            scaleY2.setDuration(1000);

            mAnimatorSet2 = new AnimatorSet();
            mAnimatorSet2.playTogether(scaleX2, scaleY2);
            mAnimatorSet2.addListener(this);
            mAnimatorSet2.start();
        }
    }

    /**
     * 關閉動畫  , 邏輯和開始動畫相反
     */
    public synchronized void closeAnim() {
        if (isOpened.get()) {
            setVisibility(VISIBLE);
            setAlpha(1.0f);

            cl_book_container.setScaleX(mStartValue.getsX() * 0.8f);
            cl_book_container.setScaleY(mStartValue.getsY());
            cl_book_container.setRotationY(-150);
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(cl_book_container,
                    "scaleX", mStartValue.getsX() * 0.8f, 1.0f);
            scaleX.setDuration(1000);
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(cl_book_container,
                    "scaleY", mStartValue.getsY(), 1.0f);
            scaleY.setDuration(1000);

            ObjectAnimator translationLine = ObjectAnimator.ofFloat(cl_book_container,
                    "translationX", 0, mStartValue.getLeft());
            translationLine.setDuration(1000);

            ObjectAnimator rotationY = ObjectAnimator.ofFloat(cl_book_container,
                    "rotationY", -150, 0);
            rotationY.setDuration(600);
            rotationY.setStartDelay(400);
            mAnimatorSet1 = new AnimatorSet();
            mAnimatorSet1.playTogether(scaleX, scaleY, translationLine, rotationY);
            mAnimatorSet1.start();

            /* ------------------------------------------------ */

            iv_book_content.setScaleX(mStartValue.getsX());
            iv_book_content.setScaleY(mStartValue.getsY());

            ObjectAnimator scaleX2 = ObjectAnimator.ofFloat(iv_book_content,
                    "scaleX", mStartValue.getsX(), 1.0f);
            scaleX2.setDuration(1000);
            ObjectAnimator scaleY2 = ObjectAnimator.ofFloat(iv_book_content,
                    "scaleY", mStartValue.getsY(), 1.0f);
            scaleY2.setDuration(1000);
            mAnimatorSet2 = new AnimatorSet();
            mAnimatorSet2.playTogether(scaleX2, scaleY2);
            mAnimatorSet2.start();
            mAnimatorSet2.addListener(this);
        }
    }


    @Override
    public void onAnimationStart(Animator animation) {
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        System.out.println("onAnimationEnd========== " + isOpened.get());
        if (isOpened.get()) { // 關閉書本動畫執行了
            isOpened.set(false);
            if (null != mEndListener) {
                mEndListener.onRemove();
            }
        } else { // 開啟書本動畫
            isOpened.set(true);
            if (null != mEndListener) {
                mEndListener.onAnimationEnd();
            }
        }
    }

    @Override
    public void onAnimationCancel(Animator animation) {
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
    }

    public void cancel() {
        mAnimatorSet1.cancel();
        mAnimatorSet2.cancel();
    }


    public interface OnAnimationEndListener {
        void onAnimationEnd();

        void onRemove();
    }

    private OnAnimationEndListener mEndListener;

    public void setEndListener(OnAnimationEndListener endListener) {
        mEndListener = endListener;
    }
}

複製程式碼

內容略多,主要涉及計算,這個細節調整,大家可根據自己喜歡的效果調整試試

再來看一個類: BookOpenViewValue ,一目瞭然啊

public class BookOpenViewValue {
    private int left;
    private int top;
    private int right;
    private int bottom;
    private float x;
    private float y;
    private float sX;
    private float sY;
    private String bookName;
    private String bookCover;

    public BookOpenViewValue(){}

    public BookOpenViewValue(int left, int top, int right, int bottom){
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }


    public int getRight() {
        return right;
    }

    public void setRight(int right) {
        this.right = right;
    }

    public int getBottom() {
        return bottom;
    }

    public void setBottom(int bottom) {
        this.bottom = bottom;
    }

    public int getLeft() {
        return left;
    }

    public void setLeft(int left) {
        this.left = left;
    }

    public int getTop() {
        return top;
    }

    public void setTop(int top) {
        this.top = top;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }

    public float getsX() {
        return sX;
    }

    public void setsX(float sX) {
        this.sX = sX;
    }

    public float getsY() {
        return sY;
    }

    public void setsY(float sY) {
        this.sY = sY;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getBookCover() {
        return bookCover;
    }

    public void setBookCover(String bookCover) {
        this.bookCover = bookCover;
    }
}
複製程式碼

最後來看看在 Activity 中用法

class MainActivity : AppCompatActivity() {
    private var mBookOpenView: BookOpenView? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        ll_contain.setOnClickListener {
            val window = window.decorView as ViewGroup
            val location = IntArray(2)
            ll_contain.getLocationInWindow(location)

            val startValue = BookOpenViewValue(location[0], location[1],
                    location[0] + (ll_contain.right - ll_contain.left),
                    location[1] + (ll_contain.bottom - ll_contain.top))


            val endValue = BookOpenViewValue(window.left,
                    window.top,
                    window.right,
                    window.bottom)

            mBookOpenView = BookOpenView(this@MainActivity)
            window.addView(mBookOpenView)
            mBookOpenView?.startAnim(startValue, endValue)

            mBookOpenView?.setEndListener(object : BookOpenView.OnAnimationEndListener {
                override fun onAnimationEnd() {
                    println("onAnimationEnd========== onAnimationEnd")
                    startActivity(Intent(this@MainActivity, ReadActivity::class.java))
                    overridePendingTransition(R.anim.read_fade_in, R.anim.read_fade_out)
                }

                override fun onRemove() {
                    println("onAnimationEnd========== onRemove")
                    window.removeView(mBookOpenView)
                }
            })
        }

    }

// 這個就是執行從 ReadActivity 返回後的動畫
    override fun onResume() {
        super.onResume()
        println("onAnimationEnd========== onResume")
        mBookOpenView?.closeAnim()
    }
}

複製程式碼

跳轉的 Activity 更簡單了:

class ReadActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_read)
    }

  // 重寫主要是去掉系統預設跳轉動畫,方便那邊執行closeAnim
    override fun finish() {
        super.finish()
        overridePendingTransition(0, 0)
    }
}
複製程式碼

最後貼上2個跳轉動畫: res/anim/

read_fade_in.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" >

</alpha>
複製程式碼

read_fade_out.xml

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="200"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0.0" >

</alpha>
複製程式碼

喜歡記得點贊,收藏,轉發哈!

Android 書本開啟和關閉動畫

相關文章