Android 弹幕(一)自定义

弹幕(一)自定义无法暂停
根据时间戳和播放器同步弹幕数据

/**
 * 一个简化版的DanmakuViewPool
 */
public class CachedDanmakuViewPool implements Pool<DanmakuView> {
    private static final String TAG = "CachedDanmakuViewPool";

    /**
     * 缓存DanmakuView队列。显示已经完毕的DanmakuView会被添加到缓存中进行复用。
     * 在一定的时间{@link CachedDanmakuViewPool#mKeepAliveTime}过后,没有被访问到的DanmakuView会被回收。
     */
    private LinkedList<DanmakuViewWithExpireTime> mCache = new LinkedList<>();

    /**
     * 缓存存活时间
     */
    private long mKeepAliveTime;
    /**
     * 定时清理缓存
     */
    private ScheduledExecutorService mChecker = Executors.newSingleThreadScheduledExecutor();
    /**
     * 创建新DanmakuView的Creator
     */
    private ViewCreator<DanmakuView> mCreator;
    /**
     * 最大DanmakuView数量。
     * 这个数量包含了正在显示的DanmakuView和已经显示完毕进入缓存等待复用的DanmakuView之和。
     */
    private int mMaxSize;
    /**
     * 正在显示的弹幕数量。
     */
    private int mInUseSize;

    /**
     * @param creator 生成一个DanmakuView
     */
    CachedDanmakuViewPool(long keepAliveTime, int maxSize, ViewCreator<DanmakuView> creator) {
        mKeepAliveTime = keepAliveTime;
        mMaxSize = maxSize;
        mCreator = creator;
        mInUseSize = 0;

        scheduleCheckUnusedViews();
    }

    /**
     * 每隔一秒检查并清理掉空闲队列中超过一定时间没有被使用的DanmakuView
     */
    private void scheduleCheckUnusedViews() {
        mChecker.scheduleWithFixedDelay(() -> {
//            EasyL.v(TAG, "scheduleCheckUnusedViews: mInUseSize=" + mInUseSize + ", mCacheSize=" + mCache.size());
            long current = System.currentTimeMillis();
            while (!mCache.isEmpty()) {
                DanmakuViewWithExpireTime first = mCache.getFirst();
                if (current > first.expireTime) {
                    mCache.remove(first);
                } else {
                    break;
                }
            }
        }, 1000, 1000, TimeUnit.MILLISECONDS);
    }

    @Override
    public DanmakuView get() {
        DanmakuView view;

        if (mCache.isEmpty()) { // 缓存中没有View
            if (mInUseSize >= mMaxSize) {
                return null;
            }
            view = mCreator.create();
        } else { // 有可用的缓存,从缓存中取
            view = mCache.poll().danmakuView;
        }
        view.addOnExitListener(v -> {
            long expire = System.currentTimeMillis() + mKeepAliveTime;
            v.restore();
            DanmakuViewWithExpireTime item = new DanmakuViewWithExpireTime();
            item.danmakuView = v;
            item.expireTime = expire;
            mCache.offer(item);
            mInUseSize--;
        });
        mInUseSize++;

        return view;
    }

    @Override
    public void release() {
        mCache.clear();
    }

    /**
     * @return 使用中的DanmakuView和缓存中的DanmakuView数量之和
     */
    @Override
    public int count() {
        return mCache.size() + mInUseSize;
    }

    @Override
    public void setMaxSize(int max) {
        mMaxSize = max;
    }

    /**
     * 一个包裹类,保存一个DanmakuView和它的过期时间。
     */
    private class DanmakuViewWithExpireTime {
        private DanmakuView danmakuView; // 缓存的DanmakuView
        private long expireTime; // 超过这个时间没有被访问的缓存将被丢弃
    }

    public interface ViewCreator<T> {
        T create();
    }

}


public class Danmaku {
    public static final String COLOR_WHITE = "#ffffffff";
    public static final String COLOR_FFD662 = "#FFD662";
    public static final String COLOR_RED = "#ffff0000";
    public static final String COLOR_GREEN = "#ff00ff00";
    public static final String COLOR_BLUE = "#ff0000ff";
    public static final String COLOR_YELLOW = "#ffffff00";
    public static final String COLOR_PURPLE = "#ffff00ff";

