Android弹幕实现:基于B站弹幕开源系统(3)

本文在附录1,2的基础上再次对异步获取弹幕并显示弹幕完善逻辑和代码,集中在上层Java代码部分:

package zhangphil.danmaku;import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.IDisplayer;
import master.flame.danmaku.danmaku.model.android.DanmakuContext;
import master.flame.danmaku.ui.widget.DanmakuView;public class MainActivity extends Activity {private DanmakuView mDanmakuView;private DanmakuContext mContext;private AcFunDanmakuParser mParser;private final int MAX_DANMAKU_LINES = 8; //弹幕在屏幕显示的最大行数private ScheduledThreadPoolExecutor mScheduledThreadPoolExecutor = null;private ConcurrentLinkedQueue<DanmakuMsg> mQueue = null; //所有的弹幕数据存取队列,在这里做线程的弹幕取和存private ArrayList<DanmakuMsg> danmakuLists = null;//每次请求最新的弹幕数据后缓存listprivate final int WHAT_GET_LIST_DATA = 0xffa01;private final int WHAT_DISPLAY_SINGLE_DANMAKU = 0xffa02;private final int BASE_TIME = 400;private final int BASE_TIME_ADD = 100;//标志文本弹幕的序列号//区别不同弹幕private static int danmakuTextMsgId = 0;private final int[] colors = {Color.RED, Color.YELLOW, Color.BLUE, Color.GREEN, Color.CYAN, Color.DKGRAY};private Handler mDanmakuHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case WHAT_GET_LIST_DATA:mDanmakuHandler.removeMessages(WHAT_GET_LIST_DATA);if (danmakuLists != null && !danmakuLists.isEmpty()) {mQueue.addAll(danmakuLists);danmakuLists.clear();if (!mQueue.isEmpty())mDanmakuHandler.sendEmptyMessage(WHAT_DISPLAY_SINGLE_DANMAKU);}break;case WHAT_DISPLAY_SINGLE_DANMAKU:mDanmakuHandler.removeMessages(WHAT_DISPLAY_SINGLE_DANMAKU);displayDanmaku();break;}}};/*** 弹幕数据封装的类(bean)*/private class DanmakuMsg {public String msg;}private void displayDanmaku() {boolean p = mDanmakuView.isPaused();//如果当前的弹幕由于Android生命周期的原因进入暂停状态,那么不应该不停的消耗弹幕数据//要知道,在这里发出一个handler消息,那么将会消费(删掉)ConcurrentLinkedQueue头部的数据if (!mQueue.isEmpty() && !p) {DanmakuMsg dm = mQueue.poll();if (!TextUtils.isEmpty(dm.msg)) {addDanmaku(dm.msg, true);}mDanmakuHandler.sendEmptyMessageDelayed(WHAT_DISPLAY_SINGLE_DANMAKU, (long) (Math.random() * BASE_TIME) + BASE_TIME_ADD);}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);danmakuLists = new ArrayList<>();mQueue = new ConcurrentLinkedQueue<>();mDanmakuView = (DanmakuView) findViewById(R.id.danmakuView);initDanmaku();mScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);GetDanmakuMessageTask mTask = new GetDanmakuMessageTask();//延迟0秒执行,每隔若干秒周期执行一次任务mScheduledThreadPoolExecutor.scheduleAtFixedRate(mTask, 0, 5, TimeUnit.SECONDS);Button show = (Button) findViewById(R.id.show);Button hide = (Button) findViewById(R.id.hide);Button sendText = (Button) findViewById(R.id.sendText);Button pause = (Button) findViewById(R.id.pause);Button resume = (Button) findViewById(R.id.resume);Button clear = (Button) findViewById(R.id.clear);show.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mDanmakuView.show();}});hide.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mDanmakuView.hide();}});sendText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//每点击一次按钮发送一条弹幕sendTextMessage();}});pause.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mDanmakuView.pause();}});resume.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mDanmakuView.resume();}});clear.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {clearDanmaku();}});}/*** 假设该线程任务模拟的就是从网络中取弹幕数据的耗时操作* 假设这些弹幕数据序列是有序的。*/private class GetDanmakuMessageTask implements Runnable {@Overridepublic void run() {danmakuLists.clear();int count = (int) (Math.random() * 50);for (int i = 0; i < count; i++) {DanmakuMsg message = new DanmakuMsg();message.msg = "弹幕:" + danmakuTextMsgId;danmakuLists.add(message);danmakuTextMsgId++;}if (!danmakuLists.isEmpty()) {Message msg = mDanmakuHandler.obtainMessage();msg.what = WHAT_GET_LIST_DATA;mDanmakuHandler.sendMessage(msg);}}}/*** 驱动弹幕显示机制重新运作起来*/private void resumeDanmaku() {if (!mQueue.isEmpty())mDanmakuHandler.sendEmptyMessageDelayed(WHAT_DISPLAY_SINGLE_DANMAKU, (int) (Math.random() * BASE_TIME) + BASE_TIME_ADD);}private void clearDanmaku() {if (danmakuLists != null && !danmakuLists.isEmpty()) {danmakuLists.clear();}if (mQueue != null && !mQueue.isEmpty())mQueue.clear();mDanmakuView.clearDanmakusOnScreen();mDanmakuView.clear();}private void initDanmaku() {mContext = DanmakuContext.create();// 设置最大显示行数HashMap<Integer, Integer> maxLinesPair = new HashMap<>();maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, MAX_DANMAKU_LINES); // 滚动弹幕最大显示5行// 设置是否禁止重叠HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<>();overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);//普通文本弹幕也描边设置样式//如果是图文混合编排编排,最后不要描边mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 10) //描边的厚度.setDuplicateMergingEnabled(false).setScrollSpeedFactor(1.2f) //弹幕的速度。注意!此值越小,速度越快!值越大,速度越慢。// by phil.setScaleTextSize(1.2f)  //缩放的值
//        .setCacheStuffer(new BackgroundCacheStuffer())  // 绘制背景使用BackgroundCacheStuffer.setMaximumLines(maxLinesPair).preventOverlapping(overlappingEnablePair);mParser = new AcFunDanmakuParser();mDanmakuView.prepare(mParser, mContext);//mDanmakuView.showFPS(true);mDanmakuView.enableDanmakuDrawingCache(true);if (mDanmakuView != null) {mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {@Overridepublic void updateTimer(DanmakuTimer timer) {}@Overridepublic void drawingFinished() {}@Overridepublic void danmakuShown(BaseDanmaku danmaku) {//Log.d("弹幕文本", "danmakuShown text=" + danmaku.text);}@Overridepublic void prepared() {mDanmakuView.start();}});}}private void sendTextMessage() {addDanmaku("zhangphil@csdn: " + System.currentTimeMillis(), true);}private void addDanmaku(CharSequence cs, boolean islive) {BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);if (danmaku == null || mDanmakuView == null) {return;}danmaku.text = cs;danmaku.padding = 5;danmaku.priority = 0;  // 可能会被各种过滤器过滤并隐藏显示danmaku.isLive = islive;danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);danmaku.textSize = 20f * (mParser.getDisplayer().getDensity() - 0.6f); //文本弹幕字体大小danmaku.textColor = getRandomColor(); //文本的颜色danmaku.textShadowColor = getRandomColor(); //文本弹幕描边的颜色//danmaku.underlineColor = Color.DKGRAY; //文本弹幕下划线的颜色danmaku.borderColor = getRandomColor(); //边框的颜色mDanmakuView.addDanmaku(danmaku);}@Overrideprotected void onPause() {super.onPause();if (mDanmakuView != null && mDanmakuView.isPrepared()) {mDanmakuView.pause();}}@Overrideprotected void onResume() {super.onResume();if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {mDanmakuView.resume();//重新启动handler消息机制,触发弹幕显示//如果没有这一个方法,那么显示弹幕的机制将失灵(失去驱动)//这个方法就是重新激发弹幕显示的handler机制。resumeDanmaku();}}private void closeGetDanmakuMessage() {if (mScheduledThreadPoolExecutor != null)mScheduledThreadPoolExecutor.shutdown();}@Overrideprotected void onDestroy() {super.onDestroy();if (mDanmakuView != null) {// dont forget release!mDanmakuView.release();mDanmakuView = null;}closeGetDanmakuMessage();}/*** 从一系列颜色中随机选择一种颜色** @return*/private int getRandomColor() {int i = ((int) (Math.random() * 10)) % colors.length;return colors[i];}
}

