您当前的位置: 首页 >  android

Kevin-Dev

暂无认证

  • 3浏览

    0关注

    544博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Android -- 开源库】DanmakuFlameMaster(弹幕) 的基本使用

Kevin-Dev 发布时间:2022-04-07 08:00:00 ,浏览量:3

在这里插入图片描述

文章目录
    • 一、前言
    • 二、功能
    • 三、实例
      • 1. 效果图
      • 2. 提前准备
      • 3. 布局文件
      • 4. 初始化弹幕数据
      • 5. 创建解析器对象
      • 6. 添加文本弹幕
      • 7. 添加图文混排弹幕
      • 8. 弹幕的隐藏/显示,暂停/继续
      • 9. 释放资源

一、前言

Android上开源弹幕解析绘制引擎项目。

GitHub 地址:DanmakuFlameMaster

二、功能
  • 使用多种方式(View/SurfaceView/TextureView)实现高效绘制

  • B站xml弹幕格式解析

  • 基础弹幕精确还原绘制

  • 支持mode7特殊弹幕

  • 多核机型优化,高效的预缓存机制

  • 支持多种显示效果选项实时切换

  • 实时弹幕显示支持

  • 换行弹幕支持/运动弹幕支持

  • 支持自定义字体

  • 支持多种弹幕参数设置

  • 支持多种方式的弹幕屏蔽

三、实例 1. 效果图

在这里插入图片描述

2. 提前准备
  • 在 build.gradle 中添加如下依赖:
repositories {
    jcenter()
}
--------------------------------------------------------------
dependencies {
    //弹幕
    implementation 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
    implementation 'com.github.ctiao:ndkbitmap-armv7a:0.9.21'
}
  • 弹幕数据文件 可以在 GitHub 上下在资源: 在这里插入图片描述
3. 布局文件



    

    

    

    

    

    

    


4. 初始化弹幕数据
private void initDanmaku() {
        // 设置最大显示行数
        HashMap maxLinesPair = new HashMap();
        maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 10); // 滚动弹幕最大显示5行
        // 设置是否禁止重叠
        HashMap overlappingEnablePair = new HashMap();
        overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);
        overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);


        mContext = DanmakuContext.create();
        mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3).setDuplicateMergingEnabled(false)
                .setScrollSpeedFactor(1.2f)
                .setScaleTextSize(1.2f)
                .setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer
//        .setCacheStuffer(new BackgroundCacheStuffer())  // 绘制背景使用BackgroundCacheStuffer
                .setMaximumLines(maxLinesPair)
                .preventOverlapping(overlappingEnablePair).setDanmakuMargin(40);
        if (mDanmakuView != null) {
            mParser = createParser(this.getResources().openRawResource(R.raw.comments));
            mDanmakuView.setCallback(new DrawHandler.Callback() {
                @Override
                public void updateTimer(DanmakuTimer timer) {

                }

                @Override
                public void drawingFinished() {

                }

                @Override
                public void danmakuShown(BaseDanmaku danmaku) {

//                    Log.d("DFM", "danmakuShown(): text=" + danmaku.text);
                }

                @Override
                public void prepared() {
                    loadData();
                    mDanmakuView.start();
                }
            });
            mDanmakuView.setOnDanmakuClickListener(new IDanmakuView.OnDanmakuClickListener() {

                @Override
                public boolean onDanmakuClick(IDanmakus danmakus) {
                    Log.d("DFM", "onDanmakuClick: danmakus size:" + danmakus.size());
                    BaseDanmaku latest = danmakus.last();
                    if (null != latest) {
                        Log.d("DFM", "onDanmakuClick: text of latest danmaku:" + latest.text);
                        return true;
                    }
                    return false;
                }

                @Override
                public boolean onDanmakuLongClick(IDanmakus danmakus) {
                    return false;
                }

                @Override
                public boolean onViewClick(IDanmakuView view) {
//                    mMediaController.setVisibility(View.VISIBLE);
                    return false;
                }
            });
            mDanmakuView.prepare(mParser, mContext);
            mDanmakuView.showFPS(false);
            mDanmakuView.enableDanmakuDrawingCache(true);
        }
    }
5. 创建解析器对象
/**
 * Created on 2022/8/17 11:20
 *
 * @author Gong Youqiang
 */
public class HuaKeDanmukuParser extends BaseDanmakuParser {
    static {
        System.setProperty("org.xml.sax.driver", "org.xmlpull.v1.sax2.Driver");
    }

    protected float mDispScaleX;
    protected float mDispScaleY;