    public static final int DEFAULT_TEXT_SIZE = 24;

    public String text;// 文字
    public int size = DEFAULT_TEXT_SIZE;// 字号
    public Mode mode = Mode.scroll;// 模式:滚动、顶部、底部
    public String color = COLOR_WHITE;// 默认白色
    public String borderColor = "";// 默认无边框

    public enum Mode {
        scroll, top, bottom
    }

    public Danmaku() {
    }

    public Danmaku(String text, int textSize, Mode mode, String color,String borderColor) {
        this.text = text;
        this.size = textSize;
        this.mode = mode;
        this.color = color;
        if (!TextUtils.isEmpty(borderColor)) {
            this.borderColor = borderColor;
        }
    }

    @Override
    public String toString() {
        return "Danmaku{" +
                "text='" + text + '\'' +
                ", size=" + size +
                ", mode=" + mode +
                ", color='" + color + '\'' +
                ", borderColor='" + borderColor + '\'' +
                '}';
    }
}

/**
 * 用法示例:
 * DanmakuManager dm = DanmakuManager.getInstance();
 * dm.init(getContext());
 * dm.show(new Danmaku("test"));
 * <p>
 * Created by LittleFogCat.
 */
@SuppressWarnings("unused")
public class DanmakuManager {
    private static final String TAG = DanmakuManager.class.getSimpleName();
    private static final int RESULT_OK = 0;
    private static final int RESULT_NULL_ROOT_VIEW = 1;
    private static final int RESULT_FULL_POOL = 2;
    private static final int TOO_MANY_DANMAKU = 2;

    private static DanmakuManager sInstance;

    /**
     * 弹幕容器
     */
    WeakReference<FrameLayout> mDanmakuContainer;
    /**
     * 弹幕池
     */
    private Pool<DanmakuView> mDanmakuViewPool;

    private Config mConfig;

    private DanmakuPositionCalculator mPositionCal;

    private Context context;
    private FrameLayout container;

    private DanmakuManager() {
    }

    public static DanmakuManager getInstance() {
        if (sInstance == null) {
            sInstance = new DanmakuManager();
        }
        return sInstance;
    }

    /**
     * 初始化。在使用之前必须调用该方法。
     */
    public void init(Context context, FrameLayout container) {
        this.context = context;
        this.container = container;

        if (mDanmakuViewPool == null) {
            mDanmakuViewPool = new CachedDanmakuViewPool(
                    60000, // 缓存存活时间:60秒
                    200, // 最大弹幕数:100
                    () -> DanmakuViewFactory.createDanmakuView(context, container));
        }
        setDanmakuContainer(container);
        ScreenUtil.init(context);

        mConfig = new Config();
        mPositionCal = new DanmakuPositionCalculator(this);
    }

    public Config getConfig() {
        if (mConfig == null) {
            mConfig = new Config();
        }
        return mConfig;
    }

    private DanmakuPositionCalculator getPositionCalculator() {
        if (mPositionCal == null) {
            mPositionCal = new DanmakuPositionCalculator(this);
        }
        return mPositionCal;
    }

    public void setDanmakuViewPool(Pool<DanmakuView> pool) {
        if (mDanmakuViewPool != null) {
            mDanmakuViewPool.release();
        }
        mDanmakuViewPool = pool;
    }

    public void clearDmAllMessage() {
        if (mDanmakuViewPool != null) {
            mDanmakuViewPool.release();
        }
    }

    /**
     * 设置允许同时出现最多的弹幕数,如果屏幕上显示的弹幕数超过该数量,那么新出现的弹幕将被丢弃,
     * 直到有旧的弹幕消失。
     *
     * @param max 同时出现的最多弹幕数,-1无限制
     */
    public void setMaxDanmakuSize(int max) {
        if (mDanmakuViewPool == null) {
            return;
        }
        mDanmakuViewPool.setMaxSize(max);
    }

