Android 书本打开和关闭动画

综合技术 2018-12-09 阅读原文

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 中用法,其实和一般普通 EditText 用法一致啦!

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)
                }
            })
        }

    }

    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>
复制代码
稀土掘金

责编内容by:稀土掘金阅读原文】。感谢您的支持!

您可能感兴趣的

Android与OpenCv的开发(一)-准备阶段 一.文档准备 官网下载地址:https://opencv.org/ 选择 版本,下载android版 下载解压 apk目录则存放着对应于各内核版本的OpenCV应用安装包 ...
Crack “VOA每日英语”, Unshelling + Removing ads... In the process of learning English these days , i want to find some useful apps to make me more affective,so, finding t...
这一次,我们用最详细的方式解析Android消息机制的源码... 决定再写一次有关Handler的源码 Handler源码解析 一、创建Handler对象 使用handler最简单的方式:直接 new 一个 Handler 的对象 Handler handler =...
Creating Accessible Android Apps: Assistive Techno... Whenever you design an Android app, you want as many people as possible to download and use that app, but this can only...
Android开发周报:微信模块化重构实践、滴滴插件化项目开源... 新闻 7月份安卓各版本份额:牛轧糖终于突破两位数 :谷歌近日给出了安卓系统各版本最新的份额数据,去年推出牛轧糖版本(7.0,7.1)终于达到了两位数的市场份额,占11.5%。2015年推出的棉花...