配色: 字号:
Android仿网络直播弹幕功能的实现
2017-01-03 | 阅:  转:  |  分享 
  
Android仿网络直播弹幕功能的实现

现在网络直播越来越火,网络主播也逐渐成为一种新兴职业,对于网络直播,弹幕功能是必须要有的,如下图:



首先来分析一下,这个弹幕功能是怎么实现的,首先在最下面肯定是一个游戏界面View,然后游戏界面上有弹幕View,弹幕的View必须要做成完全透明的,这样即使覆盖在游戏界面的上方也不会影响到游戏的正常观看,只有当有人发弹幕消息时,再将消息绘制到弹幕的View上面就可以了,下方肯定还有有操作界面View,可以让用户来发弹幕和送礼物的功能,原理示意图如下所示:



参照原理图,下面一步一步来实现这个功能。

实现视频的播放

activity_main.xml

[html]?viewplain?copy?


????xmlns:android="http://schemas.android.com/apk/res/android"??

????android:id="@+id/activity_main"??

????android:layout_width="match_parent"??

????android:layout_height="match_parent"??

????android:background="#000">??

??

????
????????android:id="@+id/video_view"??

????????android:layout_width="match_parent"??

????????android:layout_height="wrap_content"??

????????android:layout_centerInParent="true"/>??

??

MainActivity.java

[java]?viewplain?copy?

package?com.jackie.bombscreen;??

??

import?android.os.Build;??

import?android.os.Bundle;??

import?android.os.Environment;??

import?android.support.v7.app.AppCompatActivity;??

import?android.view.View;??

import?android.widget.VideoView;??

??

public?class?MainActivity?extends?AppCompatActivity?{??

????@Override??

????protected?void?onCreate(Bundle?savedInstanceState)?{??

????????super.onCreate(savedInstanceState);??

????????setContentView(R.layout.activity_main);??

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

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

????????videoView.start();??

????}??

??????

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

????????}??

????}??

}??

最后别忘了设置AndroidMainfest.xml



效果如下:



实现弹幕的效果

接下来我们开始实现弹幕效果。弹幕其实也就是一个自定义的View,它的上面可以显示类似于跑马灯的文字效果。观众们发表的评论都会在弹幕上显示出来,但又会很快地移出屏幕,既可以起到互动的作用,同时又不会影响视频的正常观看。

我们可以自己来编写这样的一个自定义View,当然也可以直接使用网上现成的开源项目。那么为了能够简单快速地实现弹幕效果,这里我就准备直接使用由哔哩哔哩开源的弹幕效果库DanmakuFlameMaster。

DanmakuFlameMaster库的项目主页地址是:https://github.com/Bilibili/DanmakuFlameMaster

添加build.gradle依赖

compile''com.github.ctiao:DanmakuFlameMaster:0.5.3''

[html]?viewplain?copy?

??


????xmlns:android="http://schemas.android.com/apk/res/android"??

????android:id="@+id/activity_main"??

????android:layout_width="match_parent"??

????android:layout_height="match_parent"??

????android:background="#000">??

??

????
????????android:id="@+id/video_view"??

????????android:layout_width="match_parent"??

????????android:layout_height="wrap_content"??

????????android:layout_centerInParent="true"/>??

??

????
????????android:id="@+id/danmaku_view"??

????????android:layout_width="match_parent"??

????????android:layout_height="match_parent"?/>??

??

修改MainActivity.java

[java]?viewplain?copy?

package?com.jackie.bombscreen;??

??

import?android.graphics.Color;??

import?android.os.Build;??

import?android.os.Bundle;??

import?android.os.Environment;??

import?android.support.v7.app.AppCompatActivity;??

import?android.view.View;??

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?mIsShowDanmaku;??

????private?DanmakuView?mDanmakuView;??

????private?DanmakuContext?mDanmakuContext;??

??

????private?BaseDanmakuParser?parser?=?new?BaseDanmakuParser()?{??

????????@Override??

????????protected?IDanmakus?parse()?{??

????????????return?new?Danmakus();??

????????}??

????};??

??

????@Override??

????protected?void?onCreate(Bundle?savedInstanceState)?{??

????????super.onCreate(savedInstanceState);??

????????setContentView(R.layout.activity_main);??

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

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

????????videoView.start();??

??

????????mDanmakuView?=?(DanmakuView)?findViewById(R.id.danmaku_view);??

????????mDanmakuView.enableDanmakuDrawingCache(true);??

????????mDanmakuView.setCallback(new?DrawHandler.Callback()?{??

????????????@Override??

????????????public?void?prepared()?{??

????????????????mIsShowDanmaku?=?true;??

????????????????mDanmakuView.start();??

????????????????generateSomeDanmaku();??

????????????}??

??

????????????@Override??

????????????public?void?updateTimer(DanmakuTimer?timer)?{??

??

????????????}??

??

????????????@Override??

????????????public?void?danmakuShown(BaseDanmaku?danmaku)?{??

??

????????????}??

??

????????????@Override??

????????????public?void?drawingFinished()?{??

??

????????????}??

????????});??

??

????????mDanmakuContext?=?DanmakuContext.create();??

????????mDanmakuView.prepare(parser,?mDanmakuContext);??

????}??