    /**
     * 设置弹幕的容器,所有的弹幕都在这里面显示
     */
    public void setDanmakuContainer(final FrameLayout root) {
        if (root == null) {
//            throw new NullPointerException("Danmaku container cannot be null!");
            return;
        }
        mDanmakuContainer = new WeakReference<>(root);
    }

    /**
     * 发送一条弹幕
     */
    public int send(Danmaku danmaku) {
        if (mDanmakuViewPool == null) {
//            throw new NullPointerException("Danmaku view pool is null. Did you call init() first?");
            mDanmakuViewPool = new CachedDanmakuViewPool(
                    60000, // 缓存存活时间:60秒q
                    200, // 最大弹幕数:100
                    () -> DanmakuViewFactory.createDanmakuView(context, container));
        }

        DanmakuView view = mDanmakuViewPool.get();

        if (view == null) {
//            EasyL.w(TAG, "show: Too many danmaku, discard");
            return RESULT_FULL_POOL;
        }
        if (mDanmakuContainer == null || mDanmakuContainer.get() == null) {
//            EasyL.w(TAG, "show: Root view is null.");
            return RESULT_NULL_ROOT_VIEW;
        }

        view.setDanmaku(danmaku);

        // 字体大小
        int textSize = danmaku.size;
        view.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);

        // 字体边框
        view.setBorderColor(danmaku.borderColor);

        // 字体颜色
        try {
            int color = Color.parseColor(danmaku.color);
            view.setTextColor(color);
        } catch (Exception e) {
            e.printStackTrace();
            view.setTextColor(Color.WHITE);
        }

        // 计算弹幕距离顶部的位置
        DanmakuPositionCalculator dpc = getPositionCalculator();
        int marginTop = dpc.getMarginTop(view);

        if (marginTop == -1) {
            // 屏幕放不下了
//            EasyL.d(TAG, "send: screen is full, too many danmaku [" + danmaku + "]");
            return TOO_MANY_DANMAKU;
        }
        FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) view.getLayoutParams();
        if (p == null) {
            p = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        p.topMargin = marginTop;
        view.setLayoutParams(p);
        view.setMinHeight((int) (getConfig().getLineHeight() * 1.35));

        view.show(mDanmakuContainer.get(), getDisplayDuration(danmaku));
        return RESULT_OK;
    }

    /**
     * @return 返回这个弹幕显示时长
     */
    int getDisplayDuration(Danmaku danmaku) {
        Config config = getConfig();
        int duration;
        switch (danmaku.mode) {
            case top:
                duration = config.getDurationTop();
                break;
            case bottom:
                duration = config.getDurationBottom();
                break;
            case scroll:
            default:
                duration = config.getDurationScroll();
                break;
        }
        return duration;
    }


    /**
     * 一些配置
     */
    public static class Config {

        /**
         * 行高,单位px
         */
        private int lineHeight;

        /**
         * 滚动弹幕显示时长
         */
        private int durationScroll;
        /**
         * 顶部弹幕显示时长
         */
        private int durationTop;
        /**
         * 底部弹幕的显示时长
         */
        private int durationBottom;

        /**
         * 滚动弹幕的最大行数
         */
        private int maxScrollLine;

        public int getLineHeight() {
            return lineHeight;
        }

        public void setLineHeight(int lineHeight) {
            this.lineHeight = lineHeight;
        }

        public int getMaxScrollLine() {
            return maxScrollLine;
        }

        public int getDurationScroll() {
            if (durationScroll == 0) {
                durationScroll = 10000;
            }
            return durationScroll;
        }

        public void setDurationScroll(int durationScroll) {
            this.durationScroll = durationScroll;
        }

        public int getDurationTop() {
            if (durationTop == 0) {
                durationTop = 5000;
            }
            return durationTop;
        }

        public void setDurationTop(int durationTop) {
            this.durationTop = durationTop;
        }

        public int getDurationBottom() {
            if (durationBottom == 0) {
                durationBottom = 5000;
            }
            return durationBottom;
        }

        public void setDurationBottom(int durationBottom) {
            this.durationBottom = durationBottom;
        }

        public int getMaxDanmakuLine() {
            if (maxScrollLine == 0) {
                maxScrollLine = 12;
            }
            return maxScrollLine;
        }

        public void setMaxScrollLine(int maxScrollLine) {
            this.maxScrollLine = maxScrollLine;
        }
    }

}


