直播中禮物動(dòng)畫暴擊 "無限疊加" 效果(一)

產(chǎn)品需求:
1、 有暴擊,暴擊的時(shí)長為5秒
2、 有數(shù)據(jù)需要無限往里面添加,主播需要實(shí)時(shí)看到送禮物的情況
3、 當(dāng)禮物被后面的數(shù)據(jù)頂上去時(shí),如果有暴擊,重新進(jìn)入,然后疊加
3、當(dāng)動(dòng)畫進(jìn)入后才顯示后面的數(shù)據(jù)(暫未實(shí)現(xiàn))

實(shí)現(xiàn)效果:
禮物暴擊效果

大致的效果圖如上,一般直播項(xiàng)目中使用的禮物框架都是類似的實(shí)現(xiàn)方式,不過有些像 *愛網(wǎng)的動(dòng)畫是有一個(gè)隊(duì)列的形式,而且沒有暴擊,當(dāng)用戶贈(zèng)送的禮物過多時(shí),會(huì)出現(xiàn)禮物不斷的出現(xiàn)在上面;還有像*魚直播App實(shí)現(xiàn)的暴擊和上面的類似,不過當(dāng)禮物推上去之后是不會(huì)重新暴擊的;我們的產(chǎn)品暴擊時(shí)間比較長,目前的實(shí)現(xiàn)方式參考了市面上多個(gè)APP的形式。

下面,我們對該需求進(jìn)行拆分:

一、對象創(chuàng)建

簡單的分析需求后,我們大致可以將該動(dòng)畫分為三個(gè)對象:

1、 禮物管理類,
    
2、 禮物布局類

3、 動(dòng)畫類
禮物管理類

該對象中,應(yīng)該保存所有的禮物布局對象,控制其添加、刪除;

禮物布局類

布局當(dāng)然是顯示當(dāng)前禮物的數(shù)據(jù):贈(zèng)送人的頭像、名字、接收人的名字、禮物的圖片、暴擊的數(shù)量等,禮物布局還有一個(gè)隱藏的功能是自身的暴擊時(shí)間和隱藏的時(shí)間控制;當(dāng)然,這兩個(gè)時(shí)間是一樣的,當(dāng)有暴擊的時(shí)候,取消隱藏的時(shí)間,然后暴擊結(jié)束后又開始隱藏時(shí)間的倒計(jì)時(shí)。

動(dòng)畫類

控制布局的顯示、隱藏、暴擊的動(dòng)畫

二、 禮物管理 GiftControl.java

2.1 添加動(dòng)態(tài)參數(shù)

首先,我們需要?jiǎng)?chuàng)建一個(gè)GiftControl,添加大致的框架方法,不需要設(shè)置為單例類,因?yàn)閱卫每赡軐?dǎo)致界面的內(nèi)存泄漏,所以我們直接設(shè)置構(gòu)造方法傳Context進(jìn)入即可:

public class GiftControl {
  public GiftControl(Context context) {
      mContext = context;
  }
}

然后,作為禮物布局的存放管理者,還需要有一個(gè)布局容器來添加,所以,還需要從頁面中傳入一個(gè)ViewGroup到里面,布局容器中還需要有一個(gè)禮物的數(shù)量,比如我上面最大的顯示數(shù)量為2,這些都可以動(dòng)態(tài)控制,所以增加一個(gè)方法設(shè)置這兩個(gè)參數(shù):

public class GiftControl {
  public GiftControl(Context context) {
      mContext = context;
  }

