安卓平台自定义弹幕库

86 阅读3分钟

介绍

此文章仅为自我做笔记用,各位大佬请移步😂!

此自定义弹幕库是很久之前在做直播时开发的需求。因其功能强大代码简洁。所以一直很喜欢这个功能。多年之后提及相关的东西仍觉很不错。所以在此记录分享一下。

代码

RoomDanmakuView.java

package com.wobo.live.room.danmaku;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

import com.wobo.live.room.danmakulib.DanmakuView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhou on 2016/5/31.
 */
public class RoomDanmakuView extends DanmakuView {
    public List<DanmakuBean> mDanmakuList  = new ArrayList<>();
    public RoomDanmakuView(Context context) {
        super(context);
    }

    public RoomDanmakuView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public List getDanmakuCacheList() {
        return mDanmakuList;
    }

    @Override
    public View getChildView(int pos) {
        return new DanmakuChildView(getContext(),mDanmakuList.get(pos));
    }
}

DanmakuChildView.java

package com.wobo.live.room.danmaku;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.wobo.live.app.utils.WboImageUrlUtils;
import com.wobo.live.expressing.SplitEmojiContentUtils;
import com.xiu8.android.activity.R;

/**
 * Created by zhou on 2016/5/31.
 */
public class DanmakuChildView extends LinearLayout{
    private DanmakuBean mDanmaku;
    public DanmakuChildView(Context context) {
        super(context);
        initView();
    }

    public DanmakuChildView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public DanmakuChildView(Context context,DanmakuBean danmaku) {
        super(context);
        mDanmaku = danmaku;
        initView();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.UNSPECIFIED), heightMeasureSpec);
    }

    private void initView() {
        View v = LayoutInflater.from(getContext()).inflate(R.layout.view_danmaku_child, this, true);
        ImageView nobleIcon = (ImageView)v.findViewById(R.id.nobleIcon);
        TextView danmakuMsg = (TextView)v.findViewById(R.id.danmaku_msg);
        SplitEmojiContentUtils.setEmoji(getContext(),danmakuMsg,mDanmaku.getNickName()+":"+mDanmaku.msg);
        if(mDanmaku.getUser().getNobleLevel()>0){
            nobleIcon.setVisibility(View.VISIBLE);
            nobleIcon.setImageResource(WboImageUrlUtils.getImage("noble_",mDanmaku.getUser().getNobleLevel()+""));
        }else{
            nobleIcon.setVisibility(View.GONE);
        }
        if(mDanmaku.getIsNoble()==1){
            v.setBackgroundResource(WboImageUrlUtils.getImage("noble_danmu_bg_",mDanmaku.getUser().getNobleLevel()+""));
        }else{
            v.setBackgroundResource(R.drawable.noble_danmu_bg_1);
        }
    }

}

DanmakuBean.java

package com.wobo.live.room.danmaku;

import com.wobo.live.app.WboBean;
import com.wobo.live.room.userbean.UserRoomInfo;

/**
 * Created by zhou on 2016/5/31.
 */
public class DanmakuBean extends WboBean{
    private long userId;
    private int level;
    private String avatar;
    public String msg;
    private String nickName;
    private UserRoomInfo user;
    private int playFlag;
    private int isNoble;//是否是贵族弹幕 1是 0不是

    public int getIsNoble() {
//        return new Random().nextInt(2);
        return isNoble;
    }

    public int getPlayFlag() {
        return playFlag;
    }

    public void setPlayFlag(int playFlag) {
        this.playFlag = playFlag;
    }

    public long getUserId() {
        return userId;
    }

    public void setUserId(long userId) {
        this.userId = userId;
    }

    public int getUserlevel() {
        return user.getUserLevel();
    }

    public String getAvatar() {
        return avatar;
    }

    public void setAvatar(String avater) {
        this.avatar = avater;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public DanmakuBean(long userId, int consumeLevel, String avatar, String msg, String nickName) {
        this.userId = userId;
        this.level = consumeLevel;
        this.avatar = avatar;
        this.msg = msg;
        this.nickName = nickName;
    }

    public UserRoomInfo getUser() {
        return user;
    }

    public void setUser(UserRoomInfo user) {
        this.user = user;
    }

    public int getLevel()
    {
        return level;
    }
}

DanmakuContainer.java

package com.wobo.live.room.danmakulib;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;

import com.android.frame.utils.VLDensityUtils;
import com.xiu8.android.activity.R;

import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by zhou on 2016/5/31.
 */
public class DanmakuContainer extends RelativeLayout{

    private long tempTime = 0;
    private int ALLTIME = 5000;
    private final int SPACE_TIEM = 1200;
    private int safeTime = 0;
    private IShowNextDanmakuListener showNext;
    public DanmakuContainer(Context context) {
        super(context);
        initView();
    }

    public DanmakuContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }


    private void initView() {
        View v = LayoutInflater.from(getContext()).inflate(R.layout.view_danmaku_container, this, true);
    }

