这里根据我用到过的功能填一些坑,但目前也只用到了部分功能,弹幕数据动态衔接的问题还没有遇到,以后遇到添加进来

1. 基本使用

基本使用看官方demo就行, 官方demo 里注释非常详细,或者看这篇文章: 开源弹幕引擎·烈焰弹幕使(DanmakuFlameMaster)使用解析

    private void initDamakuView(String s) {danmakuContext = DanmakuContext.create();HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_LR, 3); // 滚动弹幕最大显示3行HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_LR, true);overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_BOTTOM, true);danmakuContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 3) //设置描边样式.setDuplicateMergingEnabled(false).setScrollSpeedFactor(1.2f) //是否启用合并重复弹幕.setScaleTextSize(1.2f) //设置弹幕滚动速度系数,只对滚动弹幕有效.setCacheStuffer(new BackgroundCatchSpanner(this), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer  设置缓存绘制填充器,默认使用{@link SimpleTextCacheStuffer}只支持纯文字显示, 如果需要图文混排请设置{@link SpannedCacheStuffer}如果需要定制其他样式请扩展{@link SimpleTextCacheStuffer}|{@link SpannedCacheStuffer}.setMaximumLines(maxLinesPair) //设置最大显示行数.preventOverlapping(overlappingEnablePair); //设置防弹幕重叠,null为允许重叠if (danmakuView != null) {try {parser = (MyDanmakuParaser) createParser(this.getAssets().open(s)); //创建解析器对象,从raw资源目录下解析comments.xml文本} catch (IOException e) {e.printStackTrace();}danmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {@Overridepublic void updateTimer(DanmakuTimer timer) {}@Overridepublic void drawingFinished() {}@Overridepublic void danmakuShown(BaseDanmaku danmaku) {}@Overridepublic void prepared() {danmakuView.start();}});danmakuView.prepare(parser, danmakuContext);danmakuView.showFPS(true); //是否显示FPS}}

这样就初始化完成了,弹幕滚动了起来。但是在这一步很可能会遇到一个问题,设置不生效

设置不生效其实跟BaseDanmaku中一个属性priority有关,注释也注明了:

danmaku.priority = 1;  //0 表示可能会被各种过滤器过滤并隐藏显示 //1 表示一定会显示, 一般用于本机发送的弹幕

出现设置不生效/弹幕丢失等等情况,都是这个参数为1导致的。弹幕的显示是与时间同步的,有时候会出现同一时间(这个时间是很短的一个时间段(ms级),可以翻翻源码看具体是多少)内有大量弹幕,但是弹幕的速度(这个速度指的是一条弹幕显示完整所用的时长,字数不同会导致速率不同)、屏幕大小、显示大小是固定的,因此能显示的弹幕条数是有限的,多出的就被过滤了。

这里假设极端情况在01s内有200条弹幕,但是12秒内没有弹幕,他是不会将弹幕时间后移显示的,可能因为实现困难和弹幕时间的同步

优先级为1的时候一定会显示,因此会出现设置了3行,但是显示了很多行,因为弹幕太多,3行它放不下啊!只有优先级0的时候过滤条件才会生效

2. 自定义弹幕样式

BaseDanmaku支持自定义paddingtextSizetextColorborderColor等等,但是需求肯定不会如此轻松,一般有两种,加背景加图标

