彈幕實(shí)現(xiàn)原理

如今直播行業(yè)確實(shí)是非常火爆啊刻伊,大大小小的公司都要涉足一下直播的領(lǐng)域露戒,用斗魚的話來(lái)講椒功,現(xiàn)在就是千播之戰(zhàn)。而彈幕則無(wú)疑是直播功能當(dāng)中最為重要的一個(gè)功能之一智什,那么今天动漾,我就帶著大家一起來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Android端彈幕效果。

分析

首先我們來(lái)看一下斗魚上的彈幕效果荠锭,如下圖所示:

這是一個(gè)Dota2游戲直播的界面旱眯,我們可以看到,在游戲界面的上方有很多的彈幕证九,看直播的觀眾們就是在這里進(jìn)行討論的删豺。

那么這樣的一個(gè)界面該如何實(shí)現(xiàn)呢?其實(shí)并不復(fù)雜愧怜,我們只需要首先在布局中放置一個(gè)顯示游戲界面的View呀页,然后在游戲界面的上方再覆蓋一個(gè)顯示彈幕的View就可以了。彈幕的View必須要做成完全透明的拥坛,這樣即使覆蓋在游戲界面的上方也不會(huì)影響到游戲的正常觀看,只有當(dāng)有人發(fā)彈幕消息時(shí)猜惋,再將消息繪制到彈幕的View上面就可以了丸氛。原理示意圖如下所示:

但是我們除了要能看到彈幕之外也要能發(fā)彈幕才行梨撞,因此還要再在彈幕的View上面再覆蓋一個(gè)操作界面的View,然后我們就可以在操作界面上發(fā)彈幕螃成、送禮物等偿曙。原理示意圖如下所示:

這樣我們就把基本的實(shí)現(xiàn)原理分析完了竿秆,下面就讓我們開始一步步實(shí)現(xiàn)吧傅是。

實(shí)現(xiàn)視頻播放

由于本篇文章的主題是實(shí)現(xiàn)彈幕效果谎懦,并不涉及直播的任何其他功能,因此這里我們就簡(jiǎn)單地使用VideoView播放一個(gè)本地視頻來(lái)模擬最底層的游戲界面截碴。

首先使用Android Studio新建一個(gè)DanmuTest項(xiàng)目,然后修改activity_main.xml中的代碼丙躏,如下所示:

布局文件的代碼非常簡(jiǎn)單废恋,只有一個(gè)VideoView该编,我們將它設(shè)置為居中顯示喜颁。

然后修改MainActivity中的代碼,如下所示:

上面的代碼中使用了VideoView的最基本用法。在onCreate()方法中獲取到了VideoView的實(shí)例尝江,給它設(shè)置了一個(gè)視頻文件的地址,然后調(diào)用start()方法開始播放拦耐。當(dāng)然火脉,我事先已經(jīng)在SD的根目錄中準(zhǔn)備了一個(gè)叫Pixels.mp4的視頻文件。

這里使用到了SD卡的功能,但是為了代碼簡(jiǎn)單起見(jiàn)种吸,我并沒(méi)有加入運(yùn)行時(shí)權(quán)限的處理恩闻,因此一定要記得將你的項(xiàng)目的targetSdkVersion指定成23以下。

另外褂微,為了讓視頻播放可以有最好的體驗(yàn)效果宠蚂,這里使用了沉浸式模式的寫法求厕。對(duì)沉浸式模式還不理解的朋友可以參考我的上一篇文章Android狀態(tài)欄微技巧,帶你真正理解沉浸式模式币厕。

最后旱函,我們?cè)贏ndroidManifest.xml中將Activity設(shè)置為橫屏顯示并加入權(quán)限聲明券腔,如下所示:

OK,現(xiàn)在可以運(yùn)行一下項(xiàng)目了拘泞,程序啟動(dòng)之后就會(huì)自動(dòng)開始播放視頻纷纫,效果如下圖所示:

這樣我們就把第一步的功能實(shí)現(xiàn)了。

實(shí)現(xiàn)彈幕效果

接下來(lái)我們開始實(shí)現(xiàn)彈幕效果陪腌。彈幕其實(shí)也就是一個(gè)自定義的View辱魁,它的上面可以顯示類似于跑馬燈的文字效果。觀眾們發(fā)表的評(píng)論都會(huì)在彈幕上顯示出來(lái)诗鸭,但又會(huì)很快地移出屏幕染簇,既可以起到互動(dòng)的作用,同時(shí)又不會(huì)影響視頻的正常觀看强岸。

