开源弹幕引擎·烈焰弹幕使(DanmakuFlameMaster)使用解析
2016-04-04 15:04
531 查看
简介
DanmakuFlameMaster 是 Android 上开源弹幕解析绘制引擎项目,也是 Android 上最好的开源弹幕引擎·烈焰弹幕。其架构清晰,简单易用,支持多种高效率绘制方式选择,支持多种自定义功能设置上。 目前,DanmakuFlameMaster 开发包已被包括优酷土豆、开迅视频、MissEvan、echo回声、斗鱼TV、天天动听、被窝声次元、ACFUN 等 APP 使用。 Features 使用多种方式(View/SurfaceView/TextureView)实现高效绘制 B站xml弹幕格式解析 基础弹幕精确还原绘制 支持mode7特殊弹幕 多核机型优化,高效的预缓存机制 支持多种显示效果选项实时切换 实时弹幕显示支持 换行弹幕支持/运动弹幕支持 支持自定义字体 支持多种弹幕参数设置 支持多种方式的弹幕屏蔽 TODO: 继续精确/稳定绘帧周期 增加OpenGL ES绘制方式 改进缓存策略和效率
github地址:https://github.com/Bilibili/DanmakuFlameMaster
demo示例
集成方法
build.gradle中添加dependencies { compile 'com.github.ctiao:dfm:0.4.2' }
使用方法
一、布局文件定义<master.flame.danmaku.ui.widget.DanmakuView android:id="@+id/sv_danmaku" android:layout_width="match_parent" android:layout_height="match_parent" />
二、初始化
private BaseDanmakuParser mParser;//解析器对象 private IDanmakuView mDanmakuView; //实例化 mDanmakuView = (IDanmakuView) findViewById(R.id.sv_danmaku); private DanmakuContext mContext; mContext = DanmakuContext.create(); // 设置弹幕的最大显示行数 HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>(); maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 3); // 滚动弹幕最大显示3行 // 设置是否禁止重叠 HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>(); overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_LR, true); overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_BOTTOM, true); mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3) //设置描边样式 .setDuplicateMergingEnabled(false) .setScrollSpeedFactor(1.2f) //是否启用合并重复弹幕 .setScaleTextSize(1.2f) //设置弹幕滚动速度系数,只对滚动弹幕有效 .setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer 设置缓存绘制填充器,默认使用{@link SimpleTextCacheStuffer}只支持纯文字显示, 如果需要图文混排请设置{@link SpannedCacheStuffer}如果需要定制其他样式请扩展{@link SimpleTextCacheStuffer}|{@link SpannedCacheStuffer} .setMaximumLines(maxLinesPair) //设置最大显示行数 .preventOverlapping(overlappingEnablePair); //设置防弹幕重叠,null为允许重叠 if (mDanmakuView != null) { mParser = createParser(this.getResources().openRawResource(R.raw.comments)); //创建解析器对象,从raw资源目录下解析comments.xml文本 mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() { @Override public void updateTimer(DanmakuTimer timer) { } @Override public void drawingFinished() { } @Override public void danmakuShown(BaseDanmaku danmaku) { } @Override public void prepared() { mDanmakuView.start(); } }); mDanmakuView.prepare(mParser, mContext); mDanmakuView.showFPS(false); //是否显示FPS mDanmakuView.enableDanmakuDrawingCache(true);
三、创建解析器对象
/** * 创建解析器对象,解析输入流 * @param stream * @return */ private BaseDanmakuParser createParser(InputStream stream) { if (stream == null) { return new BaseDanmakuParser() { @Override protected Danmakus parse() { return new Danmakus(); } }; } ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI); try { loader.load(stream); } catch (IllegalDataException e) { e.printStackTrace(); } BaseDanmakuParser parser = new BiliDanmukuParser(); IDataSource<?> dataSource = loader.getDataSource(); parser.load(dataSource); return parser; } 注: DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_BILI) //xml解析 DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_ACFUN) //json文件格式解析
四、自定义弹幕背景和边距
private static class BackgroundCacheStuffer extends SpannedCacheStuffer { // 通过扩展SimpleTextCacheStuffer或SpannedCacheStuffer个性化你的弹幕样式 final Paint paint = new Paint(); @Override public void measure(BaseDanmaku danmaku, TextPaint paint) { danmaku.padding = 10; // 在背景绘制模式下增加padding super.measure(danmaku, paint); } @Override public void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) { paint.setColor(0x8125309b); //弹幕背景颜色 canvas.drawRect(left + 2, top + 2, left + danmaku.paintWidth - 2, top + danmaku.paintHeight - 2, paint); } @Override public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) { // 禁用描边绘制 } }
五、添加文本弹幕
private void addDanmaku(boolean islive) { BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); if (danmaku == null || mDanmakuView == null) { return; } danmaku.text = "这是一条弹幕" + System.nanoTime(); danmaku.padding = 5; danmaku.priority = 0; //0 表示可能会被各种过滤器过滤并隐藏显示 //1 表示一定会显示, 一般用于本机发送的弹幕 danmaku.isLive = islive; //是否是直播弹幕 danmaku.time = mDanmakuView.getCurrentTime() + 1200; //显示时间 danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f); danmaku.textColor = Color.RED; danmaku.textShadowColor = Color.WHITE; //阴影/描边颜色 danmaku.borderColor = Color.GREEN; //边框颜色,0表示无边框 mDanmakuView.addDanmaku(danmaku); }
六、添加图文混排弹幕
private void addDanmaKuShowTextAndImage(boolean islive) { BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher); drawable.setBounds(0, 0, 100, 100); SpannableStringBuilder spannable = createSpannable(drawable); danmaku.text = spannable; danmaku.padding = 5; danmaku.priority = 1; // 一定会显示, 一般用于本机发送的弹幕 danmaku.isLive = islive; danmaku.time = mDanmakuView.getCurrentTime() + 1200; danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f); danmaku.textColor = Color.RED; danmaku.textShadowColor = 0; // 重要:如果有图文混排,最好不要设置描边(设textShadowColor=0),否则会进行两次复杂的绘制导致运行效率降低 danmaku.underlineColor = Color.GREEN; mDanmakuView.addDanmaku(danmaku); } /** * 创建图文混排模式 * @param drawable * @return */ private SpannableStringBuilder createSpannable(Drawable drawable) { String text = "bitmap"; SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text); ImageSpan span = new ImageSpan(drawable);//ImageSpan.ALIGN_BOTTOM); spannableStringBuilder.setSpan(span, 0, text.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); spannableStringBuilder.append("图文混排"); spannableStringBuilder.setSpan(new BackgroundColorSpan(Color.parseColor("#8A2233B1")), 0, spannableStringBuilder.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE); return spannableStringBuilder; }
七、弹幕的隐藏/显示,暂停/继续
mDanmakuView.hide(); mDanmakuView.show(); //暂停 if (mDanmakuView != null && mDanmakuView.isPrepared()) { mDanmakuView.pause(); } //继续 if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) { mDanmakuView.resume(); }
八、弹幕的定时发送
Boolean b = (Boolean) mBtnSendDanmakus.getTag(); timer.cancel(); if (b == null || !b) { mBtnSendDanmakus.setText(R.string.cancel_sending_danmakus); timer = new Timer(); timer.schedule(new AsyncAddTask(), 0, 1000); mBtnSendDanmakus.setTag(true); } else { mBtnSendDanmakus.setText(R.string.send_danmakus); mBtnSendDanmakus.setTag(false); } Timer timer = new Timer(); class AsyncAddTask extends TimerTask { @Override public void run() { for (int i = 0; i < 20; i++) { addDanmaku(true); SystemClock.sleep(20); } } }
九、创建图文混排的填充适配器
private BaseCacheStuffer.Proxy mCacheStufferAdapter = new BaseCacheStuffer.Proxy() { private Drawable mDrawable; /** * 在弹幕显示前使用新的text,使用新的text * @param danmaku * @param fromWorkerThread 是否在工作(非UI)线程,在true的情况下可以做一些耗时操作(例如更新Span的drawblae或者其他IO操作) * @return 如果不需重置,直接返回danmaku.text */ @Override public void prepareDrawing(final BaseDanmaku danmaku, boolean fromWorkerThread) { if (danmaku.text instanceof Spanned) { // 根据你的条件检查是否需要需要更新弹幕 // FIXME 这里只是简单启个线程来加载远程url图片,请使用你自己的异步线程池,最好加上你的缓存池 new Thread() { @Override public void run() { String url = "http://www.bilibili.com/favicon.ico"; InputStream inputStream = null; Drawable drawable = mDrawable; if (drawable == null) { try { URLConnection urlConnection = new URL(url).openConnection(); inputStream = urlConnection.getInputStream(); drawable = BitmapDrawable.createFromStream(inputStream, "bitmap"); mDrawable = drawable; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(inputStream); } } if (drawable != null) { drawable.setBounds(0, 0, 100, 100); SpannableStringBuilder spannable = createSpannable(drawable); danmaku.text = spannable; if (mDanmakuView != null) { mDanmakuView.invalidateDanmaku(danmaku, false); } return; } } }.start(); } } @Override public void releaseResource(BaseDanmaku danmaku) { // TODO 重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable } };
十、退出时释放资源
@Override protected void onDestroy() { super.onDestroy(); if (mDanmakuView != null) { // dont forget release! mDanmakuView.release(); mDanmakuView = null; } }
相关文章推荐
- 排序算法
- poj之旅——3045
- Android Studio 生成jks密钥 和 Gradle 多渠道打包
- “代理服务器拒绝连接“问题
- [BZOJ3631][JLOI2014]松鼠的新家(链剖)
- VR游戏开发干货教程:如何创建一个VR项目
- JavaEE应用的分层模型和架构
- 论文笔记--人脸检测 DL
- bzoj 2005: [Noi2010] 能量采集
- 吃吃饭
- 吃吃饭
- 吃吃饭
- J2EE轻量级框架--4.01学习心得
- #ifdef __OBJC__ 宏定义的作用
- 解决 Qt 使用 PNG 图片时报错:libpng warning: iCCP: known incorrect sRGB profile
- 《leetCode》:Bitwise AND of Numbers Range
- InSAR学习(五)高级的InSAR技术:PS技术和SBAS技术
- 吃吃饭
- 005 ZeroMQ REQ-<ROUTER-ROUTER>-REQ 路由模式
- 第216期 辞退员工面谈有妙招 离职员工情绪管理不可少