仿映客送礼特效

这里写链接内容仿映客送小礼物的特效,顺便复习一下属性动画,话不多说先看效果图。

需求分析

可以看到整个动画有几部分组成,那我们就把每个部分拆分出来各个击破。

1.要显示那些内容以及内容间的位置关系?

可以看到我们要显示用户头像,昵称,礼物图标以及数量。所以这里我选择用FrameLayout来作为根布局。

2.需要哪些动画以及动画的执行顺序?

a.首先是整体从左到右飞入并有一个回弹(translationX + OvershootInterpolator)

b.然后是礼物从左到右飞入而且是一个带减速效果的(translationX + DecelerateInterpolator)

c.礼物数量依次累加同时伴随着缩放(scale+repeat)

d.后面的粒子效果(帧动画)

e.整体向上平移并且逐渐消失(translationY + alpha)

3.送礼的区域有两块(A,B),如何分配?

因为用户送礼的数量不固定,所以动画持续的时间也不一定。但是我们希望这两块区域能得到充分的使用,即我们需要一个队列存放这些礼物实例,A和B谁空闲,就分配给谁处理。

4.以上所有内容是否使用原生的空间就能实现?

正如上面的分析,我们有时操作整体,有时操作局部。这时我们最好能自定义一个布局继承FrameLayout,其实也就是封装一层,这样我们就可以很好的控制整个布局。除此之外,还有我们注意到礼物数量是带描边的,貌似需要我们自定义实现了。

功能实现

需求分析完了,接下来我们说说功能的实现。

首先来打我们的整体布局。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <RelativeLayout
        android:id="@+id/animation_person_rl"
        android:layout_width="wrap_content"
        android:layout_height="39dp"
        android:layout_gravity="left"
        android:layout_marginTop="22dp"
        android:background="@drawable/bg_giftlayout">

        <ImageView
            android:id="@+id/gift_userheader_iv"
            android:layout_width="39dp"
            android:layout_height="39dp"
            android:layout_margin="3dp"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:src="@mipmap/ember" />

        <TextView
            android:id="@+id/gift_usernickname_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="6dp"
            android:layout_marginTop="4dp"
            android:layout_toRightOf="@id/gift_userheader_iv"
            android:text="库日天"
            android:textColor="#ffffff"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/gift_usersign_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@id/gift_usernickname_tv"
            android:layout_below="@id/gift_usernickname_tv"
            android:layout_marginTop="4dp"
            android:ellipsize="end"
            android:text="送一个超级无敌"
            android:textColor="#ffea79"
            android:textSize="11sp" />

        <ImageView
            android:id="@+id/animation_gift"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/gift_usersign_tv"
            android:background="@mipmap/diamond2x" />
    </RelativeLayout>

    <ImageView
        android:id="@+id/animation_light"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="120dp"
        android:src="@drawable/light_star_anim" />

    <com.example.work.animationdemo.StrokeTextView
        android:id="@+id/animation_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="185dp"
        android:layout_marginTop="12dp"
        android:text="x 1"
        android:textColor="#0076ff"
        android:textSize="24sp"
        app:innnerColor="#ffffff"
        app:outerColor="#0076ff" />

</FrameLayout>

