美文网首页安卓开发博客Android自定义View
【原创】属性动画-仿掌阅电子书打开动画效果

【原创】属性动画-仿掌阅电子书打开动画效果

作者: 刀放下好好说话 | 来源:发表于2017-09-05 20:32 被阅读166次

一、先看掌阅动画效果

掌阅动画.gif

低配版:

电子书动画.gif

二、分析

从动画上看,可以拆解分为三部分进行:

  1. 书壳的翻转动画;
  2. 书扉页的放大动画;
  3. 书壳的放大动画。(这个动画和扉页是同步进行的,但是仔细看,可以看到,书壳的高度变化速率和扉页是一样的,但是宽度的速率要低于扉页)

三、代码实现

我们选择用属性动画实现,那么首先要确定哪些属性是变化的。首先扉页放大的过程中,宽高是变化的,随着宽高变化,位置也需要调整,至少需要记录left和top。那就是至少需要四个值。但是宽可以通过

宽度 = right - left
高度 = bottom - top

索性我们建立一个对象来存储每一本书的(left, top, right, bottom)。

public class BookValue {
    private int left;
    private int top;
    private int right;
    private int bottom;
}

这样我们问题就变成了,如果知道起始和终点的BookValue,那么就可以计算出中间如何一个位置的BookValue。有没有很熟悉?没错就是属性动画的自定义估值器。

public class BookEvaluator implements TypeEvaluator<BookValue> {
    @Override
    public BookValue evaluate(float fraction, BookValue startValue, BookValue endValue) {
        int startLeft = startValue.getLeft();
        int startTop = startValue.getTop();
        int startRight = startValue.getRight();
        int startBottom = startValue.getBottom();

        int endLeft = endValue.getLeft();
        int endTop = endValue.getTop();
        int endRight = endValue.getRight();
        int endBottom = endValue.getBottom();

        BookValue bookValue = new BookValue();
        bookValue.setLeft((int) (startLeft - fraction * (startLeft - endLeft)));
        bookValue.setTop((int) (startTop - fraction * (startTop - endTop)));
        bookValue.setRight((int) (startRight - fraction * (startRight - endRight)));
        bookValue.setBottom((int) (startBottom - fraction * (startBottom - endBottom)));

        return bookValue;
    }
}

新建一个估值器,传进去开始和结束的BookValue,计算返回中间的BookValue。计算不说了,那么怎么确定开始和结束位置呢,结束位置很好确定,就是整个屏幕。(实际操作中发现需要减去状态栏的高度,如果追求精度的话需要考虑,我们这里没有考虑)
那么起始位置怎么确定呢?我们点击的时候是可以拿到当前这个View的,这个View的属性里面就带了需要的位置信息。

convertView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ViewGroup window = (ViewGroup) getWindow().getDecorView();
        BookValue startValue = new BookValue(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
        BookValue endValue = new BookValue(window.getLeft(), window.getTop(), window.getRight(), window.getBottom());

        BookView bookView = new BookView(mContext);
        window.addView(bookView);
        bookView.startAnim(startValue, endValue);
    }
});            

获取屏幕的DecorView,可以说这个view的位置就是结束位置。onClick里面的view就是点击的开始位置。点击的view里面有两个子View,一个是书壳一个是书扉页,我们可以把两个view组合成一个自定义的ViewGroup:

public class BookView extends FrameLayout{

    private TextView tvBookName;
    private TextView tvBookAuthor;

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

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

    public BookView(@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.layout_book_view, this);
        setBackgroundColor(Color.TRANSPARENT);
        tvBookName = (TextView) findViewById(R.id.tv_book_name);
        tvBookAuthor = (TextView) findViewById(R.id.tv_book_author);
    }
}

BookView继承自Framelayout,再看它的布局:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_book_author"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="作者:\n小\n黑"
        android:gravity="center"
        android:background="#6d4c7c"
        android:textColor="#fff"
        />

    <TextView
        android:id="@+id/tv_book_name"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="android\n入\n门\n到\n放\n弃"
        android:gravity="center"
        android:background="#4d3b3b"
        android:textColor="#fff"
        />
</merge>

只有两个TextView,因为BookView是Framelayout,所以书壳在扉页的下面。细心的人可以看到BookView我们设置背景透明的,方便动画实现,省去了还要计算外层布局的位置,直接让它透明的充满全屏。
我们来看BookView里面的实现:

    /**
     * 开启动画
     */
    public void startAnim(BookValue startValue, BookValue endValue) {
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new BookEvaluator(), startValue, endValue);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(this);
        valueAnimator.addListener(this);

        tvBookName.setPivotX(0);
        tvBookName.setPivotY(500);
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tvBookName, "rotationY", 0, -180);
        objectAnimator.setDuration(1000);
        objectAnimator.setStartDelay(300);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(valueAnimator, objectAnimator);
        animatorSet.start();
    }

暴露一个开启动画的方法,动画上面知道肯定是两个(实际上是三个,这里简化了,所以效果也有点粗糙):

