现在网络直播越来越火,网络主播也逐渐成为一种新兴职业,对于网络直播,弹幕功能是必须要有的,如下图:

首先来分析一下,这个弹幕功能是怎么实现的,首先在最下面肯定是一个游戏界面View,然后游戏界面上有弹幕View,弹幕的View必须要做成完全透明的,这样即使覆盖在游戏界面的上方也不会影响到游戏的正常观看,只有当有人发弹幕消息时,再将消息绘制到弹幕的View上面就可以了,下方肯定还有有操作界面View,可以让用户来发弹幕和送礼物的功能,原理示意图如下所示:

参照原理图,下面一步一步来实现这个功能。

实现视频的播放

activity_main.xml

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000"><VideoViewandroid:id="@+id/video_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"/>
</RelativeLayout>

MainActivity.java

package com.jackie.bombscreen;import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.VideoView;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);VideoView videoView = (VideoView) findViewById(R.id.video_view);videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/xiaoxingyun.mp4");videoView.start();}@Overridepublic void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);if (hasFocus && Build.VERSION.SDK_INT >= 19) {View decorView = getWindow().getDecorView();decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}}
}

最后别忘了设置AndroidMainfest.xml

效果如下:

实现弹幕的效果

接下来我们开始实现弹幕效果。弹幕其实也就是一个自定义的View,它的上面可以显示类似于跑马灯的文字效果。观众们发表的评论都会在弹幕上显示出来,但又会很快地移出屏幕,既可以起到互动的作用,同时又不会影响视频的正常观看。

我们可以自己来编写这样的一个自定义View,当然也可以直接使用网上现成的开源项目。那么为了能够简单快速地实现弹幕效果,这里我就准备直接使用由哔哩哔哩开源的弹幕效果库DanmakuFlameMaster。

DanmakuFlameMaster库的项目主页地址是:https://github.com/Bilibili/DanmakuFlameMaster

添加build.gradle依赖

compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3'
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000"><VideoViewandroid:id="@+id/video_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"/><master.flame.danmaku.ui.widget.DanmakuViewandroid:id="@+id/danmaku_view"android:layout_width="match_parent"android:layout_height="match_parent" />
</RelativeLayout>

修改MainActivity.java