/**
 * 用于计算弹幕位置,来保证弹幕不重叠又不浪费空间。
 */
class DanmakuPositionCalculator {
    private static final String TAG = "DanPositionCalculator";
    private DanmakuManager mDanmakuManager;
    private List<DanmakuView> mLastDanmakus = new ArrayList<>();// 保存每一行最后一个弹幕消失的时间
    private boolean[] mTops;
    private boolean[] mBottoms;

    DanmakuPositionCalculator(DanmakuManager danmakuManager) {
        mDanmakuManager = danmakuManager;

        int maxLine = danmakuManager.getConfig().getMaxDanmakuLine();
        mTops = new boolean[maxLine];
        mBottoms = new boolean[maxLine];
    }

    private int getLineHeightWithPadding() {
        return (int) (1.35f * mDanmakuManager.getConfig().getLineHeight());
    }

    int getMarginTop(DanmakuView view) {
        switch (view.getDanmaku().mode) {
            case scroll:
                return getScrollY(view);
            case top:
                return getTopY(view);
            case bottom:
                return getBottomY(view);
        }
        return -1;
    }

    private int getScrollY(DanmakuView view) {
        if (mLastDanmakus.size() == 0) {
            mLastDanmakus.add(view);
            return 0;
        }

        int i;
        for (i = 0; i < mLastDanmakus.size(); i++) {
            DanmakuView last = mLastDanmakus.get(i);
            int timeDisappear = calTimeDisappear(last); // 最后一条弹幕还需多久消失
            int timeArrive = calTimeArrive(view); // 这条弹幕需要多久到达屏幕边缘
            boolean isFullyShown = isFullyShown(last);
//            EasyL.d(TAG, "getScrollY: 行: " + i + ", 消失时间: " + timeDisappear + ", 到达时间: " + timeArrive + ", 行高: " + lineHeight);
            if (timeDisappear <= timeArrive && isFullyShown) {
                // 如果最后一个弹幕在这个弹幕到达之前消失,并且最后一个字已经显示完毕,
                // 那么新的弹幕就可以在这一行显示
                mLastDanmakus.set(i, view);
                return i * getLineHeightWithPadding();
            }
        }
        int maxLine = mDanmakuManager.getConfig().getMaxDanmakuLine();
        if (maxLine == 0 || i < maxLine) {
            mLastDanmakus.add(view);
            return i * getLineHeightWithPadding();
        }

        return -1;
    }

    private int getTopY(DanmakuView view) {
        for (int i = 0; i < mTops.length; i++) {
            boolean isShowing = mTops[i];
            if (!isShowing) {
                final int finalI = i;
                mTops[finalI] = true;
                view.addOnExitListener(view1 -> mTops[finalI] = false);
                return i * getLineHeightWithPadding();
            }
        }
        return -1;
    }

    private int getBottomY(DanmakuView view) {
        for (int i = 0; i < mBottoms.length; i++) {
            boolean isShowing = mBottoms[i];
            if (!isShowing) {
                final int finalI = i;
                mBottoms[finalI] = true;
                view.addOnExitListener(view1 -> mBottoms[finalI] = false);
                return getParentHeight() - (i + 1) * getLineHeightWithPadding();
            }
        }
        return -1;
    }

    /**
     * 这条弹幕是否已经全部出来了。如果没有的话,
     * 后面的弹幕不能出来,否则就重叠了。
     */
    private boolean isFullyShown(DanmakuView view) {
        if (view == null) {
            return true;
        }
        int scrollX = view.getScrollX();
        int textLength = view.getTextLength();
        return textLength - scrollX < getParentWidth();
    }

    /**
     * 这条弹幕还有多少毫秒彻底消失。
     */
    private int calTimeDisappear(DanmakuView view) {
        if (view == null) {
            return 0;
        }
        float speed = calSpeed(view);
        int scrollX = view.getScrollX();
        int textLength = view.getTextLength();
        int wayToGo = textLength - scrollX;

        return (int) (wayToGo / speed);
    }