ValueAnimator valueAnimator = ValueAnimator.ofObject(new BookEvaluator(), startValue, endValue);

新建一个估值器,直接扔进属性动画里,这里不需要指定动画的作用对象,因为它实际就是开启了一个计算,不断的回调计算结果。那么回调去哪了呢?后面再说。再看第二个动画:

  tvBookName.setPivotX(0);
  tvBookName.setPivotY(500);
  ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tvBookName, "rotationY", 0, -180);
  objectAnimator.setDuration(1000);
  objectAnimator.setStartDelay(300);

这个动画是作用于书壳上的,首先要设定锚点,不然它会默认中间翻转。属性动画设置rotationY属性变化,谁的属性?第一个参数tvBookName。怎么变化?从0到-180,也就是逆时针。
锚点的PivotX等于0很好理解,就是绕y轴翻转,但是PivotY怎么理解呢?我也不清楚,但是设置了一个很大的值,它和屏幕会有一个夹角,很接近掌阅的效果。
还有一个延迟300ms,书壳是先随着扉页一起放大,再翻转,效果上就很真实了。

上面有说计算结果回调去哪了,有重写的方法回调:

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        BookValue midBookValue = (BookValue) animation.getAnimatedValue();

        tvBookName.setX(midBookValue.getLeft());
        tvBookName.setY(midBookValue.getTop());
        ViewGroup.LayoutParams layoutParams = tvBookName.getLayoutParams();
        layoutParams.width = midBookValue.getRight() - midBookValue.getLeft();
        layoutParams.height = midBookValue.getBottom() - midBookValue.getTop();
        tvBookName.setLayoutParams(layoutParams);

        tvBookAuthor.setX(midBookValue.getLeft());
        tvBookAuthor.setY(midBookValue.getTop());
        ViewGroup.LayoutParams layoutParams1 = tvBookAuthor.getLayoutParams();
        layoutParams1.width = midBookValue.getRight() - midBookValue.getLeft();
        layoutParams1.height = midBookValue.getBottom() - midBookValue.getTop();
        tvBookAuthor.setLayoutParams(layoutParams1);
    }

首先获取midBookValue,这个值是不断的返回的,所以下面的方法要不断的修改作用对象的属性,就形成了动画的视觉效果。那么我们需要修改哪些属性呢?
首先修改X与Y,这个书壳就会产生偏移(这里不能去设置left和top,会有坑,一直显示在屏幕的原点位置。至于原因,还没发现)。
然后修改书壳的宽高,是通过获取LayoutParams修改width和height实现的。
书扉页也需要同样的操作。
以上就是全部的思路。

四、最终效果

电子书动画.gif

这样看起来有点粗糙,是因为书壳和扉页宽度的变化是一致的,我们可以将书壳的宽度做一下限制,或者将他们的动画分开来写。

附上GayHub地址,有兴趣可以肛一波。

相关文章

  • 【原创】属性动画-仿掌阅电子书打开动画效果

    一、先看掌阅动画效果 低配版: 二、分析 从动画上看,可以拆解分为三部分进行: 书壳的翻转动画; 书扉页的放大动画...

  • 仿掌阅app打开书籍动画效果

    最近看到了掌阅app的打开动画,感觉不错,模仿了一下。首先看一下效果。 这个效果通过打开书籍时的效果Camera来...

  • 仿掌阅实现书籍打开动画

    本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发 一. 前言 上次打开掌阅的时...

  • 仿掌阅实现书籍打开动画

    一. 前言 上次打开掌阅的时候看到书籍打开动画的效果还不错,正好最近也在做阅读器的项目,所以想在项目中实现一下。 ...

  • Android自定义view:小技巧之实现打开书籍动画效果

    打开书籍效果 最近看到了掌阅app的打开动画,感觉不错,模仿了一下。首先看一下效果。 这个效果通过打开书籍时的效果...

  • [Android动画]属性动画-小球下落动画实现

    属性动画 属性动画是通过直接改变View属性,实现的动画效果。与补间动画不同的是,属性动画是对象的属性的真实改变,...

  • 属性动画animator

    属性动画和传统动画的区别 传统动画只是具有显示的效果,不会正真改变位置、属性动画则相反 传统动画的写法 属性动画的...

  • Android动画之属性动画

    属性动画和补间动画的不同之处就是它通过动态改变对象的属性从而达到动画效果,它可以对任何对象做动画,并且动画效果也得...

  • Android属性动画基础篇

    1、什么是属性动画 简单来说就是通过改变对象属性而形成的动画效果。 2、为什么使用属性动画 我们知道,在属性动画之...

  • 属性动画—仿雅虎加载效果

    通过自定view绘制几个不同颜色的小圆,通过旋转、聚合等动画实现的一个加载效果;自绘控件,肯定少不了画笔的使用; ...

网友评论

    本文标题:【原创】属性动画-仿掌阅电子书打开动画效果

    本文链接:https://www.haomeiwen.com/subject/vfepjxtx.html