??

????/?

??????向弹幕View中添加一条弹幕?

??????@param?content???????弹幕的具体内容?

??????@param??withBorder???弹幕是否有边框?

?????/??

????private?void?addDanmaku(String?content,?boolean?withBorder)?{??

????????BaseDanmaku?danmaku?=?mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);??

????????danmaku.text?=?content;??

????????danmaku.padding?=?5;??

????????danmaku.textSize?=?sp2px(20);??

????????danmaku.textColor?=?Color.WHITE;??

????????danmaku.setTime(mDanmakuView.getCurrentTime());??

????????if?(withBorder)?{??

????????????danmaku.borderColor?=?Color.GREEN;??

????????}??

????????mDanmakuView.addDanmaku(danmaku);??

????}??

??

????/?

??????随机生成一些弹幕内容以供测试?

?????/??

????private?void?generateSomeDanmaku()?{??

????????new?Thread(new?Runnable()?{??

????????????@Override??

????????????public?void?run()?{??

????????????????while(mIsShowDanmaku)?{??

????????????????????int?time?=?new?Random().nextInt(300);??

????????????????????String?content?=?""?+?time?+?time;??

????????????????????addDanmaku(content,?false);??

????????????????????try?{??

????????????????????????Thread.sleep(time);??

????????????????????}?catch?(InterruptedException?e)?{??

????????????????????????e.printStackTrace();??

????????????????????}??

????????????????}??

????????????}??

????????}).start();??

????}??

??

????/?

??????sp转px的方法。?

?????/??

????public?int?sp2px(float?spValue)?{??

????????final?float?fontScale?=?getResources().getDisplayMetrics().scaledDensity;??

????????return?(int)?(spValue??fontScale?+?0.5f);??

????}??

??

????@Override??

????protected?void?onPause()?{??

????????super.onPause();??

????????if?(mDanmakuView?!=?null?&&?mDanmakuView.isPrepared())?{??

????????????mDanmakuView.pause();??

????????}??

????}??

??

????@Override??

????protected?void?onResume()?{??

????????super.onResume();??

????????if?(mDanmakuView?!=?null?&&?mDanmakuView.isPrepared()?&&?mDanmakuView.isPaused())?{??

????????????mDanmakuView.resume();??

????????}??

????}??

??

????@Override??

????protected?void?onDestroy()?{??

????????super.onDestroy();??

????????mIsShowDanmaku?=?false;??

????????if?(mDanmakuView?!=?null)?{??

????????????mDanmakuView.release();??

????????????mDanmakuView?=?null;??

????????}??

????}??

??????

????@Override??

????public?void?onWindowFocusChanged(boolean?hasFocus)?{??

????????super.onWindowFocusChwww.baiyuewang.netanged(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);??

????????}??

????}??

}??

效果图如下:



加入操作界面

[html]?viewplain?copy?

??


????xmlns:android="http://schemas.android.com/apk/res/android"??

????android:id="@+id/activity_main"??

????android:layout_width="match_parent"??

????android:layout_height="match_parent"??

????android:background="#000">??

??

????
????????android:id="@+id/video_view"??

????????android:layout_width="match_parent"??

????????android:layout_height="wrap_content"??

????????android:layout_centerInParent="true"/>??

??

????
????????android:id="@+id/danmaku_view"??

????????android:layout_width="match_parent"??

????????android:layout_height="match_parent"?/>??

??

????
????????android:id="@+id/operation_layout"??

????????android:layout_width="match_parent"??

????????android:layout_height="50dp"??

????????android:layout_alignParentBottom="true"??

????????android:background="#fff"??

????????android:visibility="gone">??

??

????????
????????????android:id="@+id/edit_text"??

????????????android:layout_width="0dp"??

????????????android:layout_height="match_parent"??

????????????android:layout_weight="1"?/>??

??

????????
????????????android:id="@+id/send"??

????????????android:layout_width="wrap_content"??

????????????android:layout_height="match_parent"??

????????????android:text="Send"?/>??

??????

??

[java]?viewplain?copy?

package?com.jackie.bombscreen;??

??

import?android.graphics.Color;??

import?android.os.Build;??

import?android.os.Bundle;??

import?android.os.Environment;??

import?android.support.v7.app.AppCompatActivity;??

import?android.text.TextUtils;??

import?android.view.View;??

import?android.widget.Button;??

import?android.widget.EditText;??

import?android.widget.LinearLayout;??

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?mIsShowDanmaku;??

????private?DanmakuView?mDanmakuView;??

????private?DanmakuContext?mDanmakuContext;??

??

????private?BaseDanmakuParser?parser?=?new?BaseDanmakuParser()?{??

????????@Override??

????????protected?IDanmakus?parse()?{??

????????????return?new?Danmakus();??

????????}??

????};??

??

????@Override??

