Today I'm going to share the learning experience of the Android Bullet Screen Framework (danmakuflamemaster) by Bilibili Open source.
I am the entire framework in the form of model into the project, so more convenient to observe the source code. can also be injected in a way that depends.
dependencies {
compile ' com.github.ctiao:danmakuflamemaster:0.5.3 '
}
Let me just put in a picture of the effect I'm going to make:
Page analysis
From the figure above, the entire UI is divided into three layers. The bottom is the video layer, the middle is the bomb screen layer, the top layer is the control layer. Now the mainstream video streaming software in the market are mostly such layered, different is a live class, there may be more than one layer of interaction, display attendance information, gift information and so on.
Since it is layered, I will directly use the Framelayout frame layout to achieve. Post my layout file:
<framelayout xmlns:android= "http://schemas.android.com/apk/res/android"
android:layout_width= "Match_ Parent "
android:layout_height=" match_parent ">
<videoview
android:id=" @+id/vv_video
" Android:layout_width= "Match_parent"
android:layout_height= "match_parent"/>
< Master.flame.danmaku.ui.widget.DanmakuView
android:id= "@+id/sv_danmaku"
android:layout_width= "Match_ Parent "
android:layout_height=" match_parent "/>
<include android:id=" @+id/media_controller "
android:layout_width= "match_parent"
android:layout_height= "fill_parent"
layout= "@layout/media_ Controller "/>
</FrameLayout>
Layout of the control layer:
<framelayout xmlns:android= "http://schemas.android.com/apk/res/android" android:layout_width= "Match_parent" android:layout_height= "Match_parent" > <linearlayout android:gravity= "center_vertical" android:layout_width= "Match_parent" android:layout_height= "wrap_content" android:layout_gravity= "Bottom" "android:background=" #8acc22dd "> <button android:layout_weight=" 1 "android:id=" @+id/rotate "android:layout_width=" 0DP "Android:layout_heigh t= "Wrap_content" android:text= "@string/rotate"/> <button android:layout_width= "0DP" android:layout_weight= "1 "Android:id=" @+id/btn_hide "android:layout_height=" wrap_content "android:text=" @string/hide_danmaku "/> < Button android:id= "@+id/btn_show" android:layout_width= "0DP" android:layout_weight= "1" android:layout_height= "wrap _content "android:text=" @string/show_danmaku "/> <button android:id=" @+id/btn_pause "android:layout_width=" 0DP "android:layout_weight=" 1 "android:layout_height=" Wrap_cOntent "android:text=" @string/pause_danmaku "/> <button android:id=" @+id/btn_resume "android:layout_width="
0DP "android:layout_weight=" 1 "android:layout_height=" wrap_content "android:text=" @string/resume_danmaku "/> <button android:id= "@+id/btn_send" android:layout_width= "0DP" android:layout_weight= "1" android:layout_height= " Wrap_content "android:text=" @string/send_danmaku/> <button android:id= "@+id/btn_send_image_text" Android: Layout_width= "0DP" android:layout_weight= "1" android:layout_height= "wrap_content" android:text= "@string/send_ Danmaku_image_text "/> <button android:id=" @+id/btn_send_danmakus "android:layout_width=" 0DP "Android:layout_
weight= "1" android:layout_height= "wrap_content" android:text= "@string/send_danmakus"/> </LinearLayout> </FrameLayout>
Finish writing the layout, first write an initialization method:
Sets the maximum number of rows hashmap<integer,integer> Maxlinespair = new hashmap<> (); Maxlinespair.put (basedanmaku.type_scroll_rl,5)//scroll screen maximum display 5 line//set whether to prohibit overlapping hashmap<integer, boolean>
Overlappingenablepair = new hashmap<> ();
Overlappingenablepair.put (BASEDANMAKU.TYPE_SCROLL_RL, true);
Overlappingenablepair.put (Basedanmaku.type_fix_top, true); Mdanmakucontext = Danmakucontext.create ()//initialization context Mdanmakucontext.setdanmakustyle (Idisplayer.danmaku_style_
stroken,3)//Set the type of mdanmakucontext.setduplicatemergingenabled (false),//Set whether to merge repeated barrage
Mdanmakucontext.setscrollspeedfactor (1.2f); Set the rolling speed of the projectile screen mdanmakucontext.setscaletextsize (1.2f);//Set the size of the bounce screen font Mdanmakucontext.setcachestuffer (New Spannedcachestuffer (), mcachestufferadapter);//Set Cache draw Filler
Use Spannedcachestuffer mdanmakucontext.setmaximumlines (Maxlinespair) for graphics and text mix;//Set maximum number of rows Mdanmakucontext.preventoverlapping (Overlappingenablepair); Set whether to prohibit overlapping mparser = Createparser (This.getresources (). Openrawresource (r.raw.comments))//Loading barrage resource file Mdvdanmaku. Prepare (Mparser, mdanmakucontext);
Mdvdanmaku.showfps (TRUE); Mdvdanmaku.enabledanmakudrawingcache (TRUE);
Then paste the implementation of the drawing filler, mainly to achieve the image and text flow together the effect
Private Spannablestringbuilder createspannable (drawable drawable) {
String text = "Bitmap";
Spannablestringbuilder Spannablestringbuilder = new Spannablestringbuilder (text);
Imagespan span = new Imagespan (drawable);
Spannablestringbuilder.setspan (span, 0, text.length (), spannable.span_inclusive_exclusive);
Spannablestringbuilder.append ("Graphic mixed Row");
Spannablestringbuilder.setspan (New Backgroundcolorspan ("#8A2233B1")), 0, Spannablestringbuilder.length (), spannable.span_inclusive_inclusive);
return spannablestringbuilder;
}
Under the Basedanmaku class, the basic motion forms of the projectile screen are defined: TYPE_SCROLL_RL, TYPE_SCROLL_LR, Type_fix_top, Type_fix_bottom and type_special. That is, from right to left out, left to right out (reverse barrage), top of the screen, the bottom of the barrage and high-level barrage. (in addition to the script barrage)
When initializing, it is necessary to pass in a specific Danmukucontext context to initialize some settings through the context. Bomb screen resources are stored in the XML file, the approximate format is as follows:
.
<i>
<chatserver>chat.bilibili.com</chatserver>
<chatid>2962351</chatid>
<mission>0</mission>
<maxlimit>1500</maxlimit>
<source>k-v</ source>
<d p= "145.91299438477,1,25,16777215,1422201001,0,d6673695,757075520" > I've never seen anyone so brazen </d >
</i>
Header information does not require much attention to see the specific meaning of the p corresponding parameter under D label:
The first one: the time when the curtain appears
Second: The type of the projectile screen (1, from right to left; 6, from left to right; 5, top barrage; 4, bottom barrage; 7, advanced barrage; 8, script bullet screen)
Third: Font size
Fourth one: color
Fifth: Time stamp
Sixth: Bomb screen pool ID
Seventh: User hash value
Eighth: Bomb Screen ID
The following is a detailed analysis of the bomb screen code
/**
*
@param stream
* @return
/Private Basedanmakuparser Createparser from the barrage file (InputStream Stream) {
if (stream = = null) {return
new Basedanmakuparser () {
@Override
protected Danmakus Parse () {
return new Danmakus ();
}
Iloader loader = danmakuloaderfactory.create (Danmakuloaderfactory.tag_bili);//Create a Bilidanmakuloader instance to load the bullet screen stream file
try {
loader.load (stream);
} catch (Illegaldataexception e) {
e.printstacktrace ();
}
Basedanmakuparser parser = new Bilidanmukuparser ()//Bomb screen parser
idatasource<?> DataSource = Loader.getdatasource ();
Parser.load (DataSource);
return parser;
}
Specific solution:
String tagName = localname.length ()!= 0? Localname:qname;
TagName = Tagname.tolowercase (Locale.getdefault ()). Trim ();
if (Tagname.equals ("D")) {
String pvalue = Attributes.getvalue ("P");
Parse p value to Danmaku
string[] values = Pvalue.split (",");
if (Values.length > 0) {
long time = (long) (Float.parsefloat (values[0)) * 1000);
Rseint (Values[1]); Barrage type
Float textsize = float.parsefloat (values[2]);//font size
int color = integer.parseint (values[3)) | 0xff0000 00; Color
item = MContext.mDanmakuFactory.createDanmaku (type, mcontext);
if (item!= null) {
item.settime (time);
Item.textsize = Textsize * (mdispdensity-0.6f);
Item.textcolor = color;
Item.textshadowcolor = Color <= color.black? Color.WHITE:Color.BLACK
}}
}
When the bomb screen resource is loaded, the Mdvdanmuku prepare () method is invoked to execute the preparation. The prepared method in the Drawhandler.callback () callback is invoked when the preparation is complete. Then in this prepared method formally let the barrage start. The order of invocation is as follows:
Mdvdanmaku.prepare (Mparser, mdanmakucontext);//Incoming parsing complete barrage and context
Then execute the Prepare () method under Danmukuview
private void Prepare () {
if (handler = = null)
handler = new Drawhandler (Getlooper (Mdrawingthreadtype), this, Mdan makuvisible);//Create a handler
}
Through this handler to achieve interprocess communication.
Handler.setconfig (config);
Handler.setparser (parser);
Handler.setcallback (Mcallback);
Handler.prepare ();-"will let handler send a message to carry out the real preparation
There is a callback in the Drawhandler
Public interface Callback {public
void prepared ();
public void Updatetimer (Danmakutimer timer);
public void Danmakushown (Basedanmaku danmaku);
public void drawingfinished ();
}
The Real preparation
Mtimebase = Systemclock.uptimemillis ();
if (Mparser = null | |!mdanmakuview.isviewready ()) {//not ready, delay 0.1 seconds before executing
sendemptymessagedelayed (PREPARE);
} else {
Prepare (new Runnable () {
@Override public
void Run () {
pausedposition = 0;
Mready = true;
if (mcallback!= null) {
mcallback.prepared ();}}}
);
The above is the start call flow of the view of the bomb screen.
So, how do you add a pinball screen to pinch? Yuan Fang, what do you think? (tm How do I know what I think) look below
private void Adddanmaku (Boolean islive) {
Basedanmaku Danmaku = MDanmakuContext.mDanmakuFactory.createDanmaku ( BASEDANMAKU.TYPE_SCROLL_RL);//Add a right-to-left scrolling barrage
if (Danmaku = null | | | mdvdanmaku = NULL) {return
;
}
Danmaku.text = "This is a barrage" + system.nanotime ();
danmaku.padding = 5;
danmaku.priority = 0; May be filtered by a variety of filters and hide the display set to 1, it will be shown to be suitable for the sending of the screen
danmaku.islive = islive;
Danmaku.settime (Mdvdanmaku.getcurrenttime () + 1200);
Danmaku.textsize = 25f * (Mparser.getdisplayer (). Getdensity ()-0.6f);
Danmaku.textcolor = color.red;//default set to red font
danmaku.textshadowcolor = color.white;
Danmaku.bordercolor = color.green;//in order to distinguish other shells and their hair, and then give their own shells with a border
Mdvdanmaku.adddanmaku (Danmaku);
}
The above is a small set up to introduce the Android frame of the framework of the Dark flame, I hope to help you, if you have any questions please give me a message, small series will promptly reply to everyone. Here also thank you very much for the cloud Habitat Community website support!