    /**
     * 这条弹幕还要多少毫秒抵达屏幕边缘。
     */
    private int calTimeArrive(DanmakuView view) {
        float speed = calSpeed(view);
        int wayToGo = getParentWidth();
        return (int) (wayToGo / speed);
    }

    /**
     * 这条弹幕的速度。单位:px/ms
     */
    private float calSpeed(DanmakuView view) {
        int textLength = view.getTextLength();
        int width = getParentWidth();
        float s = textLength + width + 0.0f;
        int t = mDanmakuManager.getDisplayDuration(view.getDanmaku());

        return s / t;
    }

    private int getParentHeight() {
        ViewGroup parent = mDanmakuManager.mDanmakuContainer.get();
        if (parent == null || parent.getHeight() == 0) {
            return 1080;
        }
        return parent.getHeight();
    }

    private int getParentWidth() {
        ViewGroup parent = mDanmakuManager.mDanmakuContainer.get();
        if (parent == null || parent.getWidth() == 0) {
            return 1920;
        }
        return parent.getWidth();
    }

}

/**
 * DanmakuView的基类,继承自TextView,一个弹幕对应一个DanmakuView。
 * 这里实现了一些通用的功能。
 * <p>
 * Created by LittleFogCat.
 */
@SuppressWarnings("unused")
public class DanmakuView extends TextViewBorder {

    /**
     * 弹幕内容
     */
    private Danmaku mDanmaku;

    /**
     * 监听
     */
    private ListenerInfo mListenerInfo;

    private class ListenerInfo {
        private ArrayList<OnEnterListener> mOnEnterListeners;

        private List<OnExitListener> mOnExitListener;
    }

    /**
     * 弹幕进场时的监听
     */
    public interface OnEnterListener {
        void onEnter(DanmakuView view);
    }

    /**
     * 弹幕离场后的监听
     */
    public interface OnExitListener {
        void onExit(DanmakuView view);
    }

    /**
     * 显示时长 ms
     */
    private int mDuration;

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

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

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

    /**
     * 设置弹幕内容
     */
    public void setDanmaku(Danmaku danmaku) {
        mDanmaku = danmaku;
        setText(danmaku.text);
        switch (danmaku.mode) {
            case top:
            case bottom:
                setGravity(Gravity.CENTER);
                break;
            case scroll:
            default:
                setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
                break;
        }
    }

    public void setBorderColor(String borderColor) {
        if (!TextUtils.isEmpty(borderColor)) {
            super.setBorderColor(true);
        } else {
            super.setBorderColor(false);
        }
    }

    public Danmaku getDanmaku() {
        return mDanmaku;
    }

    /**
     * 显示弹幕
     */
    public void show(final ViewGroup parent, int duration) {
        mDuration = duration;
        switch (mDanmaku.mode) {
            case top:
            case bottom:
                showFixedDanmaku(parent, duration);
                break;
            case scroll:
            default:
                showScrollDanmaku(parent, duration);
                break;
        }

        if (hasOnEnterListener()) {
            for (OnEnterListener listener : getListenerInfo().mOnEnterListeners) {
                listener.onEnter(this);
            }
        }
        postDelayed(() -> {
            setVisibility(GONE);
            if (hasOnExitListener()) {
                for (OnExitListener listener : getListenerInfo().mOnExitListener) {
                    listener.onExit(DanmakuView.this);
                }
            }
            parent.removeView(DanmakuView.this);
        }, duration);
    }

    private void showScrollDanmaku(ViewGroup parent, int duration) {
        int screenWidth = ScreenUtil.getScreenWidth();
        int textLength = getTextLength();
        scrollTo(-screenWidth, 0);
        parent.addView(this);
        smoothScrollTo(textLength, 0, duration);
    }


    private void showFixedDanmaku(ViewGroup parent, int duration) {
        setGravity(Gravity.CENTER);
        parent.addView(this);
    }

    private ListenerInfo getListenerInfo() {
        if (mListenerInfo == null) {
            mListenerInfo = new ListenerInfo();
        }
        return mListenerInfo;
    }

