自定义视图视频加弹幕
视频布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="300dp">
<com.example.day11_surfaceview.view.YaoVideoView
android:id="@+id/yao"
android:layout_width="match_parent"
android:layout_height="match_parent"></com.example.day11_surfaceview.view.YaoVideoView>
<LinearLayout
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:id="@+id/tv_start"
android:text=" 播放/暂停 "
android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:id="@+id/tv_quick"
android:text=" 快进 "
android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:id="@+id/tv_tui"
android:text=" 快退 "
android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:id="@+id/tv_bei"
android:text=" 倍速 "
android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:id="@+id/tv_full"
android:text=" 全屏 "
android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:text="00:00"
android:id="@+id/tv_current"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"></TextView>
<SeekBar
android:id="@+id/bar"
android:layout_width="0dp"
android:layout_weight="7"
android:layout_height="wrap_content"></SeekBar>
<TextView
android:textSize="17sp"
android:textColor="#fff"
android:text="00:00"
android:id="@+id/tv_duration"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"></TextView>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
视频自定义视图
public class YaoVideoView extends SurfaceView implements SurfaceHolder.Callback {
private MediaPlayer mediaPlayer = new MediaPlayer();
//设置视频资源
public void setData(String path){
if(mediaPlayer != null){
mediaPlayer.reset();//重置
try {
mediaPlayer.setDataSource(path);
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
}
});
}
}
public void init(){
getHolder().addCallback(this);//获得surfaceview的生命周期
}
public YaoVideoView(Context context) {//java代码执行该构造
super(context);
init();
}
public YaoVideoView(Context context, AttributeSet attrs) {//xml布局执行该构造,一般是他
super(context, attrs);
init();
}
public YaoVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public YaoVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
Log.d("ytx", "被创建了: ");
if (mediaPlayer != null){
mediaPlayer.setDisplay(holder);//展示视频画面
}
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
Log.d("ytx", "改变了: ");
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
Log.d("ytx", "销毁了: ");
if(mediaPlayer != null){
mediaPlayer.release();//释放资源
mediaPlayer = null;
}
}
/**
* 播放和暂停
*/
public void play(){
if (mediaPlayer != null){
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}else{
mediaPlayer.start();
}
}
}
/**
* 快进3秒
*/
public void quick(){
if(mediaPlayer != null){
int currentPosition = mediaPlayer.getCurrentPosition();//获得当前进度
currentPosition+=3000;
mediaPlayer.seekTo(currentPosition);//播放到指定进度
}
}
/**
* 快退3秒
*/
public void tui(){
if(mediaPlayer != null){
int currentPosition = mediaPlayer.getCurrentPosition();//获得当前进度
currentPosition-=3000;
mediaPlayer.seekTo(currentPosition);//播放到指定进度
}
}
/**
* 倍速播放
*/
@RequiresApi(api = Build.VERSION_CODES.M)
public void bei(){
if(mediaPlayer != null){
PlaybackParams playbackParams = mediaPlayer.getPlaybackParams();//获得播放参数
playbackParams.setSpeed(2f);//修改速度
mediaPlayer.setPlaybackParams(playbackParams);//设置回去
}
}
/**
* 获得当前进度
* @return
*/
public int getCurrrent(){
if(mediaPlayer != null){
return mediaPlayer.getCurrentPosition();
}
return 0;
}
/**
* 获得当前时间 00:00
*/
public String getCurrrentStr(){
if(mediaPlayer != null){
int currentPosition = mediaPlayer.getCurrentPosition();//毫秒
SimpleDateFormat format = new SimpleDateFormat("mm:ss");//将毫秒---》00:00
String format1 = format.format(currentPosition);
return format1;
}
return "00:00";
}
/**
* 获得总长度
* @return
*/
public int getDuration(){
if(mediaPlayer != null){
return mediaPlayer.getDuration();
}
return 0;
}
/**
* 获得总时间 00:00
*/
public String getDurationStr(){
if(mediaPlayer != null){
int currentPosition = mediaPlayer.getDuration();//毫秒
SimpleDateFormat format = new SimpleDateFormat("mm:ss");//将毫秒---》00:00
String format1 = format.format(currentPosition);
return format1;
}
return "00:00";
}
/***
* 拖动进度条
* @param progress
*/
public void seekTo(int progress){
if(mediaPlayer != null){
mediaPlayer.seekTo(progress);
}
}
}
主页面
public class MainActivity extends AppCompatActivity {
private YaoVideoView yaoVideoView;
private TextView tv_start;
private TextView tv_quick;
private TextView tv_tui;
private TextView tv_bei;
private TextView tv_full;
private TextView tv_current;
private SeekBar bar;
private TextView tv_duration;
private Timer timer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
requestPermissions(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
},101);
}
initView();
initTimer();
}
private void initTimer() {
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
final int duration = yaoVideoView.getDuration();
final int currrent = yaoVideoView.getCurrrent();
final String durationStr = yaoVideoView.getDurationStr();
final String currrentStr = yaoVideoView.getCurrrentStr();
runOnUiThread(new Runnable() {
@Override
public void run() {
bar.setMax(duration);
bar.setProgress(currrent);
tv_duration.setText(durationStr+"");
tv_current.setText(currrentStr+"");
}
});
}
},0,100);
}
private void initView() {
tv_start = (TextView) findViewById(R.id.tv_start);
tv_quick = (TextView) findViewById(R.id.tv_quick);
tv_tui = (TextView) findViewById(R.id.tv_tui);
tv_bei = (TextView) findViewById(R.id.tv_bei);
tv_full = (TextView) findViewById(R.id.tv_full);
tv_current = (TextView) findViewById(R.id.tv_current);
bar = (SeekBar) findViewById(R.id.bar);
tv_duration = (TextView) findViewById(R.id.tv_duration);
yaoVideoView = findViewById(R.id.yao);
yaoVideoView.setData("/sdcard/Movies/1807A.mp4");//播放视频
tv_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
yaoVideoView.play();
}
});
tv_quick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
yaoVideoView.quick();
}
});
tv_tui.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
yaoVideoView.tui();
}
});
tv_bei.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onClick(View v) {
yaoVideoView.bei();
}
});
tv_full.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
change();
}
});
bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if(fromUser){
yaoVideoView.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
//横竖屏切换---????视频重头播放---》activity会重新执行---》清单文件配置
private void change() {
int width = getWindow().getDecorView().getWidth();
int height = getWindow().getDecorView().getHeight();
if(width >= height){//横屏---》竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//PORTRAIT 肖像 竖着
}else{//竖屏--->横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//LANDSCAPE 山水画 横着
}
}
}
/
普通弹幕视图
/***
绘制文字:
1.画笔:Paint
2.画布:Canvas
*/
public class DanMuView extends SurfaceView implements SurfaceHolder.Callback {
public void init(){
getHolder().addCallback(this);
setZOrderOnTop(true);//置顶
getHolder().setFormat(PixelFormat.TRANSPARENT);//透明
}
public DanMuView(Context context) {
super(context);
init();
}
public DanMuView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public DanMuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public DanMuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private float x = 200;
//创建:绘制文字
@Override
public void surfaceCreated(@NonNull final SurfaceHolder holder) {
//TODO 1:画笔对象
final Paint paint = new Paint();
paint.setColor(Color.GREEN);//颜色
paint.setStrokeWidth(10);//粗细
paint.setTextSize(50);//文字大小
//TODO 2:画布
// Canvas canvas = holder.lockCanvas();//锁定画布
// canvas.drawText("真帅!!!!!",200,400,paint);
// holder.unlockCanvasAndPost(canvas);//解除锁定画布,必须写不然就报错
//动态文字移动
new Thread(new Runnable() {
@Override
public void run() {
while (true){
Canvas canvas = holder.lockCanvas();//锁定画布
canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);//将之前写的内容清空
canvas.drawText("真帅!!!!!",x++,400,paint);
holder.unlockCanvasAndPost(canvas);//解除锁定画布,必须写不然就报错
}
}
}).start();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
//销毁
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
}
}
高级弹幕
public class GaoJiDanMuView extends SurfaceView implements SurfaceHolder.Callback {
private List<GaoJiDanMuEntity> list = new ArrayList<>();
//添加弹幕
public void addDanMu(GaoJiDanMuEntity gaoJiDanMuEntity){
list.add(gaoJiDanMuEntity);
}
public void init(){
getHolder().addCallback(this);
list.add(new GaoJiDanMuEntity("我是穷鬼",20,200,false));
list.add(new GaoJiDanMuEntity("我是富二代",40,300,true));
list.add(new GaoJiDanMuEntity("没钱真帅",30,400,false));
list.add(new GaoJiDanMuEntity("有钱真好",0,180,true));
}
public GaoJiDanMuView(Context context) {
super(context);
init();
}
public GaoJiDanMuView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GaoJiDanMuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public GaoJiDanMuView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
//绘制多条弹幕
@Override
public void surfaceCreated(@NonNull final SurfaceHolder holder) {
//会员画笔
final Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(70);
//非会员画笔
final Paint paint2 = new Paint();
paint2.setColor(Color.GREEN);
paint2.setTextSize(50);
//文字动态
new Thread(new Runnable() {
@Override
public void run() {
while (true){
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
for (int i = 0; i < list.size(); i++) {
String text = list.get(i).getText();//文字
float x = list.get(i).getX();//x坐标
float y = list.get(i).getY();//y坐标
boolean vip = list.get(i).isVip();//是否为vip
if(vip){
canvas.drawText(text,x,y,paint);
}else{
canvas.drawText(text,x,y,paint2);
}
x++;
list.get(i).setX(x);//设置新的x坐标
}
holder.unlockCanvasAndPost(canvas);
}
}
}).start();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
}
}
清单文件
<!-- configChanges 当屏幕方向 键盘 大小 发生改变的时候生命周期不会重新执行 -->
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboard|screenSize"></activity>
视频弹幕交互
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".VideoDanMuActivity">
<com.example.day11_surfaceview.view.YaoVideoView
android:id="@+id/yao"
android:layout_width="match_parent"
android:layout_height="match_parent"></com.example.day11_surfaceview.view.YaoVideoView>
<com.example.day11_surfaceview.view.GaoJiDanMuView
android:id="@+id/danmu"
android:layout_width="match_parent"
android:layout_height="match_parent"></com.example.day11_surfaceview.view.GaoJiDanMuView>
<LinearLayout
android:layout_alignParentBottom="true"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"></EditText>
<Button
android:id="@+id/bt_send"
android:text="发送"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
</LinearLayout>
</RelativeLayout>
主页面
public class VideoDanMuActivity extends AppCompatActivity {
private YaoVideoView yao;
private GaoJiDanMuView danmu;
private EditText editText;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_dan_mu);
initView();
}
private void initView() {
yao = (YaoVideoView) findViewById(R.id.yao);
danmu = (GaoJiDanMuView) findViewById(R.id.danmu);
editText = findViewById(R.id.et);
button = findViewById(R.id.bt_send);
yao.setData("/sdcard/Movies/1807A.mp4");//播放视频
danmu.setZOrderOnTop(true);//弹幕置顶
danmu.getHolder().setFormat(PixelFormat.TRANSPARENT);//弹幕透明的
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String trim = editText.getText().toString().trim();
danmu.addDanMu(new GaoJiDanMuEntity(trim,0,400,true));
}
});
}
}