这里比较简单不多说了,重点看下StrokeTextView,带描边的textview,其实就是重写了ondraw方法先绘制外层,在绘制内层。

 @Override
    protected void onDraw(Canvas canvas) {
        if (m_bDrawSideLine) {
            // 描外层
            setTextColorUseReflection(mOuterColor);
            m_TextPaint.setStrokeWidth(5);
            m_TextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            super.onDraw(canvas);

            // 描内层,恢复原先的画笔
            setTextColorUseReflection(mInnerColor);
            m_TextPaint.setStrokeWidth(0);
            m_TextPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        }
        super.onDraw(canvas);
    }
    /**
     * 使用反射的方法进行字体颜色的设置
     * @param color
     */
    private void setTextColorUseReflection(int color) {
        Field textColorField;
        try {
            textColorField = TextView.class.getDeclaredField("mCurTextColor");
            textColorField.setAccessible(true);
            textColorField.set(this, color);
            textColorField.setAccessible(false);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        m_TextPaint.setColor(color);
    }

定义礼物的实体类


public class GiftSendModel {

    private int giftCount;
    private String userAvatarRes;
    private String nickname;
    private String sig;
    private int giftRes;
    private String gift_id;
    private int star;

    public GiftSendModel(int giftCount) {
        this.giftCount = giftCount;
    }

    public int getGiftCount() {
        return giftCount;
    }

    public void setGiftCount(int giftCount) {
        this.giftCount = giftCount;
    }
    ......

封装整体布局

public class GiftFrameLayout extends FrameLayout {

    private LayoutInflater mInflater;

    RelativeLayout anim_rl;
    ImageView anim_gift, anim_light, anim_header;
    TextView anim_nickname, anim_sign;
    StrokeTextView anim_num;

    /**
     * 礼物数量的起始值
     */
    int starNum = 1;
    int repeatCount = 0;
    private boolean isShowing = false;

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

    public GiftFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mInflater = LayoutInflater.from(context);
        initView();
    }

    private void initView() {
        View view = mInflater.inflate(R.layout.animation, this, false);
        anim_rl = (RelativeLayout) view.findViewById(R.id.animation_person_rl);
        anim_gift = (ImageView) view.findViewById(R.id.animation_gift);
        anim_light = (ImageView) view.findViewById(R.id.animation_light);
        anim_num = (StrokeTextView) view.findViewById(R.id.animation_num);
        anim_header = (ImageView) view.findViewById(R.id.gift_userheader_iv);
        anim_nickname = (TextView) view.findViewById(R.id.gift_usernickname_tv);
        anim_sign = (TextView) view.findViewById(R.id.gift_usersign_tv);
        this.addView(view);
    }

    public void hideView() {
        anim_gift.setVisibility(INVISIBLE);
        anim_light.setVisibility(INVISIBLE);
        anim_num.setVisibility(INVISIBLE);
    }

    public void setModel(GiftSendModel model){
        if (0!=model.getGiftCount()) {
            this.repeatCount = model.getGiftCount();
        }
        if (!TextUtils.isEmpty(model.getNickname())) {
            anim_nickname.setText(model.getNickname());
        }
        if (!TextUtils.isEmpty(model.getSig())) {
            anim_sign.setText(model.getSig());
        }
    }

    public boolean isShowing(){
        return isShowing;
    }
     public AnimatorSet startAnimation( final int repeatCount) {
        hideView();
        //布局飞入
        ObjectAnimator flyFromLtoR = GiftAnimationUtil.createFlyFromLtoR(anim_rl, -getWidth(), 0, 400,new OvershootInterpolator());
        flyFromLtoR.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                GiftFrameLayout.this.setVisibility(View.VISIBLE);
                GiftFrameLayout.this.setAlpha(1f);
                isShowing = true;
                anim_num.setText("x " + 1);
                Log.i("TAG", "flyFromLtoR A start");
            }
        });
        //礼物飞入
        ObjectAnimator flyFromLtoR2 = GiftAnimationUtil.createFlyFromLtoR(anim_gift, -getWidth(), 0, 400,new DecelerateInterpolator());
        flyFromLtoR2.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                anim_gift.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                    GiftAnimationUtil.startAnimationDrawable(anim_light);
                     anim_num.setVisibility(View.VISIBLE);
            }
        });
        //数量增加
        ObjectAnimator scaleGiftNum = GiftAnimationUtil.scaleGiftNum(anim_num, repeatCount);
        scaleGiftNum.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationRepeat(Animator animation) {
                anim_num.setText("x " + (++starNum));
            }
        });
        //向上渐变消失
        ObjectAnimator fadeAnimator = GiftAnimationUtil.createFadeAnimator(GiftFrameLayout.this, 0, -100, 300, 400);
        fadeAnimator.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                GiftFrameLayout.this.setVisibility(View.INVISIBLE);
            }
        });
        // 复原
        ObjectAnimator fadeAnimator2 = GiftAnimationUtil.createFadeAnimator(GiftFrameLayout.this, 100, 0, 20, 0);

        AnimatorSet animatorSet = GiftAnimationUtil.startAnimation(flyFromLtoR, flyFromLtoR2, scaleGiftNum, fadeAnimator, fadeAnimator2);
        animatorSet.addListener(new AnimatorListenerAdapter() {

            @Override
            public void onAnimationEnd(Animator animation) {
                starNum = 1;
                isShowing = false;
            }

        });
        return animatorSet;

我们将所有的动画方法都写到了GiftAnimationUtil中便于管理

public class GiftAnimationUtil {

    /**
     * @param target
     * @param star         动画起始坐标
     * @param end          动画终止坐标
     * @param duration     持续时间
     * @return
     * 创建一个从左到右的飞入动画
     * 礼物飞入动画
     */
    public  static ObjectAnimator createFlyFromLtoR(final View target, float star, float end, int duration, TimeInterpolator interpolator) {
        //1.个人信息先飞出来
        ObjectAnimator anim1 = ObjectAnimator.ofFloat(target, "translationX",
                star, end);
        anim1.setInterpolator(interpolator);
        anim1.setDuration(duration);
        return  anim1;
    }

    /**
     * @param target
     * @return
     * 播放帧动画
     */
    public static AnimationDrawable startAnimationDrawable(ImageView target){
        AnimationDrawable animationDrawable = (AnimationDrawable) target.getDrawable();
        if(animationDrawable!=null) {
            target.setVisibility(View.VISIBLE);
            animationDrawable.start();
        }
        return animationDrawable;
    }

