制作视频弹幕:DanmakuFlameMaster

现在在各大视频软件和直播平台上弹幕随处可见,也是必不可少的东西。所有我们就来制作一下简单的视频弹幕吧

基本功能:随机生成弹幕,可自定义发送弹幕,弹幕的颜色随机,透明度,大小随机,速度随机,弹幕可关闭可打开

如果有不好的地方请大佬们指教。希望能够帮助到你们哦

先看效果图吧:

视频弹幕交流怎么做 java 视频弹幕生成_ide


视频弹幕交流怎么做 java 视频弹幕生成_视频弹幕交流怎么做 java_02


视频弹幕交流怎么做 java 视频弹幕生成_控件_03

  1. 一:先修改布局文件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>
  1. 二:编写业务逻辑代码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"**>