1.原理:
我们只需要首先在布局中放置一个显示游戏界面的View,然后在游戏界面的上方再覆盖一个显示弹幕的View就可以了。弹幕的View必须要做成完全透明的,这样即使覆盖在游戏界面的上方也不会影响到游戏的正常观看,只有当有人发弹幕消息时,再将消息绘制到弹幕的View上面就可以了。示意图如下所示:
但是我们除了要能看到弹幕之外也要能发弹幕才行,因此还要再在弹幕的View上面再覆盖一个操作界面的View,然后我们就可以在操作界面上发弹幕、送礼物等。示意图如下所示:
但是我们除了要能看到弹幕之外也要能发弹幕才行,因此还要再在弹幕的View上面再覆盖一个操作界面的View,然后我们就可以在操作界面上发弹幕、送礼物等。示意图如下所示:
2.MainActivity.java:
package com.example.administrator.danmu; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.MediaController; 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 VideoView videoView; private boolean showDanmaku; private DanmakuView danmakuView; private DanmakuContext danmakuContext; private BaseDanmakuParser parser=new BaseDanmakuParser() { // BaseDanmakuParser 弹幕的解析器 @Override protected IDanmakus parse() { return new Danmakus(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //视频View videoView=(VideoView)findViewById(R.id.video_view); videoView.setMediaController(new MediaController(this)); //控制播放与暂停的控件 //Uri是统一资源标识符,作用:根据这个Uri找到某个资源文件,实际上就是生成了一个路径 Uri rawUri=Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.waiting_for_love); videoView.setVideoURI(rawUri); videoView.start(); //弹幕View danmakuView=(DanmakuView)findViewById(R.id.danmaku_view); danmakuView.enableDanmakuDrawingCache(true); //提升绘制效率 danmakuView.setCallback(new DrawHandler.Callback() { // 调用setCallback()方法设置回调函数 @Override public void prepared() { showDanmaku=true; danmakuView.start(); generateSomeDanmaku(); } @Override public void updateTimer(DanmakuTimer timer) { } @Override public void danmakuShown(BaseDanmaku danmaku) { } @Override public void drawingFinished() { } }); danmakuContext=DanmakuContext.create(); //创建DanmakuContext的实例 danmakuView.prepare(parser,danmakuContext); //prepare()进行准备完成后,自动调用刚设置的回调函数中的prepared()方法。->DanmakuView就可以正常工作了 //操作界面View 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); danmakuView.setOnClickListener(new View.OnClickListener() { //点击屏幕,操作界面若隐藏,就显示,若显示,就隐藏 @Override public void onClick(View v) { if(operationLayout.getVisibility()==View.GONE){ operationLayout.setVisibility(View.VISIBLE); }else { operationLayout.setVisibility(View.GONE); } } }); send.setOnClickListener(new View.OnClickListener() { //自己编辑弹幕并发送 @Override public void onClick(View v) { String content=editText.getText().toString(); if(!TextUtils.isEmpty(content)){ addDanmaku(content,true); //传入true自己的弹幕就会带框 editText.setText(""); } } }); getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { //保证程序一直处于沉浸式模式 @Override public void onSystemUiVisibilityChange(int visibility) { if(visibility==View.SYSTEM_UI_FLAG_VISIBLE){ onWindowFocusChanged(true); } } }); } @Override public 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); } } private void addDanmaku(String content,boolean withBorder){ //添加弹幕消息 //创建BaseDanmaku的实例 TYPE_SCROLL_RL:从右向左滚动的弹幕 BaseDanmaku danmaku=danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); danmaku.text=content; danmaku.padding=5; danmaku.textSize=sp2px(20); danmaku.textColor= Color.WHITE; danmaku.setTime(danmakuView.getCurrentTime()); if(withBorder){ //判断弹幕是否有边框 danmaku.borderColor=Color.GREEN; } danmakuView.addDanmaku(danmaku); } private void generateSomeDanmaku(){ //随机生成一些弹幕效果 new Thread(new Runnable() { @Override public void run() { while (showDanmaku){ int time=new Random().nextInt(300); String content=""+time+time; addDanmaku(content,false); try{ Thread.sleep(time); }catch (InterruptedException e){ e.printStackTrace(); } } } }).start(); } public int sp2px(float spValue){ //sp转px的方法 float fontScale=getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale +0.5f); } @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; } } }
3.activityy_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.administrator.danmu.MainActivity"> <VideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true"/> <master.flame.danmaku.ui.widget.DanmakuView android:id="@+id/danmaku_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:id="@+id/operation_layout" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="50dp" android:layout_alignParentBottom="true" android:background="#fff" > <EditText android:id="@+id/edit_text" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="match_parent" android:text="Send" android:textAllCaps="false"/> </LinearLayout> </RelativeLayout>
4.AndroidMainfest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<activity android:name=".MainActivity" android:screenOrientation="landscape" //横屏显示 android:configChanges="orientation|keyboardHidden|screenLayout|screenSize">
5.build.gradle:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.3.0' compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3' //添加依赖即可使用开源库 }直接使用由哔哩哔哩开源的弹幕效果库DanmakuFlameMaster。
6.最终效果:
弹幕效果
编辑界面
发送界面
显示界面
本文源自郭神的文章。