代码运行结果如图:

附录:
1,《Android弹幕实现:基于B站弹幕开源系统(1)》链接:http://blog.csdn.net/zhangphil/article/details/68067100
2,《Android弹幕实现:基于B站弹幕开源系统(2)》链接:http://blog.csdn.net/zhangphil/article/details/68114226
3,《Java ConcurrentLinkedQueue队列线程安全操作》链接:http://blog.csdn.net/zhangphil/article/details/65936066

Android弹幕实现:基于B站弹幕开源系统(3)-文本弹幕的完善和细节调整相关推荐

  1. Android气泡弹幕,Android弹幕实现:基于B站弹幕开源系统(7)QQ、微信聊天气泡样式的弹幕...

    Android弹幕实现:基于B站弹幕开源系统(7)QQ.微信聊天气泡样式的弹幕 在附录文章得基础上,改进普通文本弹幕,实现一种特殊效果的文本弹幕,像QQ.微信一样的带有气泡背景的弹幕.实现的重点是在S ...

  2. android实现b站弹幕,Android弹幕实现:基于B站弹幕开源系统(2)

    ?? Android弹幕实现:基于B站弹幕开源系统(2) 在附录1的基础上,模拟实现一种实际开发的应用场景:从网络中不间断的周期取弹幕数据,这些弹幕数据往往是批量的,然后把这些从网络中取到的批量数据逐 ...

  3. android+弹幕实现,Android弹幕实现:基于B站弹幕开源系统(1)

     Android弹幕实现:基于B站弹幕开源系统(1) 如今的视频播放,流行在视频上飘弹幕.这里面做的相对比较成熟.稳定.使用量较多的弹幕系统,当推B站的弹幕系统,B站的弹幕系统已经作为开源项目在g ...

  4. Android弹幕实现:基于B站弹幕开源系统(1)

     Android弹幕实现:基于B站弹幕开源系统(1) 如今的视频播放,流行在视频上飘弹幕.这里面做的相对比较成熟.稳定.使用量较多的弹幕系统,当推B站的弹幕系统,B站的弹幕系统已经作为开源项目在g ...

  5. python b站自动评论_用语音识别+python自动发送弹幕,变成B站野生字幕君吧!

    在B站观看视频的时候,有时候一些带有旁白但没有字幕的视频,会有野生字幕君出现,贴心的为视频加上字幕,大大提高了视频观感.不过手工加字幕是一件很麻烦的事,需要人工卡时间和听译打字.当一个懒人程序媛想当一 ...

  6. 利用B站《无价之姐》弹幕合成词云跳舞

    写在最前边: 很长时间没有来CSDN上来写博客了.最近看到有位UP主的微信公众号推出了通过爬取B站上弹幕生成词云,再将词云合成小姐姐跳舞的视频,并且这位博主还给出了实现思路和源码.照着那位博主的方法, ...

  7. js弹幕脚本(基于油猴)

    js弹幕脚本(基于油猴) 该脚本包含往视频上插入弹幕,发射弹幕,弹幕查询,弹幕暂停,脏话过滤等基础功能.话不多说 ,直接上代码. 仅供参考 ,该代码是我给别人写的定制化的,复制后不可用. // ==U ...

  8. Android 是Google开发的基于Linux平台的开源手机操作系统

    Android 是Google开发的基于Linux平台的开源手机操作系统(在华注册商标名为"安致").它摩托罗拉的首款Android手机CLIQ包括操作系统.用户界面和应用程序 - ...

  9. selenium、you-get下载B站视频、监控播放量和弹幕

    selenium.you-get下载B站视频.监控播放量和弹幕 说明 完整代码 运行结果 说明 前两段代码是使用you-get下载视频,第一段是说明.第三段代码爬取b站视频的标题+播放量+弹幕数.注意 ...