添加图标比较简单,根据需求使用SpannableStringBuilder做图文混排就行:

    public static SpannableStringBuilder createSpannable(Context context, String s) {try {if (s.contains("\n")) {s = s.replaceAll("\n", " "); //这里处理了换行符,否则存在换行符时每行都会有图标}} catch (Exception e) {e.printStackTrace();}SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(s);Drawable drawable = context.getResources().getDrawable(R.drawable.vip_ico);drawable.setBounds(0, 0, UIUtils.dip2px(context, 15),UIUtils.dip2px(context, 15)); //算好padding做个居中处理ImageSpan span = new CenterAlignImageSpan(drawable);spannableStringBuilder.setSpan(span, 0, s.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);spannableStringBuilder.append(" ");spannableStringBuilder.append(s);spannableStringBuilder.setSpan(new BackgroundColorSpan(Color.TRANSPARENT),0, spannableStringBuilder.length(),Spannable.SPAN_INCLUSIVE_INCLUSIVE);return spannableStringBuilder;}

使用:

danmaku.text = createSpannable(getContext(), text);

自定义样式需要继承SpannedCacheStuffer

重写drawText()方法可以重写text的样式:

    //修改弹幕字体@Overridepublic void drawText(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, TextPaint paint, boolean fromWorkerThread) {Typeface mTypeFace = Typeface.createFromAsset(context.getAssets(), "fonts/ukij_tor.ttf");  //字体文件paint.setTypeface(mTypeFace);super.drawText(danmaku, lineText, canvas, left, top, paint, fromWorkerThread);}

重写drawStroke()画边框

    @Overridepublic void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {}

这里有个坑,就是我们其实看到的弹幕上下间距是靠padding实现的,加上边框就很明显能看出来了。但实际上我们的需求一般都会要求边框上线有个间距,也就是margin,因此想要实现这种效果只能从背景下手, 画一个上下不撑满的圆角矩形:

重写drawBackground()画背景

    @Overrideprotected void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {super.drawBackground(danmaku, canvas, left, top);if ((boolean) danmaku.tag) {    //tag接受Object对象,可以用它来区分需要重绘的弹幕类型RectF rectF = new RectF(left + 2, top + dp2px(context, 5),left + danmaku.paintWidth - 2,top + danmaku.paintHeight - dp2px(context, 5));canvas.drawRoundRect(rectF, dp2px(context, 13), dp2px(context, 13), paint);}}

使用:

 danmakuContext.setCacheStuffer(new BackgroundCatchSpanner(this), mCacheStufferAdapter);

其中的mChacheStufferAdapter中可以进行图片等资源的异步加载:

    private BaseCacheStuffer.Proxy mCacheStufferAdapter = new BaseCacheStuffer.Proxy() {@Overridepublic void prepareDrawing(final BaseDanmaku danmaku, boolean fromWorkerThread) {//这个回调会在BaseDanmaku.prepare的时候调用 //弹幕会按时间顺序依次准备,准备完毕之后很快就会进行显示}@Overridepublic void releaseResource(BaseDanmaku danmaku) {//在弹幕显示完毕后会启动回收流程,这时会调用这个方法,可以进行资源释放}};

3. 弹幕控制

基本的start(),stop(),pause(),resume(),seekTo()使用都很简单不多讲了

这里要注意的是start(long position)seekTo(long position)的区别,start(position)移动后position之前的弹幕不会被清除,而seekTo会有一个清屏的效果,翻源码可以看到seekTo最终调用了一个清屏的方法,可以分情况使用这两种方式。

    @Overridepublic void reset() {if (danmakus != null)danmakus = new Danmakus();if (mRenderer != null)mRenderer.clear();    //这里清屏了}@Overridepublic void seek(long mills) {reset();    //resetmContext.mGlobalFlagValues.updateVisibleFlag();mContext.mGlobalFlagValues.updateFirstShownFlag();mContext.mGlobalFlagValues.updateSyncOffsetTimeFlag();mContext.mGlobalFlagValues.updatePrepareFlag();mRunningDanmakus = new Danmakus(Danmakus.ST_BY_LIST);mStartRenderTime = mills < 1000 ? 0 : mills;mRenderingState.reset();mRenderingState.endTime = mStartRenderTime;mLastBeginMills = mLastEndMills = 0;if (danmakuList != null) {BaseDanmaku last = danmakuList.last();if (last != null && !last.isTimeOut()) {mLastDanmaku = last;}}}

4. 数据解析

官方给的demo里是解析xml格式的文件,看的我一脸懵逼,当默默搞懂了官方数据类型及其含义时,反应过来我们的格式其实并不相同…

官方提供了json格式的解析器,将数据处理成InputStream,在初始化ILoader的时候使用A站格式即可:

    /*** 创建解析器对象,解析输入流** @param stream* @return*/private BaseDanmakuParser createParser(InputStream stream) {if (stream == null) {return new BaseDanmakuParser() {@Overrideprotected Danmakus parse() {return new Danmakus();}};}//A站是Json格式ILoader loader = DanmakuLoaderFactory.create(DanmakuLoaderFactory.TAG_ACFUN);try {loader.load(stream);} catch (IllegalDataException e) {e.printStackTrace();}MyDanmakuParaser parser = new MyDanmakuParaser(this);IDataSource<?> dataSource = loader.getDataSource();parser.load(dataSource);return parser;}

重写BaseDanmakuParser

public class MyDanmakuParaser extends BaseDanmakuParser {protected float mDispScaleX;protected float mDispScaleY;private Context context;public MyDanmakuParaser(Context context){this.context=context;}@Overrideprotected IDanmakus parse() {if (mDataSource != null) {JSONSource source = (JSONSource) mDataSource; //jsonSourceJSONArray jsonArray = source.data();IDanmakus result = new Danmakus(ST_BY_TIME, false, mContext.getBaseComparator());    for (int i = 0; i < jsonArray.length(); i++) {    //在这里将数据取出来设置进去BaseDanmaku danmaku =  mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_LR, mContext);danmaku.padding = MainActivity.dp2px(context,10);danmaku.priority = 0;danmaku.textSize = MainActivity.dp2px(context,15);danmaku.textColor = Color.WHITE;danmaku.setTimer(mTimer);
//                danmaku.borderColor=Color.RED;danmaku.index=i;try {JSONObject object = jsonArray.getJSONObject(i);danmaku.text = object.optString("text", "");danmaku.setTime(object.optLong("time", 1000));danmaku.flags=mContext.mGlobalFlagValues;} catch (JSONException e) {e.printStackTrace();}result.addItem(danmaku);}return result;}return null;}//从time到最后的弹幕总数  加这个方法是因为我们有个需求是需要开屏显示XX条弹幕来袭public int getCount(long time){ return getDanmakus().sub(time,Integer.MAX_VALUE).size();}public BaseDanmaku getFirst(long time){    //获取第一条弹幕return getDanmakus().sub(time,Integer.MAX_VALUE).first();}@Overridepublic BaseDanmakuParser setDisplayer(IDisplayer disp) {super.setDisplayer(disp);mDispScaleX = mDispWidth / DanmakuFactory.BILI_PLAYER_WIDTH;mDispScaleY = mDispHeight / DanmakuFactory.BILI_PLAYER_HEIGHT;return this;}
}

可以看到弹幕解析主要是parase()这个方法,看源码可以发现,这个方法是在getDanmakus()中调用的,最终它是在danmakuView.prepare()方法中调用

也就是说,可以通过这两个方法更新整体弹幕数据,例如切换视频时可以重新设置弹幕数据并再次调用danmakuView.prepare()

5. 属性调整

以行数为例,原来显示3行,动态改为显示2行:

 if (danmakuView != null && danmakuContext != null) {HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_LR, 2); // 滚动弹幕最大显示3行danmakuContext.setMaximumLines(maxLinesPair);danmakuView.invalidate();    //调用重绘}

6. 弹幕倍速

danmakuView.setCallback(new DrawHandler.Callback() {@Overridepublic void updateTimer(DanmakuTimer timer) {//倍速if (danmakuSpeed != 1) {timer.add((long) (timer.lastInterval() * (danmakuSpeed - 1)));}}...});

这个倍速就很顺滑

Android DanmakuFlameMaster的踩坑方式相关推荐

  1. android 字体文件压缩,Android 字体使用踩坑指南

    Android 字体使用踩坑指南 最近项目改版,根据ui的设计,需要使用到三字体.在使用过程中遇到一些坑,于是有了这个避坑指南! 字体压缩 第一个坑!字体库的体积太大. 字体压缩的前提是要使用的内容是 ...

  2. Android Studio安装踩坑

    title: Android Studio安装踩坑 date: 2018-09-07 19:31:32 updated: tags: [Android,Android Studio,坑] descri ...

  3. Android google翻译踩坑之旅

    # Android google翻译踩坑之旅   最近由于工作需求,需要为游戏Android平台接入Google翻译的SDK,由于关于翻译的文章非常少,访问官方文档又需要翻墙,更可气的是找到的博客写了 ...

  4. Android BLE蓝牙踩坑总结

    简介 自从Android-BLE库开源了一段时间以来,越来越多的小伙伴问到了各种各样的关于BLE的奇怪问题,在这里我想跟大家分享一下本人对于Android BLE蓝牙的一些看法和解决方式,避免刚接触的 ...

  5. android gradle权威指南pdf_干货 | 携程 Android 10适配踩坑指南

    作者简介 曙光,携程资深软件工程师,负责市场营销相关研发及管理工作.2019 年 9 月 3 日,Google 发布了 Android 10 正式版.Android 10 聚焦移动创新.安全隐私和数字 ...

  6. Android LottieAnimation使用---踩坑篇

    LottieAnimation众所周知是Airbnb出的一个实现复杂动画的一个框架,可以在Android,IOS等平台使用.使用的步骤也非常简单.但本文不是讲解它的使用.Lottie虽然好用,但也不是 ...

  7. android Bitmap 处理踩坑记

    吐槽 新项目用到外接的摄像头,摄像头抓拍后调用opencv做算法分析,总是运行一会直接jni error导致程序crash,试了好多方法结果发现是对接的摄像头多的原因(200万像素的3个摄像头一直开着 ...

  8. android 人脸识别踩坑实录

    随着AI技术的发展,人脸识别的应用场景越来越多,提供技术支持的API也有好多可以选择,但是大部分都是需要收费的,或者免费试用.由此可见人脸识别算法确实是核心技术,不是随便就可以获取到的.经过多次尝试, ...

  9. Android ijkplayer 编译踩坑与记录(ijk0.8.8--ffmpeg4.0)

    本文记录了 ijkplayer 的编译完整流程,以及编译过程中遇到的坑及解决方案,如果不想自己编译,可以使用我编译好的结果.已放入 github: ijkplayer_Build4Android 编译 ...

最新文章

  1. Nature 子刊:三代测序的DNA提取和宏基因组学分析
  2. [Java] Web开发环境搭建 - MyEclipse 篇
  3. Qt中多线程与界面组件的通信
  4. 机器人II(sdutoj2585)-JAVA
  5. C#让TopMost窗体弹出并置顶层但不获取当前输入焦点的终极办法
  6. 使用Azure Blob存储托管Maven工件
  7. SciTE文本编辑器初体验
  8. 学习CGI之前,需要配置阿帕奇---windows
  9. 清楚form表单数据的便捷jQuery之法
  10. 会扫地炒菜,将来机器人当钟点工
  11. 《Fortran 95 程序设计》阅读笔记一
  12. VARCHART XGantt系列教程:甘特图调度程序探究
  13. 内存泄漏、虚拟内存、物理内存的联系
  14. 商务网站建设与维护【15】
  15. 伪造微信语音文件的过程分析
  16. Matlab中(),[],与{}的用法区别
  17. 文心一言对于宣传文案理解
  18. 从100套真题中提炼而出的100个经典句子
  19. eclipse Luna 用tomct7 部署项目
  20. python中fact()是什么意思_python中fact函数是什么及如何使用?

热门文章

  1. 基于JavaWeb的影视评论网站设计与实现 毕业论文+项目源码及数据库文件
  2. 【ChatGPT】ChatGPT 能否取代程序员?
  3. golang扫描端口
  4. 虚拟化四路服务器,虚拟化强健平台 四款主流四路服务器精选
  5. 如何禁用笔记本电脑自带的键盘和触控板以及如何恢复
  6. GPT转MBR分区格式
  7. 微软官方720P、1080P高清测试视频短片下载
  8. Ajax面试题 | 前端
  9. 商务汇报PPT制作的七堂课-第三课:结构搭建
  10. 未来计算机的四大新技术是什么,汪成为院士:未来计算机技术发展四大动力