打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
直播送花特效



引言


这是作者第一个CSDN的文章,写的不好的地方,请大家多多提提意见哈。今天写的文章是关于动画的,前几天公司要求我写一个观看直播点击送花与主播收到花朵的效果。



主播接到花朵的效果 



观众送花效果


思路


好了看见了效果之后想必大家都应该知道肯定想到要用Animator(4.0增加的动画类),想必很多人都接触到了,但是刚接触Android的兄弟可能就没怎么使用这个。网上有很多文章对这个类的介绍,所以这里就不注重解释了。


主要的思路如下:

1.确定起点(主播是随机起点)与终点位置;

2.为其添加动画特效,从起点运动到终点,然后销毁View


代码


我就不拿以前的代码了,我一边敲一边说,首先创建个项目(不多说了哈),然后我们创建一个工具类FlowerAnimation.java。我们就对这个类疯狂撸吧。


固定起点->固定终点特效实现


    private static final String TAG = 'FlowerAnimation';

    //上下文

    private Context mContext;

    //生成的View添加在的ViewGroup

    private ViewGroup rootView;

    private ViewGroup.LayoutParams layoutParams;


    //资源文件

    private Drawable[] drawables;

    //插值器

    private Interpolator[] interpolators;


上面代码的注释很清楚了,大家一看就明白了    ,TGA这是为了测试打印log使用的(AS快捷键-logt+enter)。


    public FlowerAnimation(Context mContext, ViewGroup rootView) {

         this.mContext = mContext;

         this.rootView = rootView;

         init();

    }   


    private void init() {

        drawables = new Drawable[8];

        drawables[0] = mContext.getResources().getDrawable(R.mipmap.flower_01);

        drawables[1] = mContext.getResources().getDrawable(R.mipmap.flower_02);

        drawables[2] = mContext.getResources().getDrawable(R.mipmap.flower_03);

        drawables[3] = mContext.getResources().getDrawable(R.mipmap.flower_04);

        drawables[4] = mContext.getResources().getDrawable(R.mipmap.flower_05);

        drawables[5] = mContext.getResources().getDrawable(R.mipmap.flower_06);

        drawables[6] = mContext.getResources().getDrawable(R.mipmap.flower_07);

        drawables[7] = mContext.getResources().getDrawable(R.mipmap.flower_08);


        interpolators = new Interpolator[4];

        interpolators[0] = new LinearInterpolator();//线性

        interpolators[1] = new AccelerateInterpolator();//加速

        interpolators[2] = new DecelerateInterpolator();//减速

        interpolators[3] = new AccelerateDecelerateInterpolator();//先加速后减速


        layoutParams = new ViewGroup.LayoutParams(DensityUtil.dip2px(mContext, 50), DensityUtil.dip2px(mContext, 50));

    }


上面就是一些初始化的东西了,大家看看就好。


准备工作做好了,我们写最主要的方法,那就是生成动画了,上代码


/**

    /**

     * 开启动画

     *

     * @param view   执行动画的view

     * @param startP 起点 如果传null 默认从view位置开始

     * @param stopP  终点

     */

    public void startAnim(@NonNull final View view, @Nullable PointF startP, @NonNull PointF stopP) {

        if (startP == null) {

            startP = new PointF(view.getX(), view.getY());

        }

        //透明度变化

        ObjectAnimator animatorAlpha = ObjectAnimator.ofFloat(view, 'alpha', 0, 1);

        animatorAlpha.setDuration(200);

        //位移动画

        ObjectAnimator animatorX = ObjectAnimator.ofFloat(view, 'translationX', startP.x, stopP.x);

        animatorX.setDuration(1000);

        ObjectAnimator animatorY = ObjectAnimator.ofFloat(view, 'translationY', startP.y, stopP.y);

        animatorY.setDuration(1000);

        //生成动画集合

        AnimatorSet set = new AnimatorSet();

        //开启透明度动画然后执行位移动画

        set.play(animatorAlpha).before(animatorX).with(animatorY);

        //加入植入器

        set.setInterpolator(interpolators[rand.nextInt(interpolators.length)]);

        //添加动画监听事件 为了移除view 防止造成oom

        set.addListener(new AnimatorListenerAdapter() {

            @Override

            public void onAnimationEnd(Animator animation) {

                super.onAnimationEnd(animation);

                rootView.removeView(view);

            }

        });

        set.start();

    }


    /**

     * 开启动画

     *

     * @param view  执行动画的view

     * @param stopP 终点

     */

    public void startAnim(@NonNull final View view, @NonNull PointF stopP) {

        startAnim(view, null, stopP);

    }


    /**

     * 添加花朵

     *

     * @param startPoint

     */

    public void addFlower(@NonNull PointF startPoint, @NonNull PointF stopP) {

        ImageView flower = new ImageView(mContext);

        flower.setX(startPoint.x);

        flower.setY(startPoint.y);

        Drawable drawable = drawables[rand.nextInt(drawables.length)];

        flower.setBackground(drawable);

        rootView.addView(flower, layoutParams);

        startAnim(flower, startPoint, stopP);

    }


