贝塞尔曲线 - 花束直播点赞效果

1. 效果


花束直播点赞效果

先说一下这种效果都用到了哪些东西:
1.自定义View的一些基础;
2.随机数的使用;
3.插补器的使用;
4.属性动画的一些高级用法
5.贝塞尔曲线应用到属性动画

2.分析和实现


2.1效果实现:

可以基本分为两个部分,具体看实现
  1.点击START ANIM按钮的时候,底部出现一个ImageView它的drawable是随机的,并伴着缩放和透明度的变化;
  2.等第一步的动画执行完后开始向上移动,移动的轨迹是一个曲线,我们要用到贝塞尔曲线公式去不断的改变图片的位置。

2.2分步实现:

1.自定义View继承RelativeLayout,初始化一些基本的参数:

public class LoveLayout extends RelativeLayout {
    private Drawable mRed, mYellow, mBlue;
    private Drawable[] mDrawables;
    private Interpolator[] mInterpolators;
    private int mDrawableHeight, mDrawableWidth;
    private int mWidth, mHeight;
    private LayoutParams params;
    private Random mRandom = new Random();

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

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

    public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        initDrawable();
        initInterpolator();
        // 初始化params
        params = new LayoutParams(mDrawableWidth, mDrawableHeight);
        // 父容器水平居中
        params.addRule(CENTER_HORIZONTAL, TRUE);
        // 父容器的底部
        params.addRule(ALIGN_PARENT_BOTTOM, TRUE);
    }

    /**
     * 初始化几种插补器
     */
    private void initInterpolator() {
        mInterpolators = new Interpolator[4];
        mInterpolators[0] = new LinearInterpolator();// 线性
        mInterpolators[1] = new AccelerateDecelerateInterpolator();// 先加速后减速
        mInterpolators[2] = new AccelerateInterpolator();// 加速
        mInterpolators[3] = new DecelerateInterpolator();// 减速
    }

    private void initDrawable() {
        mRed = getResources().getDrawable(R.drawable.pl_red);
        mYellow = getResources().getDrawable(R.drawable.pl_yellow);
        mBlue = getResources().getDrawable(R.drawable.pl_blue);

        mDrawables = new Drawable[3];
        mDrawables[0] = mRed;
        mDrawables[1] = mYellow;
        mDrawables[2] = mBlue;

        // 得到图片的实际宽高
        mDrawableWidth = mRed.getIntrinsicWidth();
        mDrawableHeight = mRed.getIntrinsicHeight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = getMeasuredWidth();
        mHeight = getMeasuredHeight();
    }
}

2.当点击START NIMA的时候不断的往容器里面添加随机的ImageView,然后开始执行透明度的缩放的动画,这里用属性动画ObjectAnimator.ofFloat();

    /**
    * 添加点赞图片
    **/
    public void addLove() {
        final ImageView loveIv = new ImageView(getContext());
        loveIv.setImageDrawable(mDrawables[mRandom.nextInt(mDrawables.length)]);
        loveIv.setLayoutParams(params);
        addView(loveIv);

        // 最终的属性动画集合
        AnimatorSet finalSet = getAnimatorSet(loveIv);
        finalSet.start();
    }

    /**
     * 构造三个属性动画
     */
    private AnimatorSet getAnimatorSet(ImageView loveIv) {
        // 1.alpha动画
        ObjectAnimator alpha = ObjectAnimator
                .ofFloat(loveIv, "alpha", 0.3f, 1f);

        // 2.缩放动画
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(loveIv, "scaleX", 0.2f,
                1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(loveIv, "scaleY", 0.2f,
                1f);
        // 刚刚进入动画集合
        AnimatorSet enter = new AnimatorSet();
        enter.setDuration(500);
        enter.playTogether(alpha, scaleX, scaleY);
        enter.setTarget(loveIv);
        return enter;
    }

这里我们可以试着看看效果,是不是可以伴随着动画添加到容器底部了。

3.利用三次方贝塞尔曲线,来不断的修改ImageView的位置,这里我们要看看这个方程,我们要继承这个估值器TypeEvaluator,至于贝塞尔是大学高数中的东西了,可以简单分析一下:
  

  
贝塞尔曲线方程

整个曲线我们可以简单的理解为这个S路曲线,S也会有四个点:
1.P0最下面的这个起点,也就是我们刚刚添加进来最下方居中的这个点 ((mWidth - mDrawableWidth) / 2, mHeight- mDrawableHeight)
2.P1是下半部分抛物线的顶点,这里是随机;
3.P2是上半部分抛物线的顶点,这里也是随机;
4.P3是最上面位置的终点(mRandom.nextInt(mWidth), 0),也就是最上面的这个点;
5.t的范围是[0,1],我们确定这四个点之后就开始套公式了:

// 贝塞尔估值器
public class BezierEvaluator implements TypeEvaluator<PointF> {
    private PointF point1, point2;

    public BezierEvaluator(PointF pointF1, PointF pointF2) {
        this.point1 = pointF1;
        this.point2 = pointF2;
    }

    @Override
    public PointF evaluate(float t, PointF point0, PointF point3) {
        // t百分比, 0~1
        PointF point = new PointF();
        point.x = point0.x * (1 - t) * (1 - t) * (1 - t) //
                + 3 * point1.x * t * (1 - t) * (1 - t)//
                + 3 * point2.x * t * t * (1 - t)//
                + point3.x * t * t * t;//
        
        point.y = point0.y * (1 - t) * (1 - t) * (1 - t) //
                + 3 * point1.y * t * (1 - t) * (1 - t)//
                + 3 * point2.y * t * t * (1 - t)//
                + point3.y * t * t * t;//
        // 套用上面的公式把点返回
        return point;
    }

}

4.利用属性动画这个方法ValueAnimator.ofObject(),然后添加监听不断的改变ImageView的x,y坐标值,当然还有一些后续工作如动画执行完后将ImageView移除释放资源等等。

    /**
     * 贝塞尔曲线动画(核心,不断的修改ImageView的坐标ponintF(x,y) )
     */
    private ValueAnimator getBezierValueAnimator(final ImageView loveIv) {
        // 曲线的两个顶点
        PointF pointF2 = getPonitF(2);
        PointF pointF1 = getPonitF(1);
        // 起点位置
        PointF pointF0 = new PointF((mWidth - mDrawableWidth) / 2, mHeight
                - mDrawableHeight);
        // 结束的位置
        PointF pointF3 = new PointF(mRandom.nextInt(mWidth), 0);
        // 估值器Evaluator,来控制view的行驶路径(不断的修改point.x,point.y)
        BezierEvaluator evaluator = new BezierEvaluator(pointF1, pointF2);
        // 属性动画不仅仅改变View的属性,还可以改变自定义的属性
        ValueAnimator animator = ValueAnimator.ofObject(evaluator, pointF0,
                pointF3);
        animator.addUpdateListener(new AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 不断改变ImageView的x,y的值
                PointF pointF = (PointF) animation.getAnimatedValue();
                loveIv.setX(pointF.x);
                loveIv.setY(pointF.y);
                loveIv.setAlpha(1 - animation.getAnimatedFraction() + 0.1f);// 得到百分比
            }
        });
        animator.setTarget(loveIv);
        animator.setDuration(3000);
        return animator;
    }

所有分享大纲:Android进阶之旅 - 自定义View篇

视频讲解地址:http://pan.baidu.com/s/1nvwqaA9

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容