红橙Darren视频笔记 点赞效果 动画练习

效果

在这里插入图片描述

知识点

位移动画 随机数 估值器(TypeEvaluator )使用

思路

1.在底部中心位置初始化图片
1.1用上透明度变化和scale变化的动画
2.计算二阶贝塞尔曲线
3.添加透明度变化动画,飘到顶部时颜色最淡
4.优化
4.1加一些随机的插值器
4.2动画结束时将添加的View删除

大部分代码

1 自定义Layout

class LikesLayout extends RelativeLayout {
    private static final String TAG = "LikesLayout";

    //星星的图片宽高
    private int mDrawableWidth = 0;
    private int mDrawableHeight = 0;
    // 用于产生随机数
    private final Random mRandom;
    // 存储图片资源id
    private final int[] mImageResId;
    //整个layout宽高
    private int mWidth;
    private int mHeight;
    private final Interpolator[] mInterpolator;

    public LikesLayout(Context context) {
        this(context, null);
    }

    public LikesLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LikesLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mRandom = new Random();
        // 初始化资源id
        mImageResId = new int[]{R.drawable.star_blue, R.drawable.star_green, R.drawable.star_red, R.drawable.star_blue_bright, R.drawable.star_orange_light};
        Drawable drawable = ContextCompat.getDrawable(context, R.drawable.star_blue);
        //假设所有星星的大小相等(如果不等要分别计算)
        if (drawable != null) {
            mDrawableWidth = drawable.getIntrinsicWidth();
            mDrawableHeight = drawable.getIntrinsicHeight();
        }
        mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator(), new AccelerateInterpolator(),
                new DecelerateInterpolator(), new LinearInterpolator()};
    }

    // 获取控件的宽高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }

    public void addLikes() {
        for (int i = 0; i < 30; i++) {
            addLike();
        }
    }

    // 1.在底部中心位置初始化图片 用上透明度变化和scale变化的动画
    private void addLike() {
        // 添加一个ImageView在底部
        final ImageView likeIv = new ImageView(getContext());
        // 给一个图片资源(随机) 这里视频有点问题 比如 mRandom.nextInt(5);取值可能是是[0,5)的整数,视频里面写道 mRandom.nextInt(mImageRes.length - 1) 其实-1是不必要的
        int imageIndex = mRandom.nextInt(mImageResId.length);
        likeIv.setImageResource(mImageResId[imageIndex]);
        // 怎么添加到底部中心?利用RelativeLayout的LayoutParams
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        params.addRule(ALIGN_PARENT_BOTTOM);
        params.addRule(CENTER_HORIZONTAL);
        likeIv.setLayoutParams(params);
        addView(likeIv);

        AnimatorSet animator = getAnimator(likeIv);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                // 4.2动画结束时将添加的View删除
                removeView(likeIv);
            }
        });
        animator.start();
    }

    private AnimatorSet getAnimator(ImageView likeIv) {
        // 入场动画 添加的效果:有放大和透明度变化 (属性动画)
        // 1.1用上透明度变化和scale变化的动画
        AnimatorSet innerAnimator = new AnimatorSet();
        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(likeIv, "alpha", 0.3f, 1.0f);
        ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(likeIv, "scaleX", 0.3f, 1.0f);
        ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(likeIv, "scaleY", 0.3f, 1.0f);
        innerAnimator.playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator);
        innerAnimator.setDuration(350);


        // 运行的路径动画  playSequentially 按循序执行
        AnimatorSet allAnimatorSet = new AnimatorSet();
        allAnimatorSet.playSequentially(innerAnimator, getBezierAnimator(likeIv));// 2.计算二阶贝塞尔曲线 本节难点
        return allAnimatorSet;
    }

    private Animator getBezierAnimator(ImageView likeIv) {
        //计算三阶贝塞尔曲线中的起点 控制点1 控制点2 终点
        //起始点在屏幕底部中间位置
        //控制点1取下半屏幕的随机点
        //控制点2取上半屏幕的随机点
        //起始点在屏幕顶部中间位置

        //所有动画计算的基准是图片的左上角
        PointF statPoint = new PointF(mWidth / 2 - mDrawableWidth / 2, mHeight - mDrawableHeight);
        PointF controlPoint1 = new PointF(mRandom.nextInt(mWidth) - mDrawableWidth, mRandom.nextInt(mHeight / 2) + mHeight / 2);
        PointF controlPoint2 = new PointF(mRandom.nextInt(mWidth) - mDrawableWidth, mRandom.nextInt(mHeight / 2));
        PointF endPoint = new PointF(mRandom.nextInt(mWidth - mDrawableWidth), 0);
        BezierTypeEvaluator evaluator = new BezierTypeEvaluator(controlPoint1, controlPoint2);
        ValueAnimator bezierAnimator = ObjectAnimator.ofObject(evaluator, statPoint, endPoint);
        // 4.1加一些随机的插值器
        bezierAnimator.setInterpolator(mInterpolator[mRandom.nextInt(mInterpolator.length)]);
        bezierAnimator.addUpdateListener(animation -> {
            PointF pointF = (PointF) animation.getAnimatedValue();
            likeIv.setX(pointF.x);
            likeIv.setY(pointF.y);
            // 3.添加透明度变化动画,飘到顶部时颜色最淡
            float t = animation.getAnimatedFraction();
            likeIv.setAlpha(1 - t);
        });
        bezierAnimator.setDuration(3000);
        return bezierAnimator;
    }
}