    public void addOnEnterListener(OnEnterListener l) {
        ListenerInfo li = getListenerInfo();
        if (li.mOnEnterListeners == null) {
            li.mOnEnterListeners = new ArrayList<>();
        }
        if (!li.mOnEnterListeners.contains(l)) {
            li.mOnEnterListeners.add(l);
        }
    }

    public void clearOnEnterListeners() {
        ListenerInfo li = getListenerInfo();
        if (li.mOnEnterListeners == null || li.mOnEnterListeners.size() == 0) {
            return;
        }
        li.mOnEnterListeners.clear();
    }

    public void addOnExitListener(OnExitListener l) {
        ListenerInfo li = getListenerInfo();
        if (li.mOnExitListener == null) {
            li.mOnExitListener = new CopyOnWriteArrayList<>();
        }
        if (!li.mOnExitListener.contains(l)) {
            li.mOnExitListener.add(l);
        }
    }

    public void clearOnExitListeners() {
        ListenerInfo li = getListenerInfo();
        if (li.mOnExitListener == null || li.mOnExitListener.size() == 0) {
            return;
        }
        li.mOnExitListener.clear();
    }

    public boolean hasOnEnterListener() {
        ListenerInfo li = getListenerInfo();
        return li.mOnEnterListeners != null && li.mOnEnterListeners.size() != 0;
    }

    public boolean hasOnExitListener() {
        ListenerInfo li = getListenerInfo();
        return li.mOnExitListener != null && li.mOnExitListener.size() != 0;
    }

    public int getTextLength() {
        return (int) getPaint().measureText(getText().toString());
    }

    public int getDuration() {
        return mDuration;
    }

    /**
     * 恢复初始状态
     */
    public void restore() {
        clearOnEnterListeners();
        clearOnExitListeners();
        setVisibility(VISIBLE);
        setScrollX(0);
        setScrollY(0);
    }

    private Scroller mScroller;

    public void smoothScrollTo(int x, int y, int duration) {
        if (mScroller == null) {
            mScroller = new Scroller(getContext(), new LinearInterpolator());
            setScroller(mScroller);
        }

        int sx = getScrollX();
        int sy = getScrollY();
        mScroller.startScroll(sx, sy, x - sx, y - sy, duration);
    }

