制作视频弹幕:DanmakuFlameMaster
现在在各大视频软件和直播平台上弹幕随处可见,也是必不可少的东西。所有我们就来制作一下简单的视频弹幕吧
基本功能:随机生成弹幕,可自定义发送弹幕,弹幕的颜色随机,透明度,大小随机,速度随机,弹幕可关闭可打开。
如果有不好的地方请大佬们指教。希望能够帮助到你们哦
先看效果图吧:
- 一:先修改布局文件activity_main.xml
基本实现的关键步骤:
(1)弹幕界面主要控件是:显示视频播放(VideoView控件),弹幕文本信息(DanmakuFlameMaster库),弹幕输入框(EditText控件),弹幕的发送按钮(Button控件)
(2)布局为相对布局,DanmakuFlameMaster库的添加:在build.gradle文件里添加依赖:compile ‘com.github.ctiao:DanmakuFlameMaster:0.9.25’
(3)DanmakuView可见性设置为不可见
(4)弹幕的输入框和发送按钮用一个线性布局,输入框的长度使用权重:android:layout_width=“0dp” android:layout_weight=“1” ,使它100%填充。
在build.gradle文件里添加依赖:
implementation 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
activity_main.xml的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000">
<VideoView
android:id="@+id/videoview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<master.flame.danmaku.ui.widget.DanmakuView
android:id="@+id/danmaku"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/ly_send"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_alignParentBottom="true"
android:background="#fff"
android:visibility="gone">
<Switch
android:id="@+id/open"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textOff="关"
android:textOn="开"
android:checked="true"/>
<EditText
android:id="@+id/et_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="发送" />
</LinearLayout>
</RelativeLayout>
- 二:编写业务逻辑代码MainActivity.java
(1)获取界面控件:initView()方法获取界面控件的实例,初始化界面控件。
(2)播放视频:playVideo()方法:获取视频的uri(路径);如果uri不为空,解析该视频,将String类型解析为uri类型,start开始播放,setAlpha():设置透明度
(3)随机生成与添加弹幕:generateDanmakus()方法生成弹幕:添加线程:开子线程,重写run()方法,生成0-299的正整数添加到内容,中,addDanmaku(content, false)添加到屏幕,不显示边框;addDanmaku()方法添加弹幕:创建弹幕对象; TYPE_SCROLL_RL:表示从右向左滚动的弹幕;设置弹幕的内容,内边距,字体大小,颜色,设置时间为当时时间;判断弹幕是否有边框,如果有边框就设置边框颜色,最后将该条弹幕显示在屏幕上。
(4)创建弹幕解析器:全局的BaseDanmakuParser。
(5)初始化弹幕:调用了setCallback()方法来设置回调函数,prepared()方法:显示弹幕,调用start()和generateDanmakus()方法开始随机生成弹幕并显示;调用DanmakuContext.create()方法创建了一个DanmakuContext的实例,DanmakuContext可以用于对弹幕的各种全局配置进行设定,如设置字体、设置最大显示行;调用了enableDanmakuDrawingCache()方法来提升绘制效率;调用DanmakuView的prepare()方法进行弹幕准备;给DanmakuView设置了一个点击事件,当点击屏幕时就会触发这个点击事件。然后进行判断,如果操作界面是隐藏的就将它显示出来,如果操作界面是显示的就将它隐藏掉;给发送按钮注册了一个点击事件,当点击发送时,获取EditText中的输入内容,然后调用addDanmaku()方法将这条消息添加到DanmakuView上。另外,这条弹幕是由我们自己发送的,因此addDanmaku()方法的第二个参数要传入true。 附加功能的实现逻辑:
1.弹幕的开关:首先在布局文件中加入Switch控件,默认状态为开启状态;在MainActivity中:在initDanmaku()方法中设置开关监控回调:实现CompoundButton.OnCheckedChangeListener接口,并实现其内部的onCheckedChanged方法;if语句判断开关的状态:danmakuView.show()方法//打开弹幕,danmakuView.hide()//关闭弹幕;
2.多种颜色的弹幕:在MainActivity中:修改addDanmaku方法中的字体颜色的设置:实例化Random的对象;设置颜色值为Color.rgb(random.nextInt(256),random.nextInt(256), random.nextInt(256)) //rgb的值在0-255之间,三个值分别为红,绿,蓝的数值;
3.弹幕透明度:在上一条上面修改:danmaku.textColor = Color.argb(random.nextInt(256),random.nextInt(256),random.nextInt(256), random.nextInt(256));
4.弹幕的字体大小不同:danmaku.textSize =(random.nextInt(15) + 35)生成15-35的随机数;
5.速度随机:danmaku.duration.setFactor((float)Math.random()*5);//滚动系数越小越快
MainActivity.java代码如下:
package com.example.barrage;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Switch;
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 showDanmaku;
private DanmakuView danmakuView;
private DanmakuContext danmakuContext; // (Context:上下文,运行的环境)
private Button sendButton;
private LinearLayout sendLayout;
private EditText editText;
private VideoView videoView;
private Switch open;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView(); //初始化界面控件
playVideo(); //播放视频
initDanmaku();
}
/**
* 初始化界面控件
*/
private void initView() {
videoView = (VideoView) findViewById(R.id.videoview);
sendLayout = (LinearLayout) findViewById(R.id.ly_send);
sendButton = (Button) findViewById(R.id.btn_send);
editText = (EditText) findViewById(R.id.et_text);
danmakuView = (DanmakuView) findViewById(R.id.danmaku);
open=(Switch) findViewById(R.id.open);
}
/**
* 播放视频
*/
private void playVideo() {
//视频地址
String uri = "android.resource://" + getPackageName() + "/" + R.raw.yuan;
if (uri != null) {
videoView.setVideoURI(Uri.parse(uri)); //解析视频
videoView.start(); //播放视频
} else {
videoView.getBackground().setAlpha(0);//将背景设为透明,透明度:0-255
}
}
/**
* 创建弹幕解析器
*/
private BaseDanmakuParser parser = new BaseDanmakuParser() {
@Override
protected IDanmakus parse() {
return new Danmakus();
}
};
/**
* 初始化弹幕
*/
private void initDanmaku() {
danmakuView.setCallback(new DrawHandler.Callback() {//设置回调函数
@Override
public void prepared() {
showDanmaku = true;
danmakuView.start(); //开始弹幕
generateDanmakus(); //调用随机生成弹幕方法
}
@Override
public void updateTimer(DanmakuTimer timer) {
}
@Override
public void danmakuShown(BaseDanmaku danmaku) {
}
@Override
public void drawingFinished() {
}
});
danmakuContext = DanmakuContext.create();
danmakuView.enableDanmakuDrawingCache(true);//提升屏幕绘制效率(Cache:缓存)
danmakuView.prepare(parser, danmakuContext);//进行弹幕准备(parser:解析器)
//为danmakuView设置点击事件
danmakuView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (sendLayout.getVisibility() == View.GONE) {
sendLayout.setVisibility(View.VISIBLE);//显示布局
} else {
sendLayout.setVisibility(View.GONE); //隐藏布局
}
}
});
//为发送按钮设置点击事件
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String content = editText.getText().toString();
if (!TextUtils.isEmpty(content)) {
addDanmaku(content, true);//添加一条弹幕
editText.setText("");
}
}
});
// 弹幕的开关
open.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
// TODO Auto-generated method stub
if (isChecked) {
danmakuView.show();//打开弹幕
} else {
danmakuView.hide();// 关闭弹幕
}
}
});
}
/**
* 添加一条弹幕
*
* @param content 弹幕的具体内容
* @param border 弹幕是否有边框
*/
private void addDanmaku(String content, boolean border) {
//创建弹幕对象,TYPE_SCROLL_RL表示从右向左滚动的弹幕 TYPE_SCROLL_LR:从左向右(context上下文)
BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(
BaseDanmaku.TYPE_SCROLL_LR);
danmaku.text = content;
danmaku.padding = 6;
Random random = new Random();
danmaku.textSize =(random.nextInt(15) + 35);
danmaku.textColor = Color.argb(random.nextInt(256),random.nextInt(256),random.nextInt(256), random.nextInt(256));
// danmaku.textColor = Color.rgb(random.nextInt(256),random.nextInt(256), random.nextInt(256));//弹幕文字的颜色
// 速度
// danmaku.duration.setFactor((float)Math.random()*5);//滚动系数越小越快
danmaku.setTime(danmakuView.getCurrentTime()); //设置时间为当前时间
if (border) {
danmaku.borderColor = Color.RED;//弹幕文字边框的颜色
}
danmakuView.addDanmaku(danmaku); //添加一条弹幕
}
/**
* 随机生成一些弹幕内容
*/
private void generateDanmakus() { //generate 生成
new Thread(new Runnable() { //子线程
@Override
public void run() {
while (showDanmaku) {
int num = new Random().nextInt(300); //生成0-299的正整数
String content = "" + num;
addDanmaku(content, false);
try {
Thread.sleep(num); //休眠299毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
protected void onPause() {
super.onPause();
if (danmakuView != null && danmakuView.isPrepared()) {
danmakuView.pause();
}
}
@Override
protected void onResume() {
super.onResume();
if (danmakuView != null && danmakuView.isPrepared() &&
danmakuView.isPaused()) {
danmakuView.resume();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
showDanmaku = false;
if (danmakuView != null) {
danmakuView.release();
danmakuView = null;
}
}
}
自定义APP图标和视频横屏播放在AndroidMainfast.xml
android:icon="@mipmap/barrage_icon"
<activity android:name=".MainActivity" **android:screenOrientation="landscape"**>