好了,我们看到,生成这个动画需要View(这是必然的)终点也是必然,起点就无所谓了。每一步的注释都写的很清楚,ObjectAnimator这个类功能很强大(4.0+)。下面开始写个界面来看看效果啦。界面代码我直接上代码 不讲解了!对了有关于view位置的问题大家直接去看别的文章吧,很多的哈,我这里贴个我认为不错的:


http://blog.csdn.net/jason0539/article/details/42743531


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    //终点坐标imageView

    private ImageView endFlowerIv;

    //开启动画按钮 也是起点坐标

    private Button startFlowerBt;

    private FlowerAnimation flowerAnimation;


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        initView();

    }


    private void initView() {

        endFlowerIv = (ImageView) findViewById(R.id.main_end_flower_iv);

        startFlowerBt = (Button) findViewById(R.id.main_start_flower_bt);

        startFlowerBt.setOnClickListener(this);

        flowerAnimation = new FlowerAnimation(this, (ViewGroup) findViewById(R.id.activity_main));

    }



    @Override

    public void onClick(View v) {

        flowerAnimation.addFlower(new PointF(v.getX(), v.getY()), new PointF(endFlowerIv.getX(), endFlowerIv.getY()));

    }


    android:id='@+id/activity_main'

    android:layout_width='match_parent'

    android:layout_height='match_parent'>


    <>

        android:id='@+id/main_end_flower_iv'

        android:layout_width='50dp'

        android:layout_height='50dp'

        android:layout_gravity='center_horizontal'

        android:background='@mipmap/flower_01' />



    <>

        android:id='@+id/main_start_flower_bt'

        android:layout_width='50dp'

        android:layout_height='50dp'

        android:layout_gravity='center_horizontal|bottom'

        android:background='@mipmap/flower_01' />


下面是效果图



随即起点->固定终点特效实现


这个问题想必大家在上面的基础上就完全可以写出来 直接把代码贴上来与效果


     /**

     * 添加花朵 随即生成起点(rootView范围)

     *

     * @param stopP 终点

     */

    public void addFlowerByScope(@NonNull PointF stopP) {

        float x = rand.nextFloat() * rootView.getWidth();

        float y = rand.nextFloat() * rootView.getHeight();

        addFlower(new PointF(x, y), stopP);

    }


     /**

     * 添加花朵 随即生成起点

     *

     * @param stopP  终点

     * @param scopeP 范围  随即生成的点将会按照此范围随即取值

     */

    public void addFlowerByScope(@NonNull PointF stopP, @NonNull PointF scopeP) {

        float x = rand.nextFloat() * scopeP.x;

        float y = rand.nextFloat() * scopeP.y;

        addFlower(new PointF(x, y), stopP);

    }


界面的点击事件换成对应的方法


@Override

    public void onClick(View v) {

        flowerAnimation.addFlowerByScope(new PointF(endFlowerIv.getX(), endFlowerIv.getY()));

    }




到此,你会发现已经完成主播接到花朵的效果(就是随即从各个地方出现花朵飞到花朵出)以上就是主播界面显示的效果了。代码比较简单,下面实现观众点击送花的效果。


思路呢?其实跟上面差不多,观众送花的效果类似固定点到固定点的效果(类似哈哈),为什么说类似呢?因为从图上可以看到,路径是不同的,很明显发现 观众送花的效果的路径是随即的(乱飘)。这里就引出来了ValueAnimator 这个东西你会发现他是ObjectAnimator的父类。


ValueAnimator 顾名思义哈 就是针对数值的动画,他能帮我完成什么呢?

比如我我想让一个数值从0-10 时间是10s,我们来写写看我们新建一个ValueAnimActivity.java来实现观众的界面,顺便在里面测试demo。


public class ValueAnimActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView countTv;

    private Button startBt;


    @Override

    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_value);

        initView();

    }


    private void initView() {

        countTv = (TextView) findViewById(R.id.value_count_tv);

        startBt = (Button) findViewById(R.id.value_start_bt);

        startBt.setOnClickListener(this);

    }


    @Override

    public void onClick(View v) {

        startValueAnim();

    }


    private void startValueAnim() {

        //从0-10 时间10s

        ValueAnimator countAnim = ValueAnimator.ofInt(0, 10)

                .setDuration(10000);

        countAnim.setInterpolator(new LinearInterpolator());

        countAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override

            public void onAnimationUpdate(ValueAnimator animation) {

                countTv.setText(animation.getAnimatedValue().toString());

            }

        });

        countAnim.start();

    }


}


    android:layout_width='match_parent'

    android:layout_height='match_parent'

    android:orientation='vertical'>


    <>

        android:id='@+id/value_count_tv'

        android:layout_width='wrap_content'

        android:layout_height='wrap_content'

        android:layout_gravity='center'

        android:textSize='30dp'  />


    <>

        android:id='@+id/value_start_bt'

        android:layout_width='wrap_content'

        android:layout_height='wrap_content'

        android:layout_gravity='center_horizontal|bottom'

        android:text='start' />