    @Override
    public void computeScroll() {
        if (mScroller != null && mScroller.computeScrollOffset()) {
//            EasyL.v(TAG, "computeScroll: " + mScroller.getCurrX());
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    void callExitListener() {
        for (OnExitListener listener : getListenerInfo().mOnExitListener) {
            listener.onExit(this);
        }
    }
}

class DanmakuViewFactory {
    @SuppressLint("InflateParams")
    static DanmakuView createDanmakuView(Context context) {
        return (DanmakuView) LayoutInflater.from(context)
                .inflate(R.layout.danmaku_view, null, false);
    }

    static DanmakuView createDanmakuView(Context context, ViewGroup parent) {
        return (DanmakuView) LayoutInflater.from(context)
                .inflate(R.layout.danmaku_view, parent, false);
    }
}

public class DanMuView extends FrameLayout {
    private FrameLayout container;
    private DanmakuManager dm;
    private static final int SENDDMLIST = 1001;
    //    private List<IDanmaku> danmakus = new ArrayList<>();
    private long currentTime = 0;
    private HashMap<Integer, List<BulletBean.DataBean>> danmaku = new HashMap<>();

    public DanMuView(@NonNull Context context) {
        this(context, null);
    }

    public DanMuView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DanMuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    public void init(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.danma_layout, this, false);
        addView(view);

        container = view.findViewById(R.id.frame_layout);
        initDanmu(context);
    }

    private void initDanmu(Context context) {
        // 获得DanmakuManager单例
        dm = DanmakuManager.getInstance();
        dm.init(context, container);
        dm.setMaxDanmakuSize(35); // 设置同屏最大弹幕数

        DanmakuManager.Config config = dm.getConfig(); // 弹幕相关设置
        config.setDurationScroll(10000); // 设置滚动字幕显示时长,默认10秒
        config.setMaxScrollLine(3); // 设置滚动字幕最大行数
        config.setLineHeight(ScreenUtil.autoSize(40)); // 设置行高
    }

    public void setDuration(int duration){
        DanmakuManager.Config config = dm.getConfig(); // 弹幕相关设置
        config.setDurationScroll(duration); // 设置滚动字幕显示时长,默认10秒
    }

    /**
     * 发送弹幕消息
     *
     * @param danmaku
     */
    public void sendDmMessage(IDanmaku danmaku) {
        if (container != null) {
            dm.setDanmakuContainer(container);
        }
        dm.send(danmaku);
    }

    /**
     * 发送弹幕组
     *
     * @param danmakus
     */
    public void sendDmMessage(HashMap<Integer, List<BulletBean.DataBean>> danmakus) {
        danmaku.clear();
        danmaku.putAll(danmakus);
        Log.e("zrq_dm","*" + danmaku.size() + " - " + danmakus.size());
        if (container != null) {
            dm.setDanmakuContainer(container);
        }
    }

    public void clearDmAllMessage() {
        DanmakuManager.getInstance().clearDmAllMessage();
        container.removeAllViews();
        danmaku.clear();
    }

    public void release() {
        clearDmAllMessage();
    }

    public void setCurrentTime(String date, long time) {
        String[] split = date.split(":");

        List<BulletBean.DataBean> dataBeans = danmaku.get(Integer.parseInt(time / 60 / 1000 + ""));
//        Log.e("zrq_dm"," -- "+danmaku.toString());
        if (dataBeans == null) {
            return;
        }
//        Log.e("zrq_dm","----");
        int curTime = Integer.parseInt(split[0]) * 60 * 60 + Integer.parseInt(split[1]) * 60 + Integer.parseInt(split[2]);
        for (int i = 0; i < dataBeans.size(); i++) {
            BulletBean.DataBean dataBean = dataBeans.get(i);
//            if (Integer.parseInt(split[2]) == dataBean.time && Integer.parseInt(split[1]) == dataBean.minute){
            if (curTime == dataBean.time) {
                sendDmMessage(new IDanmaku(0, dataBeans.get(i).content));
            }
        }
    }

    public void setCurrentTime(long time) {
        currentTime = time;
        //对应的分取对应内的所有数据
        List<BulletBean.DataBean> dataBeans = danmaku.get(Integer.parseInt(currentTime / 60 / 1000 + ""));
        if (dataBeans == null) {
            return;
        }
        for (int i = 0; i < dataBeans.size(); i++) {
            long times = (long) dataBeans.get(i).minute * 1000 * 60 + dataBeans.get(i).time * 1000;

            if (currentTime <= times && times <= currentTime + 1000) {
                sendDmMessage(new IDanmaku(times, dataBeans.get(i).content));
            }
        }
//        for (int i = 0; i < danmakus.size(); i++) {
//            if (currentTime <= danmakus.get(i).time && currentTime + 1000 >= danmakus.get(i).time) {
//                sendDmMessage(danmakus.get(i));
//                break;
//            }
//        }
    }

}

public class IDanmaku extends Danmaku {
    public long time;
    public String text;
    public int size = Danmaku.DEFAULT_TEXT_SIZE;

    public IDanmaku(long time,String text,int textSize) {
        super(text,textSize,Mode.scroll,COLOR_WHITE,"");
        this.time = time;
        this.size = textSize;
    }

    public IDanmaku(long time,String text,String textColor) {
        super(text,50,Mode.scroll,textColor,"");
        this.time = time;
        this.size = 50;
    }

    public IDanmaku(long time,String text,String textColor,String borderColor) {
        super(text,50,Mode.scroll,textColor,borderColor);
        this.time = time;
        this.size = 50;
    }

    public IDanmaku(long time,String text) {
        super(text,50,Mode.scroll,COLOR_WHITE,"");
        this.time = time;
    }

}

public interface Pool<T> {
    /**
     * 从缓存中获取一个T的实例
     */
    T get();

    /**
     * 释放缓存
     */
    void release();

    /**
     * @return 缓存中T实例的数量
     */
    int count();

