產(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ǔ)一下吧??