    public boolean isCanShow(View danmakuChildView){
        if(danmakuChildView==null){//如果送过来的view为空则证明外面有处理不了的view类型,所以直接返回true,让上层移除当前bean
            return true;
        }
        int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        danmakuChildView.measure(w, h);
        int measuredWidth = danmakuChildView.getMeasuredWidth();
//        System.out.println("(ALLTIME-(System.currentTimeMillis()-tempTime)+SPACE_TIEM="+(ALLTIME-(System.currentTimeMillis()-tempTime)+SPACE_TIEM)+"----"+"VLDensityUtils.getScreenWidth()*ALLTIME/(VLDensityUtils.getScreenWidth()+measuredWidth))"+VLDensityUtils.getScreenWidth()*ALLTIME/(VLDensityUtils.getScreenWidth()+measuredWidth)+"System.currentTimeMillis()-tempTime"+(System.currentTimeMillis()-tempTime)+"safeTime"+safeTime);
//        System.out.println("System.currentTimeMillis()"+System.currentTimeMillis()+"   tempTime"+tempTime);
//        System.out.println("tempTime"+tempTime+"  safeTime="+safeTime);
        if((ALLTIME-(System.currentTimeMillis())+SPACE_TIEM<VLDensityUtils.getScreenWidth()*ALLTIME/(VLDensityUtils.getScreenWidth()+measuredWidth))&&(System.currentTimeMillis()-tempTime>safeTime)){
           //剩余时间加安全时间  需要小于 第二个view 运行到x为0时的时间   2 当前已启动时间  大于安全时间,这两个条件
            //可以处理 先显示 然后 return true
            tempTime = System.currentTimeMillis();
            safeTime = measuredWidth*ALLTIME/(measuredWidth+VLDensityUtils.getScreenWidth());
            showDanmaku(danmakuChildView,measuredWidth,safeTime);
//            System.out.println("reutrn true");
            return true;
        }else{
            //不可处理return false;
//            System.out.println("reutrn false");
            return false;
        }
    }
    Timer timer = new Timer();
    public void showDanmaku(final View danmakuChildView, int measuredWidth, final int safeTime){
        TranslateAnimation animation = new TranslateAnimation(VLDensityUtils.getScreenWidth(),-measuredWidth,0,0);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                timer.schedule(new TimerTask(){
                    @Override
                    public void run() {
                        if(showNext!=null){
                            post(new Runnable() {
                                @Override
                                public void run() {
                                    showNext.showNextDanmaku();
                                }
                            });
                        }
                    }
                } ,safeTime+SPACE_TIEM);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                removeView(danmakuChildView);
//                WboApplication.instance().broadcastMessage(MessageIDs.MESSAGEID_FC_DANMAKU_GET_LOCAL, null, null);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        animation.setInterpolator(new LinearInterpolator());
        animation.setDuration(ALLTIME);
        danmakuChildView.startAnimation(animation);
        addView(danmakuChildView);
    }

    public void setShowNext(IShowNextDanmakuListener showNext) {
        this.showNext = showNext;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        timer.cancel();
    }
}

IDanmakuView.java

package com.wobo.live.room.danmakulib;

import java.util.List;

/**
 * Created by zhou on 2016/5/31.
 */
public interface IDanmakuView {
    List getDanmakuCacheList();
    boolean isAddDanmakuSuccess(int pos);
}

DanmakuView.java

package com.wobo.live.room.danmakulib;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

import com.xiu8.android.activity.R;

import java.util.List;

/**
 * Created by zhou on 2016/5/31.
 */
public abstract class DanmakuView extends LinearLayout implements IDanmakuView {
    private DanmakuContainer firstView;
    public DanmakuView(Context context) {
        super(context);
        initView();
    }

    public DanmakuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    private void initView() {
        View v = LayoutInflater.from(getContext()).inflate(R.layout.view_danmaku, this, true);
        firstView = (DanmakuContainer)v.findViewById(R.id.first_view);
        firstView.setShowNext(mShowNextListener);
    }

    @Override
    public synchronized boolean isAddDanmakuSuccess(int pos){
        if(getDanmakuCacheList().get(pos)==null){
            return false;
        }
        if(firstView.isCanShow(getChildView(pos))){
            return true;
        }
        return false;
    }

    public abstract List getDanmakuCacheList();

    IShowNextDanmakuListener mShowNextListener = new IShowNextDanmakuListener() {
        @Override
        public void showNextDanmaku() {
            if (getDanmakuCacheList().size()>0 && isAddDanmakuSuccess(0)) {
                getDanmakuCacheList().remove(0);
            }
        }
    };
    public abstract View getChildView(int t);
}

IShowNextDanmakuListener.java

package com.wobo.live.room.danmakulib;

/**
 * Created by zhou on 2017/2/15.
 */

public interface IShowNextDanmakuListener {
    void showNextDanmaku();
}