    void setMaxSize(int max);
}

/**
 * Created by LittleFogCat.
 * <p>
 * 自动适配屏幕像素的工具类。
 * 需要先调用{@link ScreenUtil#init(Context)}才能正常使用。如果屏幕旋转,
 * 那么需要再次调用{@link ScreenUtil#init(Context)}以更新。
 */

@SuppressWarnings({"unused", "WeakerAccess", "SuspiciousNameCombination"})
public class ScreenUtil {
    private static final String TAG = "ScreenUtil";
    /**
     * 屏幕宽度,在调用init()之后通过{@link ScreenUtil#getScreenWidth()}获取
     */
    private static int sScreenWidth = 1920;

    /**
     * 屏幕高度,在调用init()之后通过{@link ScreenUtil#getScreenHeight()} ()}获取
     */
    private static int sScreenHeight = 1080;

    /**
     * 设计宽度。用于{@link ScreenUtil#autoWidth(int)}
     */
    private static int sDesignWidth = 1080;

    /**
     * 设计高度。用于{@link ScreenUtil#autoHeight(int)} (int)}
     */
    private static int sDesignHeight = 1920;

    /**
     * 初始化ScreenUtil。在屏幕旋转之后,需要再次调用这个方法,否则计算将会出错。
     */
    public static void init(Context context) {
        DisplayMetrics m = context.getResources().getDisplayMetrics();

        sScreenWidth = m.widthPixels;
        sScreenHeight = m.heightPixels;

        if (sDesignWidth > sDesignHeight != sScreenWidth > sScreenHeight) {
            int tmp = sDesignWidth;
            sDesignWidth = sDesignHeight;
            sDesignHeight = tmp;
        }
    }

    public static void setDesignWidthAndHeight(int width, int height) {
        sDesignWidth = width;
        sDesignHeight = height;
    }

    /**
     * 根据实际屏幕和设计图的比例,自动缩放像素大小。
     * <p>
     * 例如设计图大小是1920像素x1080像素,实际屏幕是2560像素x1440像素,那么对于一个设计图中100x100像素的方形,
     * 实际屏幕中将会缩放为133像素x133像素。这有可能导致图形的失真(当实际的横竖比和设计图不同时)
     *
     * @param origin 设计图上的像素大小
     * @return 实际屏幕中的尺寸
     */
    public static int autoSize(int origin) {
        return autoWidth(origin);
    }

    /**
     * 对于在横屏和竖屏下尺寸不同的物体,分别给出设计图的像素,返回实际屏幕中应有的像素。
     *
     * @param land 在横屏设计图中的像素大小
     * @param port 在竖屏设计图中的像素大小
     */
    public static int autoSize(int land, int port) {
        return isPortrait() ? autoSize(port) : autoSize(land);
    }

    /**
     * 根据屏幕分辨率自适应宽度。
     *
     * @param origin 设计图中的宽度,像素
     * @return 实际屏幕中的宽度,像素
     */
    public static int autoWidth(int origin) {
        if (sScreenWidth == 0 || sDesignWidth == 0) {
            return origin;
        }
        int autoSize = origin * sScreenWidth / sDesignWidth;
        if (origin != 0 && autoSize == 0) {
            return 1;
        }
        return autoSize;
    }

    /**
     * 根据屏幕分辨率自适应高度
     *
     * @param origin 设计图中的高度,像素
     * @return 实际屏幕中的高度,像素
     */
    public static int autoHeight(int origin) {
        if (sScreenHeight == 0 || sDesignHeight == 0) {
            return origin;
        }
        int auto = origin * sScreenHeight / sDesignHeight;
        if (origin != 0 && auto == 0) {
            return 1;
        }
        return auto;
    }

    public static int getScreenWidth() {
        return sScreenWidth;
    }

    public static void setScreenWidth(int w) {
        sScreenWidth = w;
    }

    public static int getScreenHeight() {
        return sScreenHeight;
    }

    public static void setScreenHeight(int h) {
        sScreenHeight = h;
    }

    /**
     * 是否是竖屏
     */
    public static boolean isPortrait() {
        return getScreenHeight() > getScreenWidth();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值