    /**
     * @param target
     * @param drawable
     * 设置帧动画
     */
    public static void  setAnimationDrawable(ImageView target, AnimationDrawable drawable){

        target.setBackground(drawable);
    }

    /**
     * @param target
     * @param num
     * @return
     * 送礼数字变化
     */
    public static ObjectAnimator scaleGiftNum(final TextView target , int num){
        PropertyValuesHolder anim4 = PropertyValuesHolder.ofFloat("scaleX",
                1.7f, 0.8f,1f);
        PropertyValuesHolder anim5 = PropertyValuesHolder.ofFloat("scaleY",
                1.7f, 0.8f,1f);
        PropertyValuesHolder anim6 = PropertyValuesHolder.ofFloat("alpha",
                1.0f, 0f,1f);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, anim4, anim5, anim6).setDuration(480);
        animator.setRepeatCount(num);
        return animator;

    }

    /**
     * @param target
     * @param star
     * @param end
     * @param duration
     * @param startDelay
     * @return
     * 向上飞 淡出
     */
    public static ObjectAnimator createFadeAnimator(final View target, float star, float end, int duration, int startDelay){

        PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", star,end);
        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f,0f);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, translationY, alpha);
        animator.setStartDelay(startDelay);
        animator.setDuration(duration);
        return animator;
    }

    /**
     * @param animators
     * @return
     * 按顺序播放动画
     */
    public static AnimatorSet startAnimation(ObjectAnimator animator1, ObjectAnimator animator2, ObjectAnimator animator3, ObjectAnimator animator4, ObjectAnimator animator5){
        AnimatorSet animSet = new AnimatorSet();
//        animSet.playSequentially(animators);
        animSet.play(animator1).before(animator2);
        animSet.play(animator3).after(animator2);
        animSet.play(animator4).after(animator3);
        animSet.play(animator5).after(animator4);
        animSet.start();
        return animSet;
    }

}

所有的动画效果均是用属性动画完成,其中不仅有单个的动画,还有组合动画。属性动画用起来方面而且功能十分强大!

最后看下MainActivity中的实现

public class MainActivity extends AppCompatActivity {

    private GiftFrameLayout giftFrameLayout1;
    private GiftFrameLayout giftFrameLayout2;

    List<GiftSendModel> giftSendModelList = new ArrayList<GiftSendModel>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         giftFrameLayout1 = (GiftFrameLayout) findViewById(R.id.gift_layout1);
         giftFrameLayout2 = (GiftFrameLayout) findViewById(R.id.gift_layout2);