代码跟布局都很简单 我们简单写了个ValueAnimator的例子 直接看效果




那怎么实现我们的效果呢?这里就用到一个方法


public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)


他能让一个对象进行改变,那怎么改变呢?其实随便是什么?都要跟时间挂钩(重要),为什么跟时间挂钩,这还要解释么?onAnimationUpdate回调的方法你打印log你会发现他会调用N多次,10S回调了496次,可想而知我们对象改变也会跟时间有关系,那么我们看看TypeEvaluator(类型评估者)这是一个接口,我们来看看它要我们实现的方法

   

public T evaluate(float fraction, T startValue, T endValue);


 这个方法看到之后,后面2个我肯定知道是什么意思,动画的起始值,那第一个是什么?(英文翻译是分数)咱们说过肯定跟时间有关的,那么这个是不是就是时间呢?看了官方解释之后,这个意思就是当前完成动画的百分比。

    还不懂?那好我们还看看官方有没有默认给我们实现的类,一看,有很多,我们直接拿来一个用看看效果,上代码


private void startValueAnim1() {

        //从0-10 时间10s

        ValueAnimator countAnim = ValueAnimator.ofObject(new IntEvaluator() {

            @Override

            public Integer evaluate(float fraction, Integer startValue, Integer endValue) {

                Log.e(TAG, 'evaluate() called with: fraction = [' + fraction + '], startValue = [' + startValue + '], endValue = [' + endValue + ']');

                return super.evaluate(fraction, startValue, endValue);

            }

        }, 0, 10)

                .setDuration(10000);

        countAnim.setInterpolator(new LinearInterpolator());

        countAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override

            public void onAnimationUpdate(ValueAnimator animation) {

                countTv.setText(animation.getAnimatedValue().toString());

            }

        });

        countAnim.start();

    }




下面重点来了,如果要实现这种效果,那就要一个公式(贝塞尔曲线)这个当初我也是一头雾水啊,先不管直接套用公式就行了(先上一个图片) 




这个公式只要理解我们给出4个点,他就算出当前的点。这里有4个点,但是我们只有2个点,另外2个点是为了控制曲线的走向,我随即取就可以咯。好了,我们先不管点,先把TypeEvaluator写好


 /**

     * 自定义的估值器

     */

    public static class MyTypeEvaluator implements TypeEvaluator {

        private PointF pointF1, pointF2;


        public MyTypeEvaluator(PointF pointF1, PointF pointF2) {

            this.pointF1 = pointF1;

            this.pointF2 = pointF2;

        }


        @Override

        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {

            float timeLeft = 1.0f - fraction;

            PointF pointF = new PointF();//结果

            pointF.x = timeLeft * timeLeft * timeLeft * (startValue.x)

                    + 3 * timeLeft * timeLeft * fraction * (pointF1.x)

                    + 3 * timeLeft * fraction * fraction * (pointF2.x)

                    + fraction * fraction * fraction * (endValue.x);


            pointF.y = timeLeft * timeLeft * timeLeft * (startValue.y)

                    + 3 * timeLeft * timeLeft * fraction * (pointF1.y)

                    + 3 * timeLeft * fraction * fraction * (pointF2.y)

                    + fraction * fraction * fraction * (endValue.y);

            return pointF;

        }

    }


只是简单的套用公式,就不用多讲了直接复制就好,要不然能看的眼花。


下面放上取中间控制点的代码



注释也简单,我觉得很好弄懂,下面上主要代码







下面是最终效果啦



到这里差不多可以结束了。想必大家可能会问,如果动画还没执行完就退出了,那就内存泄漏了啊。所以我再来个方法。




好了,跑起来没有问题了。


源码地址:https://github.com/CFlingchen/CSDN1



  • 来自:CSDN-CF凌晨

  • http://blog.csdn.net/qq_27495349/article/details/53008975

  • 程序员大咖整理发布,转载请联系作者获得授权


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
一步一步android(7):关于界面控件的学习【gridview、button、imagebutton】
2.5.9 AlertDialog(对话框)详解 | 菜鸟教程
Animation-list,帧动画+属性动画,做出Flash般的效果
android如何创建一个透明的ImageButton
Android在导航栏添加音量加减按钮安卓源码案例
Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服