我們可以自己來(lái)編寫這樣的一個(gè)自定義View锻弓,當(dāng)然也可以直接使用網(wǎng)上現(xiàn)成的開源項(xiàng)目。那么為了能夠簡(jiǎn)單快速地實(shí)現(xiàn)彈幕效果蝌箍,這里我就準(zhǔn)備直接使用由嗶哩嗶哩開源的彈幕效果庫(kù)DanmakuFlameMaster了青灼。

DanmakuFlameMaster庫(kù)的項(xiàng)目主頁(yè)地址是:https://github.com/Bilibili/DanmakuFlameMaster

話說(shuō)現(xiàn)在使用Android Studio來(lái)引入一些開源庫(kù)真的非常方便,只需要在build.gradle文件里面添加開源庫(kù)的依賴就可以了妓盲。那么我們修改app/build.gradle文件聚至,并在dependencies閉包中添加如下依賴:

這樣我們就將DanmakuFlameMaster庫(kù)引入到當(dāng)前項(xiàng)目中了。然后修改activity_main.xml中的代碼本橙,如下所示:

可以看到扳躬,這里在RelativeLayout中加入了一個(gè)DanmakuView控件,這個(gè)控件就是用于顯示彈幕信息的了。注意一定要將DanmakuView寫在VideoView的下面贷币,因?yàn)镽elativeLayout中后添加的控件會(huì)被覆蓋在上面击胜。

接下來(lái)修改MainActivity中的代碼,我們?cè)谶@里加入彈幕顯示的邏輯役纹,如下所示:

publicclassMainActivityextendsAppCompatActivity{privatebooleanshowDanmaku;privateDanmakuView danmakuView;privateDanmakuContext danmakuContext;privateBaseDanmakuParser parser =newBaseDanmakuParser() {@OverrideprotectedIDanmakusparse() {returnnewDanmakus();

}

};@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

VideoView videoView = (VideoView) findViewById(R.id.video_view);

videoView.setVideoPath(Environment.getExternalStorageDirectory() +"/Pixels.mp4");

videoView.start();

danmakuView = (DanmakuView) findViewById(R.id.danmaku_view);

danmakuView.enableDanmakuDrawingCache(true);

danmakuView.setCallback(newDrawHandler.Callback() {@Overridepublicvoidprepared() {

showDanmaku =true;

danmakuView.start();

generateSomeDanmaku();

}@OverridepublicvoidupdateTimer(DanmakuTimer timer) {

}@OverridepublicvoiddanmakuShown(BaseDanmaku danmaku) {

}@OverridepublicvoiddrawingFinished() {

}

});

danmakuContext = DanmakuContext.create();

danmakuView.prepare(parser, danmakuContext);

}/**? ? * 向彈幕View中添加一條彈幕? ? *@paramcontent? ? *? ? ? ? ? 彈幕的具體內(nèi)容? ? *@paramwithBorder? ? *? ? ? ? ? 彈幕是否有邊框? ? */privatevoidaddDanmaku(String content,booleanwithBorder) {

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);

}/**

* 隨機(jī)生成一些彈幕內(nèi)容以供測(cè)試

*/privatevoidgenerateSomeDanmaku() {newThread(newRunnable() {@Overridepublicvoidrun() {while(showDanmaku) {inttime =newRandom().nextInt(300);

String content =""+ time + time;

addDanmaku(content,false);try{

Thread.sleep(time);

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

}).start();

}/**

* sp轉(zhuǎn)px的方法偶摔。

*/publicintsp2px(floatspValue) {finalfloatfontScale = getResources().getDisplayMetrics().scaledDensity;return(int) (spValue * fontScale +0.5f);

}@OverrideprotectedvoidonPause() {super.onPause();if(danmakuView !=null&& danmakuView.isPrepared()) {

danmakuView.pause();

}

}@OverrideprotectedvoidonResume() {super.onResume();if(danmakuView !=null&& danmakuView.isPrepared() && danmakuView.isPaused()) {

danmakuView.resume();

}

}@OverrideprotectedvoidonDestroy() {super.onDestroy();

showDanmaku =false;if(danmakuView !=null) {

danmakuView.release();

danmakuView =null;

}

}

......

}

可以看到,在onCreate()方法中我們先是獲取到了DanmakuView控件的實(shí)例促脉,然后調(diào)用了enableDanmakuDrawingCache()方法來(lái)提升繪制效率辰斋,又調(diào)用了setCallback()方法來(lái)設(shè)置回調(diào)函數(shù)。

接著調(diào)用DanmakuContext.create()方法創(chuàng)建了一個(gè)DanmakuContext的實(shí)例瘸味,DanmakuContext可以用于對(duì)彈幕的各種全局配置進(jìn)行設(shè)定宫仗,如設(shè)置字體、設(shè)置最大顯示行數(shù)等旁仿。這里我們并沒(méi)有什么特殊的要求藕夫,因此一切都保持默認(rèn)。

另外我們還需要?jiǎng)?chuàng)建一個(gè)彈幕的解析器才行枯冈,這里直接創(chuàng)建了一個(gè)全局的BaseDanmakuParser毅贮。