  /**
   * @param giftLayoutParent 存放禮物控件的父容器
   * @param giftMaxNum       禮物控件的數(shù)量
   * @return
   */
  public GiftControl setGiftLayout(LinearLayout giftLayoutParent, @NonNull int giftMaxNum) {
      if (giftMaxNum <= 0) {
          throw new IllegalArgumentException("GiftFrameLayout數(shù)量必須大于0");
      }
      if (giftLayoutParent.getChildCount() > 0) {
          //如果父容器沒有子孩子,就進(jìn)行添加
          return this;
      }
      mGiftLayoutParent = giftLayoutParent;
      mGiftLayoutMaxNums = giftMaxNum;
      LayoutTransition transition = new LayoutTransition();
      transition.setAnimator(LayoutTransition.CHANGE_APPEARING,
                transition.getAnimator(LayoutTransition.CHANGE_APPEARING));
      transition.setAnimator(LayoutTransition.APPEARING, 
                transition.getAnimator(LayoutTransition.APPEARING));
      transition.setAnimator(LayoutTransition.DISAPPEARING, 
                transition.getAnimator(LayoutTransition.CHANGE_APPEARING));
      transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, 
                transition.getAnimator(LayoutTransition.DISAPPEARING));
      mGiftLayoutParent.setLayoutTransition(transition);
      return this;
  }
}

上面,我們添加了兩個(gè)參數(shù)設(shè)置,還給布局添加了 LayoutTransition動(dòng)畫,關(guān)于LayoutTransition動(dòng)畫的使用,官網(wǎng)對其進(jìn)行了詳細(xì)的解釋使用:
https://developer.android.com/reference/android/animation/LayoutTransition

2.2 添加禮物數(shù)據(jù)、顯示禮物布局

添加禮物

public class GiftControl {
  public GiftControl(Context context) {
      mContext = context;
  }

  /**
   * @param giftLayoutParent 存放禮物控件的父容器
   * @param giftMaxNum       禮物控件的數(shù)量
   * @return
   */
  public GiftControl setGiftLayout(LinearLayout giftLayoutParent, @NonNull int giftMaxNum) {
      if (giftMaxNum <= 0) {
          throw new IllegalArgumentException("GiftFrameLayout數(shù)量必須大于0");
      }
      if (giftLayoutParent.getChildCount() > 0) {
          //如果父容器沒有子孩子,就進(jìn)行添加
          return this;
      }
      mGiftLayoutParent = giftLayoutParent;
      mGiftLayoutMaxNums = giftMaxNum;
      LayoutTransition transition = new LayoutTransition();
      transition.setAnimator(LayoutTransition.CHANGE_APPEARING,
                transition.getAnimator(LayoutTransition.CHANGE_APPEARING));
      transition.setAnimator(LayoutTransition.APPEARING, 
                transition.getAnimator(LayoutTransition.APPEARING));
      transition.setAnimator(LayoutTransition.DISAPPEARING, 
                transition.getAnimator(LayoutTransition.CHANGE_APPEARING));
      transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, 
                transition.getAnimator(LayoutTransition.DISAPPEARING));
      mGiftLayoutParent.setLayoutTransition(transition);
      return this;
  }
  
  /**
   * 添加禮物數(shù)據(jù)
   */
  public synchronized void loadGift(LiveGiftBean gift) {
      showGift(gift);
  }

    private synchronized void showGift(LiveGiftBean giftBean) {
        if (giftBean == null) {
            return;
        }
        LiveGiftLayout giftLayout;
        int childCount = mGiftLayoutParent.getChildCount();
        Log.d(TAG, "showGift: 禮物布局的個(gè)數(shù)" + childCount);
       
        //沒有超過最大的禮物布局?jǐn)?shù)量,可以繼續(xù)添加禮物布局
        giftLayout = new LiveGiftLayout(mContext);
        giftLayout.setIndex(0);
        //兩個(gè)參數(shù)分別是layout_width,layout_height
        RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mGiftLayoutParent.getLayoutParams();
        //這個(gè)就是添加其他屬性的,這個(gè)是在父元素的底部。
        lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        mGiftLayoutParent.addView(giftLayout);
        boolean hasGift = giftLayout.setGift(giftBean);
        if (hasGift) {
            giftLayout.startAnimation(customAnim);
            // 添加到view隊(duì)列中
            mLiveGiftLayouts.add(giftLayout);
        }
    }
}

添加數(shù)據(jù),直接調(diào)用顯示數(shù)據(jù)的方法,在里面,我們新建布局類,然后外層是一個(gè)RelativeLayout將其規(guī)則設(shè)置為底部,因?yàn)樯厦媸亲缘紫蛏蟿?dòng)畫;