最新文章

  1. javascript实现tab切换的方法(2)
  2. 验证码 -图形图像识别的算法。http://blog.csdn.net/xtalk2008/archive/2007/11/01/1861310.aspx...
  3. DjangoAdmin站点调整列表页展示
  4. PJ2018T4 对称二叉树 树形结构
  5. r语言c5.0决策树算法参数,决策树算法CART、C5.0的R语言实现——(三)
  6. Mac OS X / IOS 内存管理机制简要说明
  7. kali 运行java_kali linux运行java程序
  8. centos7邮件服务器SSL配置
  9. 一根烟上热搜,先让 AI 看看你的肺
  10. 国内访问 Atom 源很慢 解决方案
  11. wordpress学习(四)---url伪静态简单了解
  12. 运维必读:避免故障、拒绝背锅的六大原则!
  13. [MSSQL] 数据库置疑怎么处理?
  14. 编程语言排行榜 - 2020年度最热门编程语言盘点!
  15. jQuery双指放大缩小页面内容
  16. 微软控制云服务器软件,服务器远程控制用什么软件
  17. Oracle 12.2 新特性--PDB和CDB的字符集可以不一样
  18. 纯手工采用java编写双色球、大乐透随机生成算法,真实模拟出球
  19. Python   pip安装selenium安装不了报错原因
  20. redirect失败的原因,Could not resolve view with name

热门文章

  1. Linux学习 脚本中常用的命令
  2. 让LYNC来次真格的国际长途呼叫
  3. java怎么读取excel思路_java读取本地excel数据
  4. Spring Security 5.0.x 参考手册 【翻译自官方GIT-2018.06.12】
  5. python里apply用法_Python apply函数的用法
  6. pandas的apply函数常见用法总结
  7. python绘图编程100例_编程语言入门经典100例【Python版】
  8. 蓝牙模块BC213 支持对接华为鸿蒙
  9. 决策树可视化工具——Graphviz安装(Windows下)
  10. 2014淘宝运营干货:网店从零起步