        findViewById(R.id.action).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                starGiftAnimation(createGiftSendModel());
            }
        });

    }

    private GiftSendModel createGiftSendModel(){
            return new GiftSendModel((int)(Math.random()*10));
    }

    private void starGiftAnimation(GiftSendModel model){
        if (!giftFrameLayout1.isShowing()) {
            sendGiftAnimation(giftFrameLayout1,model);
        }else if(!giftFrameLayout2.isShowing()){
            sendGiftAnimation(giftFrameLayout2,model);
        }else{
            giftSendModelList.add(model);
        }
    }

    private void sendGiftAnimation(final GiftFrameLayout view, GiftSendModel model){
        view.setModel(model);
        AnimatorSet animatorSet = view.startAnimation(model.getGiftCount());
        animatorSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                synchronized (giftSendModelList) {
                    if (giftSendModelList.size() > 0) {
                        view.startAnimation(giftSendModelList.get(giftSendModelList.size() - 1).getGiftCount());
                        giftSendModelList.remove(giftSendModelList.size() - 1);
                    }
                }
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

其中关于缓存区的策略大家可以根据实际需求进行定制。

Demo地址

时间: 2024-10-26 08:40:06

仿映客送礼特效的相关文章

做一款仿映客的直播App?看我就够了

来源:JIAAIR 链接:http://www.jianshu.com/p/5b1341e97757 一.直播现状简介   1.技术实现层面: 技术相对都比较成熟,设备也都支持硬编码.IOS还提供现成的 Video ToolBox框架,可以对摄像头和流媒体数据结构进行处理,但Video ToolBox框架只兼容8.0以上版本,8.0以下就需要用x264的库软编了. github上有现成的开源实现,推流.美颜.水印.弹幕.点赞动画.滤镜.播放都有.技术其实不是很难,而且现在很多云厂商都提供SDK,

做一款仿映客的直播App

投稿文章,作者:JIAAIR(GitHub) 一.直播现状简介 1.技术实现层面 技术相对都比较成熟,设备也都支持硬编码.iOS还提供现成的Video ToolBox框架,可以对摄像头和流媒体数据结构进行处理,但Video ToolBox框架只兼容8.0以上版本,8.0以下就需要用x264的库软编了. github 上有现成的开源实现,推流.美颜.水印.弹幕.点赞动画.滤镜.播放都有.技术其实不是很难,而且现在很多云厂商都提供SDK,七牛云.金山云.乐视云. 腾讯云.百度云.斗鱼直播伴侣推流端,

仿映客客户端、TableView多种动画效果、边缘返回手势等源码

iOS精选源码 相册多选框架 zhPopupController 简单快捷弹出自定义视图 tableView实现多种动画效果 自定义动画的PageControl 展示tableView和collectionView的结构,框架CHD_ListView_Structure RxSwift+Moya+ObjectMapper优雅的网络请求级模型转换 多种指示器文字效果源码SGPagingView react-native低仿映客直播客户端 支持iOS/Android ScreenEdgePanGes

Android 仿映客直播间给主播发送礼物(实现连击效果)

效果图 类库的介绍 org.dync.giftlibrary.widget GiftAnimationUtil.java 动画类GiftControl.java 给外部调用的类(核心)GiftFrameLayout.java 礼物布局类GiftModel.java 给礼物布局填充数据类以上是礼物动画一(推荐使用礼物动画一,在demo中的Gift1Activity.java使用) LeftGiftControl.java 给外部调用的类(核心)LeftGiftsItemLayout.java 礼物

iOS-实现映客首页TabBar和滑动隐藏NavBar和TabBar

之前在做直播的时候,参照了映客App,发现其首页的效果还挺不错,在网上找了一下相关仿映客App代码和博客,大部分都是说如何播放直播流和推流,对于UI这块甚少,所以我自己花了点时间研究了一下映客的首页UI效果. 转载自 SUN'S BLOG - 专注互联网知识,分享互联网精神! . 映客首页主要分两部分,一部分是实现没有文字而且中间按钮突出的TabBar,另一部分是显示滑动ScrollView隐藏和显示NavBar和TabBar.我们来慢慢看. 一.TabBar实现 首先,我们看下实现后的效果.

CSS3仿LOGO图片阴影特效

又一个CSS3阴影效果,个人感觉CSS3的阴影比传统的CSS滤镜实现的阴影更生动,阴影可以是无规则的,而且阴影的大小也是随时变化的,本例子就是用CSs3写成一个Logo,然后再加以阴影特效,重点不是展示阴影效果有多漂亮,而是如何去实现这种CSS3效果. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transit

花椒映客感觉要开打了,又是因为抄袭的事儿

里约奥运会还在进行,各国的运动健将还在实力拼杀,而奥运会之外,在目前最火爆的移动视频直播行业,行业双寡头花椒和映客似乎也要展开一场拼杀了. 今日下午,花椒直播在自己的官方微博向映客隔空喊话,以打趣的方式发难,"知道你喜欢我们的产品,也不用这么明目张胆的表白呀,七夕已经过完了~"调侃的语气中暗藏着比严厉指责更强的力度,网友们见势也已经准备好板凳,期待看一场火药味浓的精彩好戏,结果让人失望的是,映客好像并没有正面的回应. 笔者出于好奇,仔细了解了下其中经过,原来事件的起因是映客最近在做商业

映客直播陷入“黑屏门”,直播圈虚火烧身?

王思聪手撕斗鱼和网红而引爆的直播平台数据造假的热浪还没退去,当下风头最劲,号称在最近6个月内,用了10倍于其他对手的力量和资本(资本还能算.力量嘛--),一口气拿下一亿用户,被某媒体冠以直播之王称号的映客被爆出"某用户测试黑屏直播3个小时,竟然有21个观众不离不弃." "黑屏门"只是又一个娱乐事件吗?显然不是,这不过是又一次将直播平台习惯性数据造假综合征,再一次用夸张和搞笑的方式,揭开给大众欣赏.而映客这种烧直播的虚火的生存方式,也未免虚火太旺以致引火烧身了. 没什

YY、映客、陌陌、KK、斗鱼五大直播阵营即将形成?

随着papi酱等为代表的网红崛起,网络直播忽然之间变得异常火爆起来,四个关键词恰当地形容了这个市场的火爆程度. 四个关键词 “全民参与”,网络直播让每个人都做主播逐渐成为一种现实,人人心中都有一个明星梦,越来越多的男女主播正在涌现出来,很多电视电台节目主持人也都在开始纷纷开通自己的直播频道. “明星大腕”,我们所熟知的当红明星周杰伦.贾乃亮.范冰冰.Angelababy.林更新.陈赫.周笔畅.李玟.刘涛等都在开始尝试直播,就连小米雷军也玩起了直播. “资本追逐”,此前宋城演艺26亿元收购秀场平台