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"?/>??
??
??????????
??
[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);??
????????}??
????}??
}??
|
|