好了,動(dòng)畫管理類,簡單的框架就是這樣,后面就只需要往方法上疊加邏輯就好了。

三、 動(dòng)畫布局類 LiveGiftLayout.java

布局類是一個(gè)自定義的組合View,布局是比較簡單的,根據(jù)創(chuàng)建自定義View的方式來即可:

public class LiveGiftLayout extends FrameLayout {
    private LayoutInflater mInflater;
    private Context mContext;

    RelativeLayout mGiftItemContent;
    ImageView mIvGift, mIvSenderHeader;
    TextView mTvSenderName, mTvSenderInfo;
    TextView mTvGiftNum;

    private View rootView;

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

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

    public LiveGiftLayout(Context context, AttributeSet attributeSet, @AttrRes int defStyleAttr) {
        super(context, attributeSet, defStyleAttr);

        mInflater = LayoutInflater.from(context);
        mContext = context;
        initView();
    }

    private void initView() {
        rootView = mInflater.inflate(R.layout.item_live_voice_gift, null);
        mGiftItemContent = rootView.findViewById(R.id.item_live_voice_gift_content);
        mIvGift = rootView.findViewById(R.id.item_live_voice_gift_iv_gift);
        mTvGiftNum = rootView.findViewById(R.id.item_live_voice_gift_tv_num);
        mIvSenderHeader = rootView.findViewById(R.id.item_live_voice_gift_iv_header);
        mTvSenderName = rootView.findViewById(R.id.item_live_voice_gift_tv_nickname);
        mTvSenderInfo = rootView.findViewById(R.id.item_live_voice_gift_tv_info);

        this.addView(rootView);
    }

    public boolean setGift(LiveGiftBean gift) {
        if (gift == null) {
            return false;
        }
        mGift = gift;

        if (mGift.isCurrentStart()) {
            mGiftCount = gift.getGiftCount() + mGift.getHitCombo();
        } else {
            mGiftCount = gift.getGiftCount();
        }
        if (!TextUtils.isEmpty(gift.getSendUserName())) {
            mTvSenderName.setText(gift.getSendUserName());
        }
        if (!TextUtils.isEmpty(gift.getGiftId())) {
            mTvSenderInfo.setText(gift.getGiftName());
        }
        // 設(shè)置頭像
        mIvSenderHeader.setImageDrawable(ContextCompat.getDrawable(mContext, gift.getSendUserPic()));
        // 設(shè)置名字
        String firstText = "送 ";
        String beforeColor = "#2d2d2d";
        String afterColor = "#ff3a72";

        //創(chuàng)建SpannableStringBuilder,并添加前面文案
        SpannableStringBuilder builder = new SpannableStringBuilder(firstText);
        //設(shè)置前面的字體顏色
        builder.setSpan(new ForegroundColorSpan(Color.parseColor(beforeColor)), 0, firstText.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
        //追加后面文案
        builder.append(gift.getRecevierUserName());
        //設(shè)置后面的字體顏色
        builder.setSpan(new ForegroundColorSpan(Color.parseColor(afterColor)), firstText.length(), builder.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
        mTvSenderInfo.setText(builder);

        // 設(shè)置禮物圖片
        mIvGift.setImageDrawable(ContextCompat.getDrawable(mContext, gift.getGiftPic()));

        mTvGiftNum.setText("x " + (mGiftCount));
        mCombo = mGiftCount;
        return true;
    }
}

上面,我們創(chuàng)建了一個(gè)自定義組合View,因?yàn)樵?code>GiftControl中顯示layout的時(shí)候,調(diào)用了setGift()方法,所以我們在里面創(chuàng)建這個(gè)方法,目的是設(shè)置里面控件數(shù)據(jù)。

到現(xiàn)在,我們可以添加數(shù)據(jù)到控制類中,然后顯示數(shù)據(jù)到ViewGroup中,圖片的話,自己腦補(bǔ)一下吧??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容