package com.jackie.bombscreen;import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.VideoView;import java.util.Random;import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.android.DanmakuContext;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.ui.widget.DanmakuView;public class MainActivity extends AppCompatActivity {private boolean mIsShowDanmaku;private DanmakuView mDanmakuView;private DanmakuContext mDanmakuContext;private BaseDanmakuParser parser = new BaseDanmakuParser() {@Overrideprotected IDanmakus parse() {return new Danmakus();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);VideoView videoView = (VideoView) findViewById(R.id.video_view);videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/xiaoxingyun.mp4");videoView.start();mDanmakuView = (DanmakuView) findViewById(R.id.danmaku_view);mDanmakuView.enableDanmakuDrawingCache(true);mDanmakuView.setCallback(new DrawHandler.Callback() {@Overridepublic void prepared() {mIsShowDanmaku = true;mDanmakuView.start();generateSomeDanmaku();}@Overridepublic void updateTimer(DanmakuTimer timer) {}@Overridepublic void danmakuShown(BaseDanmaku danmaku) {}@Overridepublic void drawingFinished() {}});mDanmakuContext = DanmakuContext.create();mDanmakuView.prepare(parser, mDanmakuContext);}/*** 向弹幕View中添加一条弹幕* @param content       弹幕的具体内容* @param  withBorder   弹幕是否有边框*/private void addDanmaku(String content, boolean withBorder) {BaseDanmaku danmaku = mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);danmaku.text = content;danmaku.padding = 5;danmaku.textSize = sp2px(20);danmaku.textColor = Color.WHITE;danmaku.setTime(mDanmakuView.getCurrentTime());if (withBorder) {danmaku.borderColor = Color.GREEN;}mDanmakuView.addDanmaku(danmaku);}/*** 随机生成一些弹幕内容以供测试*/private void generateSomeDanmaku() {new Thread(new Runnable() {@Overridepublic void run() {while(mIsShowDanmaku) {int time = new Random().nextInt(300);String content = "" + time + time;addDanmaku(content, false);try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}/*** sp转px的方法。*/public int sp2px(float spValue) {final float fontScale = getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale + 0.5f);}@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();}}@Overrideprotected void onDestroy() {super.onDestroy();mIsShowDanmaku = false;if (mDanmakuView != null) {mDanmakuView.release();mDanmakuView = null;}}@Overridepublic void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);if (hasFocus && Build.VERSION.SDK_INT >= 19) {View decorView = getWindow().getDecorView();decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}}
}

效果图如下:

加入操作界面

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#000"><VideoViewandroid:id="@+id/video_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"/><master.flame.danmaku.ui.widget.DanmakuViewandroid:id="@+id/danmaku_view"android:layout_width="match_parent"android:layout_height="match_parent" /><LinearLayoutandroid:id="@+id/operation_layout"android:layout_width="match_parent"android:layout_height="50dp"android:layout_alignParentBottom="true"android:background="#fff"android:visibility="gone"><EditTextandroid:id="@+id/edit_text"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1" /><Buttonandroid:id="@+id/send"android:layout_width="wrap_content"android:layout_height="match_parent"android:text="Send" /></LinearLayout>
</RelativeLayout>
package com.jackie.bombscreen;import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.VideoView;import java.util.Random;import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
import master.flame.danmaku.danmaku.model.DanmakuTimer;
import master.flame.danmaku.danmaku.model.IDanmakus;
import master.flame.danmaku.danmaku.model.android.DanmakuContext;
import master.flame.danmaku.danmaku.model.android.Danmakus;
import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
import master.flame.danmaku.ui.widget.DanmakuView;public class MainActivity extends AppCompatActivity {private boolean mIsShowDanmaku;private DanmakuView mDanmakuView;private DanmakuContext mDanmakuContext;private BaseDanmakuParser parser = new BaseDanmakuParser() {@Overrideprotected IDanmakus parse() {return new Danmakus();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);VideoView videoView = (VideoView) findViewById(R.id.video_view);videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/xiaoxingyun.mp4");videoView.start();mDanmakuView = (DanmakuView) findViewById(R.id.danmaku_view);mDanmakuView.enableDanmakuDrawingCache(true);mDanmakuView.setCallback(new DrawHandler.Callback() {@Overridepublic void prepared() {mIsShowDanmaku = true;mDanmakuView.start();generateSomeDanmaku();}@Overridepublic void updateTimer(DanmakuTimer timer) {}@Overridepublic void danmakuShown(BaseDanmaku danmaku) {}@Overridepublic void drawingFinished() {}});mDanmakuContext = DanmakuContext.create();mDanmakuView.prepare(parser, mDanmakuContext);final LinearLayout operationLayout = (LinearLayout) findViewById(R.id.operation_layout);final Button send = (Button) findViewById(R.id.send);final EditText editText = (EditText) findViewById(R.id.edit_text);mDanmakuView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (operationLayout.getVisibility() == View.GONE) {operationLayout.setVisibility(View.VISIBLE);} else {operationLayout.setVisibility(View.GONE);}}});send.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {String content = editText.getText().toString();if (!TextUtils.isEmpty(content)) {addDanmaku(content, true);editText.setText("");}}});getWindow().getDecorView().setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() {@Overridepublic void onSystemUiVisibilityChange(int visibility) {if (visibility == View.SYSTEM_UI_FLAG_VISIBLE) {onWindowFocusChanged(true);}}});}/*** 向弹幕View中添加一条弹幕* @param content       弹幕的具体内容* @param  withBorder   弹幕是否有边框*/private void addDanmaku(String content, boolean withBorder) {BaseDanmaku danmaku = mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);danmaku.text = content;danmaku.padding = 5;danmaku.textSize = sp2px(20);danmaku.textColor = Color.WHITE;danmaku.setTime(mDanmakuView.getCurrentTime());if (withBorder) {danmaku.borderColor = Color.GREEN;}mDanmakuView.addDanmaku(danmaku);}/*** 随机生成一些弹幕内容以供测试*/private void generateSomeDanmaku() {new Thread(new Runnable() {@Overridepublic void run() {while(mIsShowDanmaku) {int time = new Random().nextInt(300);String content = "" + time + time;addDanmaku(content, false);try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}/*** sp转px的方法。*/public int sp2px(float spValue) {final float fontScale = getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale + 0.5f);}@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();}}@Overrideprotected void onDestroy() {super.onDestroy();mIsShowDanmaku = false;if (mDanmakuView != null) {mDanmakuView.release();mDanmakuView = null;}}@Overridepublic void onWindowFocusChanged(boolean hasFocus) {super.onWindowFocusChanged(hasFocus);if (hasFocus && Build.VERSION.SDK_INT >= 19) {View decorView = getWindow().getDecorView();decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| View.SYSTEM_UI_FLAG_FULLSCREEN| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}}
}

效果图如下:

自己发的弹幕有绿色边框,很容易区分。

基本上实现了弹幕的功能,当然,里面的知识点还有很多,这只是最基本的功能。有时间的话,建议学学DanmakuFlameMaster,里面还有很多炫酷的功能。

Android仿网络直播弹幕功能的实现相关推荐

  1. android点赞取消赞功能吗,Android 仿微博的点赞功能的实现原理(持续点赞再取消)...

    搜索热词 产品需求,实现类似微博的持续点赞再取消功能,因为自己也偶尔刷微博,对这功能有一定的使用上的了解, 至于微博点赞的具体实现我并不知道,微博点赞在断网的情况下依然能点赞,不会提示网络异常,等有网 ...

  2. android 仿微信demo————登录功能实现(移动端)

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  3. android 仿微信demo————注册功能完善添加头像功能(移动端)

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  4. android 仿微信demo————登录功能实现(服务端)

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  5. android 仿微信聊天界面 以及语音录制功能,Android仿微信录制语音功能

    本文实例为大家分享了Android仿微信录制语音的具体代码,供大家参考,具体内容如下 前言 我把录音分成了两部分 1.UI界面,弹窗读秒 2.一个类(包含开始.停止.创建文件名功能) 第一部分 由于6 ...

  6. android 仿微信demo————注册功能实现(服务端)

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  7. android 仿微信demo————注册功能完善添加头像功能(服务端)

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  8. android仿音乐播放器,Android仿音乐播放器功能

    本文实例为大家分享了Android仿音乐播放器功能的具体代码,供大家参考,具体内容如下 读取本地音乐文件 源代码: import android.media.MediaPlayer; import a ...

  9. Android仿斗鱼直播的弹幕效果

    今天,我就带着大家一起来实现一个简单的Android端弹幕效果. 分析 首先我们来看一下斗鱼上的弹幕效果,如下图所示: 这是一个Dota2游戏直播的界面,我们可以看到,在游戏界面的上方有很多的弹幕,看 ...

  10. android的实现关注好友功能,android仿微信好友列表功能

    android studio实现微信好友列表功能,注意有一个jar包我没有放上来,请大家到MainActivity中的那个网址里面下载即可,然后把pinyin4j-2.5.0.jar复制粘贴到项目的a ...

最新文章

  1. vim编辑器----常用命令
  2. Oracle表空间文件损坏后的排查及解决
  3. python绘制3d图形-python matlibplot绘制3D图形
  4. ASP长文章内容自动分页函数
  5. bzoj1055 [HAOI2008]玩具取名 区间DP
  6. Element UI的Table用法
  7. python 队列与栈的实现
  8. 重读经典:《Generative Adversarial Nets》
  9. Jenkins 安装FAQ
  10. 大数据的岗位职责,我们未来的大数据职业选择有哪些
  11. Duplicate class com.blankj.utilcode.BuildConfig found in modules classes.jar
  12. html中电脑自动输入,电脑一直自动打字怎么办
  13. 大白菜U盘PE重装系统
  14. 共享锁和排他锁的区别
  15. 阿里云将全面提供IPv6服务 | 中国制造业正被双向挤压,需智能化发展
  16. word文档的生成以及echarts图片的插入
  17. appinventor认识
  18. node-sass安装失败 error D:\xxx\xxx\node_modules\node-sass: Command failed.(window + mac M1 pro)
  19. 【五子棋AI循序渐进】发布一个完整的有一定棋力的版本(含源码)
  20. html原生轮播图的实现,使用原生js实现点击切换图片(轮播图)效果

热门文章

  1. Rk 平台显示屏调试
  2. 以太坊智能合约开发语言 - Solidity
  3. 刷B站学数分Day1|如何写出一份合格的数据分析师简历
  4. 卷积神经网络中感受野的详细介绍
  5. python用空格隔开每一个字符_Python(字符串操作实例1)一个字符串用空格隔开
  6. MYSQL中出现 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction的问题解决
  7. 低代码掀起“数字革命”,引领制造业数字化转型
  8. 修改植物大战僵尸数据
  9. 数论——Baby Step Giant Step大步小步算法
  10. IAM之Tivoli Identity Manager(二)