    @Override
    public Danmakus parse() {

        if (mDataSource != null) {
            AndroidFileSource source = (AndroidFileSource) mDataSource;
            try {
                XMLReader xmlReader = XMLReaderFactory.createXMLReader();
                XmlContentHandler contentHandler = new XmlContentHandler();
                xmlReader.setContentHandler(contentHandler);
                xmlReader.parse(new InputSource(source.data()));
                return contentHandler.getResult();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        return null;
    }

    public class XmlContentHandler extends DefaultHandler {

        private static final String TRUE_STRING = "true";

        public Danmakus result;

        public BaseDanmaku item = null;

        public boolean completed = false;

        public int index = 0;

        public Danmakus getResult() {
            return result;
        }

        @Override
        public void startDocument() throws SAXException {
            result = new Danmakus(ST_BY_TIME, false, mContext.getBaseComparator());
        }

        @Override
        public void endDocument() throws SAXException {
            completed = true;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes)
                throws SAXException {
            String tagName = localName.length() != 0 ? localName : qName;
            tagName = tagName.toLowerCase(Locale.getDefault()).trim();
            if (tagName.equals("d")) {
                // 我从未见过如此厚颜无耻之猴
                // 0:时间(弹幕出现时间)
                // 1:类型(1从右至左滚动弹幕|6从左至右滚动弹幕|5顶端固定弹幕|4底端固定弹幕|7高级弹幕|8脚本弹幕)
                // 2:字号
                // 3:颜色
                // 4:时间戳 ?
                // 5:弹幕池id
                // 6:用户hash
                // 7:弹幕id
                String pValue = attributes.getValue("p");
                // parse p value to danmaku
                String[] values = pValue.split(",");
                if (values.length > 0) {
                    long time = (long) (parseFloat(values[0]) * 1000); // 出现时间
                    int type = parseInteger(values[1]); // 弹幕类型
                    float textSize = parseFloat(values[2]); // 字体大小
                    int color = (int) ((0x00000000ff000000 | parseLong(values[3])) & 0x00000000ffffffff); // 颜色
                    // int poolType = parseInteger(values[5]); // 弹幕池类型(忽略
                    item = mContext.mDanmakuFactory.createDanmaku(type, mContext);
                    if (item != null) {
                        item.setTime(time);
                        item.textSize = textSize * (mDispDensity - 0.6f);
                        item.textColor = color;
                        item.textShadowColor = color = 11) {
                        endX = parseFloat(textArr[7]);
                        endY = parseFloat(textArr[8]);
                        if (!"".equals(textArr[9])) {
                            translationDuration = parseInteger(textArr[9]);
                        }
                        if (!"".equals(textArr[10])) {
                            translationStartDelay = (long) (parseFloat(textArr[10]));
                        }
                    }
                    if (isPercentageNumber(textArr[0])) {
                        beginX *= DanmakuFactory.BILI_PLAYER_WIDTH;
                    }
                    if (isPercentageNumber(textArr[1])) {
                        beginY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
                    }
                    if (textArr.length >= 8 && isPercentageNumber(textArr[7])) {
                        endX *= DanmakuFactory.BILI_PLAYER_WIDTH;
                    }
                    if (textArr.length >= 9 && isPercentageNumber(textArr[8])) {
                        endY *= DanmakuFactory.BILI_PLAYER_HEIGHT;
                    }
                    item.duration = new Duration(alphaDuraion);
                    item.rotationZ = rotateZ;
                    item.rotationY = rotateY;
                    mContext.mDanmakuFactory.fillTranslationData(item, beginX,
                            beginY, endX, endY, translationDuration, translationStartDelay, mDispScaleX, mDispScaleY);
                    mContext.mDanmakuFactory.fillAlphaData(item, beginAlpha, endAlpha, alphaDuraion);

                    if (textArr.length >= 12) {
                        // 是否有描边
                        if (!TextUtils.isEmpty(textArr[11]) && TRUE_STRING.equalsIgnoreCase(textArr[11])) {
                            item.textShadowColor = Color.TRANSPARENT;
                        }
                    }
                    if (textArr.length >= 13) {
                        //TODO 字体 textArr[12]
                    }
                    if (textArr.length >= 14) {
                        // Linear.easeIn or Quadratic.easeOut
                        ((SpecialDanmaku) item).isQuadraticEaseOut = ("0".equals(textArr[13]));
                    }
                    if (textArr.length >= 15) {
                        // 路径数据
                        if (!"".equals(textArr[14])) {
                            String motionPathString = textArr[14].substring(1);
                            if (!TextUtils.isEmpty(motionPathString)) {
                                String[] pointStrArray = motionPathString.split("L");
                                if (pointStrArray.length > 0) {
                                    float[][] points = new float[pointStrArray.length][2];
                                    for (int i = 0; i = 2) {
                                            points[i][0] = parseFloat(pointArray[0]);
                                            points[i][1] = parseFloat(pointArray[1]);
                                        }
                                    }
                                    mContext.mDanmakuFactory.fillLinePathData(item, points, mDispScaleX,
                                            mDispScaleY);
                                }
                            }
                        }
                    }
                }

            }
        }

        private String decodeXmlString(String title) {
            if (title.contains("&")) {
                title = title.replace("&", "&");
            }
            if (title.contains(""")) {
                title = title.replace(""", "\"");
            }
            if (title.contains(">")) {
                title = title.replace(">", ">");
            }
            if (title.contains("<")) {
                title = title.replace("<", "            
关注
打赏
1658837700
查看更多评论
0.0405s