????protected?void?onCreate(Bundle?savedInstanceState)?{??

????????super.onCreate(savedInstanceState);??

????????setContentView(R.layout.activity_main);??

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

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

????????videoView.start();??

??

????????mDanmakuView?=?(DanmakuView)?findViewById(R.id.danmaku_view);??

????????mDanmakuView.enableDanmakuDwww.tt951.comrawingCache(true);??

????????mDanmakuView.setCallback(new?DrawHandler.Callback()?{??

????????????@Override??

????????????public?void?prepared()?{??

????????????????mIsShowDanmaku?=?true;??

????????????????mDanmakuView.start();??

????????????????generateSomeDanmaku();??

????????????}??

??

????????????@Override??

????????????public?void?updateTimer(DanmakuTimer?timer)?{??

??

????????????}??

??

????????????@Override??

????????????public?void?danmakuShown(BaseDanmaku?danmaku)?{??

??

????????????}??

??

????????????@Override??

????????????public?void?drawingFinished()?{??

??

????????????}??

????????});??

??

????????mDanmakuContext?=?DanmakuContext.create();??

????????mDanmakuView.prepare(parser,?mDanmakuContext);??

??

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

????????mDanmakuView.setOnClickListener(new?View.OnClickListener()?{??

????????????@Override??

????????????public?void?onClick(View?view)?{??

????????????????if?(operationLayout.getVisibility()?==?View.GONE)?{??

????????????????????operationLayout.setVisibility(View.VISIBLE);??

????????????????}?else?{??

????????????????????operationLayout.setVisibility(View.GONE);??

????????????????}??

????????????}??

????????});??

??????????

????????send.setOnClickListener(new?View.OnClickListener()?{??

????????????@Override??

????????????public?void?onClick(View?view)?{??

????????????????String?content?=?editText.getText().toString();??

????????????????if?(!TextUtils.isEmpty(content))?{??

????????????????????addDanmaku(content,?true);??

????????????????????editText.setText("");??

????????????????}??

????????????}??

????????});??

??

????????getWindow().getDecorView().setOnSystemUiVisibilityChangeListener?(new?View.OnSystemUiVisibilityChangeListener()?{??

????????????@Override??

????????????public?void?onSystemUiVisibilityChange(int?visibility)?{??

????????????????if?(visibility?==?View.SYSTEM_UI_FLAG_VISIBLE)?{??

????????????????????onWindowFocusChanged(true);??

????????????????}??

????????????}??

????????});??

????}??

??

????/?

??????向弹幕View中添加一条弹幕?

??????@param?content???????弹幕的具体内容?

??????@param??withBorder???弹幕是否有边框?

?????/??

????private?void?addDanmaku(String?content,?boolean?withBorder)?{??

????????BaseDanmaku?danmaku?=?mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);??

????????danmaku.text?=?content;??

????????danmaku.padding?=?5;??

????????danmaku.textSize?=?sp2px(20);??

????????danmaku.textColor?=?Color.WHITE;??

????????danmaku.setTime(mDanmakuView.getCurrentTime());??

????????if?(withBorder)?{??

????????????danmaku.borderColor?=?Color.GREEN;??

????????}??

????????mDanmakuView.addDanmaku(danmaku);??

????}??

??

????/?

??????随机生成一些弹幕内容以供测试?

?????/??

????private?void?generateSomeDanmaku()?{??

????????new?Thread(new?Runnable()?{??

????????????@Override??

????????????public?void?run()?{??

????????????????while(mIsShowDanmaku)?{??

????????????????????int?time?=?new?Random().nextInt(300);??

????????????????????String?content?=?""?+?time?+?time;??

????????????????????addDanmaku(content,?false);??

????????????????????try?{??

????????????????????????Thread.sleep(time);??

????????????????????}?catch?(InterruptedException?e)?{??

????????????????????????e.printStackTrace();??

????????????????????}??

????????????????}??

????????????}??

????????}).start();??

????}??

??

????/?

??????sp转px的方法。?

?????/??

????public?int?sp2px(float?spValue)?{??

????????final?float?fontScale?=?getResources().getDisplayMetrics().scaledDensity;??

????????return?(int)?(spValue??fontScale?+?0.5f);??

????}??

??

????@Override??

????protected?void?onPause()?{??

????????super.onPause();??

????????if?(mDanmakuView?!=?null?&&?mDanmakuView.isPrepared())?{??

????????????mDanmakuView.pause();??

????????}??

????}??

??

????@Override??

????protected?void?onResume()?{??

????????super.onResume();??

????????if?(mDanmakuView?!=?null?&&?mDanmakuView.isPrepared()?&&?mDanmakuView.isPaused())?{??

????????????mDanmakuView.resume();??

????????}??

????}??

??

????@Override??

????protected?void?onDestroy()?{??

????????super.onDestroy();??

????????mIsShowDanmaku?=?false;??

????????if?(mDanmakuView?!=?null)?{??

????????????mDanmakuView.release();??

????????????mDanmakuView?=?null;??

????????}??

????}??

??

??

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

????????}??

????}??

}??



献花(0)
+1
(本文系thedust79首藏)