有了DanmakuContext和BaseDanmakuParser,接下來(lái)我們就可以調(diào)用DanmakuView的prepare()方法來(lái)進(jìn)行準(zhǔn)備尘奏,準(zhǔn)備完成后會(huì)自動(dòng)調(diào)用剛才設(shè)置的回調(diào)函數(shù)中的prepared()方法滩褥,然后我們?cè)谶@里再調(diào)用DanmakuView的start()方法,這樣DanmakuView就可以開始正常工作了炫加。

雖說(shuō)DanmakuView已經(jīng)在正常工作了铸题,但是屏幕上沒(méi)有任何彈幕信息的話我們也看不出效果,因此我們還要增加一個(gè)添加彈幕消息的功能琢感。

觀察addDanmaku()方法,這個(gè)方法就是用于向DanmakuView中添加一條彈幕消息的探熔。其中首先調(diào)用了createDanmaku()方法來(lái)創(chuàng)建一個(gè)BaseDanmaku實(shí)例驹针,TYPE_SCROLL_RL表示這是一條從右向左滾動(dòng)的彈幕,然后我們就可以對(duì)彈幕的內(nèi)容诀艰、字體大小柬甥、顏色、顯示時(shí)間等各種細(xì)節(jié)進(jìn)行配置了其垄。注意addDanmaku()方法中有一個(gè)withBorder參數(shù)苛蒲,這個(gè)參數(shù)用于指定彈幕消息是否帶有邊框,這樣才好將自己發(fā)送的彈幕和別人發(fā)送的彈幕進(jìn)行區(qū)分绿满。

這樣我們就把最基本的彈幕功能就完成了臂外,現(xiàn)在只需要當(dāng)在接收到別人發(fā)送的彈幕消息時(shí),調(diào)用addDanmaku()方法將這條彈幕添加到DanmakuView上就可以了。但接收別人發(fā)送來(lái)的消息又涉及到了即時(shí)通訊技術(shù)漏健,顯然這一篇文章中不可能將復(fù)雜的即時(shí)通訊技術(shù)也進(jìn)行講解嚎货,因此這里我專門寫了一個(gè)generateSomeDanmaku()方法來(lái)隨機(jī)生成一些彈幕消息,這樣就可以模擬出和斗魚類似的彈幕效果了蔫浆。

除此之外殖属,我們還需要在onPause()、onResume()瓦盛、onDestroy()方法中進(jìn)行一些邏輯處理洗显,以保證DanmakuView的資源可以得到釋放。

現(xiàn)在重新運(yùn)行一下程序原环,效果如下圖所示:

這樣我們就把第二步的功能也實(shí)現(xiàn)了挠唆。

加入操作界面

那么下面我們開始進(jìn)行第三步功能實(shí)現(xiàn),加入發(fā)送彈幕消息的操作界面扮念。

首先修改activity_main.xml中的代碼损搬,如下所示:

這里的邏輯還是比較簡(jiǎn)單的,我們先是給DanmakuView設(shè)置了一個(gè)點(diǎn)擊事件柜与,當(dāng)點(diǎn)擊屏幕時(shí)就會(huì)觸發(fā)這個(gè)點(diǎn)擊事件巧勤。然后進(jìn)行判斷,如果操作界面是隱藏的就將它顯示出來(lái)弄匕,如果操作界面是顯示的就將它隱藏掉颅悉,這樣就可以簡(jiǎn)單地通過(guò)點(diǎn)擊屏幕來(lái)實(shí)現(xiàn)操作界面的隱藏和顯示了。

接下來(lái)我們又給發(fā)送按鈕注冊(cè)了一個(gè)點(diǎn)擊事件迁匠,當(dāng)點(diǎn)擊發(fā)送時(shí)剩瓶,獲取EditText中的輸入內(nèi)容,然后調(diào)用addDanmaku()方法將這條消息添加到DanmakuView上城丧。另外延曙,這條彈幕是由我們自己發(fā)送的,因此addDanmaku()方法的第二個(gè)參數(shù)要傳入true亡哄。