2 自定义估值器
这块如果不熟悉 可以先看一下上一篇博客 https://blog.csdn.net/u011109881/article/details/112549820
后记的最后一条,那里的估值器比较简单 是线性的,容易理解。

class BezierTypeEvaluator implements TypeEvaluator<PointF> {
    private final PointF mControlPoint1;
    private final PointF mControlPoint2;


    public BezierTypeEvaluator(PointF controlPoint1, PointF controlPoint2) {
        mControlPoint1 = controlPoint1;
        mControlPoint2 = controlPoint2;
    }

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation:
     * 注意下面的这段描述 计算结果result = x0 + fraction * (x1 - x0)
     * 其中x0代表起始值 x1代表终点值 t代表比值
     * <code>result = x0 + fraction * (x1 - x0)</code>,
     * where <code>x0</code> is <code>startPoint</code>, <code>x1</code> is <code>endPoint</code>,
     * and <code>fraction</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     *                   从起点到终点的比值
     * @param startPoint The start value.
     *                   起始点的值
     * @param endPoint   The end value.
     *                   终点的值
     * @return A linear interpolation between the start and end values, given the
     * <code>fraction</code> parameter.
     * 此函数的意义是计算某个比值fraction时 中间点的状态(位置) 其中fraction的区间为[0,1] 比如fraction也可以理解为百分比,为0.5表示一半的意思
     * <p>
     * <p>
     * 以上的解释为通用解释
     * 对于求三阶贝塞尔曲线的本方法而言
     * 该方法用于计算在特定 起点,终点,控制点1,控制点2时,fraction从0变化到1,返回的PointF连接起来将构成三阶贝塞尔曲线
     * 至于计算方法 直接套用百度百科的三阶贝塞尔曲线的公式即可 fraction即时百科中的t
     */
    @Override
    public PointF evaluate(float fraction, PointF startPoint, PointF endPoint) {
        PointF pointOnBezier = new PointF();
        pointOnBezier.x = (float) (Math.pow((1 - fraction), 3) * startPoint.x + 3 * mControlPoint1.x * fraction * Math.pow(1 - fraction, 2) + 3 * mControlPoint2.x * fraction * fraction * (1 - fraction) + endPoint.x * Math.pow(fraction, 3));
        pointOnBezier.y = (float) (Math.pow((1 - fraction), 3) * startPoint.y + 3 * mControlPoint1.y * fraction * Math.pow(1 - fraction, 2) + 3 * mControlPoint2.y * fraction * fraction * (1 - fraction) + endPoint.y * Math.pow(fraction, 3));
        return pointOnBezier;
    }
}

3 Activity

public class MainActivity extends AppCompatActivity {
    private LikesLayout mLikesLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLikesLayout = findViewById(R.id.likeLayout);
    }

    public void like(View view) {
        mLikesLayout.addLikes();
    }
}

4 布局

<?xml version="1.0" encoding="utf-8"?>
<com.example.likesview.LikesLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/likeLayout"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/like"
        android:onClick="like"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点赞!" />

</com.example.likesview.LikesLayout>

5 星星的布局(红色为例 是使用Android studio创建的)

<vector android:height="48dp"
    android:tint="#FF0000"
    android:viewportHeight="24"
    android:viewportWidth="24"
    android:width="48dp"
    xmlns:android="http://schemas.android.com/apk/res/android">
    <path
        android:fillColor="@android:color/white"
        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z" />
</vector>

后记:

视频中的随机数有问题,图片我们只看到两种颜色 视频有点问题 比如 mRandom.nextInt(5);取值可能是是[0,5)的整数,视频里面写道 mRandom.nextInt(mImageRes.length - 1) 其实-1是不必要的,正因为视频-1了 所以还有一种颜色没有出现

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值