最后枝缔,由于系統(tǒng)輸入法彈出的時(shí)候會(huì)導(dǎo)致焦點(diǎn)丟失,從而退出沉浸式模式蚊惯,因此這里還對(duì)系統(tǒng)全局的UI變化進(jìn)行了監(jiān)聽(tīng)愿卸,保證程序一直可以處于沉浸式模式。

這樣我們就將所有的代碼都完成了截型,現(xiàn)在可以運(yùn)行一下看看最終效果了趴荸。由于電影播放的同時(shí)進(jìn)行GIF截圖生成的文件太大了,無(wú)法上傳宦焦,因此這里我是在電影暫停的情況進(jìn)行操作的发钝。效果如下圖所示:

可以看到顿涣,我們自己發(fā)送的彈幕是有一個(gè)綠色邊框包圍的,很容易和其他彈幕區(qū)分開笼平。

這樣我們就把第三步的功能也實(shí)現(xiàn)了园骆。

雖說(shuō)現(xiàn)在我們已經(jīng)成功實(shí)現(xiàn)了非常不錯(cuò)的彈幕效果,但其實(shí)這只是DanmakuFlameMaster庫(kù)提供的最基本的功能而已寓调。嗶哩嗶哩提供的這個(gè)彈幕開源庫(kù)中擁有極其豐富的功能锌唾,包含各種不同的彈幕樣式、特效等等夺英。不過(guò)本篇文章的主要目標(biāo)是帶大家了解彈幕效果實(shí)現(xiàn)的思路晌涕,并不是要對(duì)DanmakuFlameMaster這個(gè)庫(kù)進(jìn)行全面的解析。如果你對(duì)這個(gè)庫(kù)非常感興趣痛悯,可以到它的github主頁(yè)上面去學(xué)習(xí)更多的用法余黎。

源碼下載,請(qǐng)點(diǎn)擊這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末载萌,一起剝皮案震驚了整個(gè)濱河市惧财,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扭仁,老刑警劉巖垮衷,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乖坠,居然都是意外死亡搀突,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門熊泵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)仰迁,“玉大人,你說(shuō)我怎么就攤上這事顽分⌒煨恚” “怎么了卒蘸?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵雌隅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我悬秉,道長(zhǎng),這世上最難降的妖魔是什么冰蘑? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任和泌,我火速辦了婚禮,結(jié)果婚禮上祠肥,老公的妹妹穿的比我還像新娘武氓。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布县恕。 她就那樣靜靜地躺著东羹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪忠烛。 梳的紋絲不亂的頭發(fā)上属提,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音美尸,去河邊找鬼冤议。 笑死,一個(gè)胖子當(dāng)著我的面吹牛师坎,可吹牛的內(nèi)容都是我干的恕酸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼胯陋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蕊温!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起遏乔,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤义矛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后按灶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體症革,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年鸯旁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了噪矛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铺罢,死狀恐怖艇挨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情韭赘,我是刑警寧澤缩滨,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站泉瞻,受9級(jí)特大地震影響脉漏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袖牙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一侧巨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鞭达,春花似錦司忱、人聲如沸皇忿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鳍烁。三九已至,卻和暖如春繁扎,著一層夾襖步出監(jiān)牢的瞬間幔荒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工锻离, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铺峭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓汽纠,卻偏偏與公主長(zhǎng)得像卫键,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虱朵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,516評(píng)論 25 707
  • 如今直播行業(yè)確實(shí)是非巢耆火爆啊絮宁,大大小小的公司都要涉足一下直播的領(lǐng)域,用斗魚的話來(lái)講服协,現(xiàn)在就是千播之戰(zhàn)绍昂。而彈幕則無(wú)疑...
    GB_speak閱讀 3,091評(píng)論 1 30
  • 內(nèi)容是博主照著書敲出來(lái)的,博主碼字挺辛苦的偿荷,轉(zhuǎn)載請(qǐng)注明出處窘游,后序內(nèi)容陸續(xù)會(huì)碼出。 當(dāng)了解了Android坐標(biāo)系和觸...
    Blankj閱讀 6,626評(píng)論 3 61
  • 崗位職責(zé): 1) 負(fù)責(zé)市場(chǎng)調(diào)查跳纳,競(jìng)品分析忍饰,對(duì)相關(guān)信息和數(shù)據(jù)進(jìn)行整理分析,撰寫分析報(bào)告交相關(guān)人員做決策參考寺庄; 2) ...
    書中自有黃金屋123閱讀 293評(píng)論 0 1
  • 青磚素瓦遠(yuǎn)紅塵艾蓝, 不見(jiàn)俗世淡留痕。 萬(wàn)載古剎今仍在斗塘, 空余老僧守柴門赢织。
    晚城閱讀 105評(píng)論 0 0