热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

SurfaceView播放视频发送弹幕并实现滚动歌词

这篇文章主要为大家详细介绍了SurfaceView播放视频发送弹幕并实现滚动歌词,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了SurfaceView播放视频发送弹幕,并实现滚动歌词,供大家参考,具体内容如下

SurfaceView使用步骤

使用SurfaceView的步骤:

  • 首先要继承SurfaceView,实现SurfaceHolder.Callback接口。
  • 重写方法:surfaceChanged:surface大小或格式发生变化时触发,在surfaceCreated调用后该函数至少会被调用一次。
  • surfaceCreated:Surface创建时触发,一般在这个函数开启绘图线程(新的线程,不要再这个线程中绘制Surface)。
  • surfaceDestroyed:销毁时触发,一般不可见时就会销毁。
  • 利用getHolder()获取SurfaceHolder对象,调用SurfaceHolder.addCallback添加回调
  • SurfaceHolder.lockCanvas 获取Canvas对象并锁定画布,调用Canvas绘图,SurfaceHolder.unlockCanvasAndPost 结束锁定画布,提交改变。

SurfaceHolder

SurfaceHolder回调自己的三个生命周期:

  • surfaceCreated(SurfaceHolder holder)创建时
  • surfaceChanged(SurfaceHolder holder, int format, int width, int height)改变时
  • surfaceDestroyed(SurfaceHolder holder)销毁时

SurfaceView播放视频并可以发送弹幕

需要准备两个SurfaceView,一个是负责播放视频,一个负责展示弹幕。弹幕是展示在视频上面的,所以我们要给展示弹幕的SurfaceView设置两个方法:
surfaceView_danmu.setZOrderOnTop(true); ——置于上方
surfaceView_danmuHolder.setFormat(PixelFormat.TRANSPARENT); ——背景透明

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
  
  public static final int PROGRESS = 101;

  SurfaceView surfaceView,surfaceView_danmu;
  SurfaceHolder surfaceHolder,surfaceView_danmuHolder;
  MediaPlayer mediaPlayer = new MediaPlayer();
  String path = "/sdcard/aa.mp4";
  SeekBar seekBar;
  Timer timer;
  EditText editText;
  Button button,bt_start,bt_stop;
  int x = 0;

  ArrayList list = new ArrayList<>();
  Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if (msg.what == PROGRESS){
        seekBar.setProgress((Integer) msg.obj);
        mediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {
          @Override
          public void onSeekComplete(MediaPlayer mp) {
            Toast.makeText(MainActivity.this, "播放完毕!", Toast.LENGTH_SHORT).show();
          }
        });
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    surfaceView = findViewById(R.id.sufaceview);
    surfaceView_danmu = findViewById(R.id.sufaceview_danmu);
    editText = findViewById(R.id.et);
    button = findViewById(R.id.bt_send);
    seekBar = findViewById(R.id.bar);
    bt_start = findViewById(R.id.bt_start);
    bt_stop = findViewById(R.id.bt_stop);

    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.addCallback(this);

    surfaceView_danmuHolder = surfaceView_danmu.getHolder();
    surfaceView_danmuHolder.addCallback(this);
    surfaceView_danmu.setZOrderOnTop(true);
    surfaceView_danmuHolder.setFormat(PixelFormat.TRANSPARENT);

    if (mediaPlayer!=null) {
      try {
        mediaPlayer.setDataSource(path);
        mediaPlayer.prepareAsync();
        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
          @Override
          public void onPrepared(MediaPlayer mp) {
            mediaPlayer.start();

            int duration = mediaPlayer.getDuration();
            seekBar.setMax(duration);

            timer = new Timer();
            timer.schedule(new TimerTask() {
              @Override
              public void run() {
                int currentPosition = mediaPlayer.getCurrentPosition();
                Message message = Message.obtain();
                message.what = PROGRESS;
                message.obj = currentPosition;
                handler.sendMessage(message);
              }
            },0,1000);
          }
        });

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
          @Override
          public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
          }

          @Override
          public void onStartTrackingTouch(SeekBar seekBar) {

          }

          @Override
          public void onStopTrackingTouch(SeekBar seekBar) {
            int progress = seekBar.getProgress();
            mediaPlayer.seekTo(progress);
          }
        });
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    
    button.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        String s = editText.getText().toString();
        int y= (int) (Math.random()*300);
        list.add(new DanMu(0,y,s,Color.RED));
      }
    });

    bt_start.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        mediaPlayer.start();
      }
    });

    bt_stop.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        mediaPlayer.pause();
      }
    });
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    if (holder == surfaceHolder) {
      mediaPlayer.setDisplay(holder);
    }else if (holder == surfaceView_danmuHolder) {
      new MyDanMuThread().start();
    }
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {

  }

  class MyDanMuThread extends Thread {
    @Override
    public void run() {
      super.run();
      Paint paint = new Paint();
      paint.setColor(Color.RED);
      paint.setTextSize(40);
      while (true) {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Canvas canvas = surfaceView_danmuHolder.lockCanvas();
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        for (int i=0;i

实现滚动歌词

要准备一个装有时间和歌词的集合,准备一个Timer计时器和一个该展示第几个歌词。每隔一秒去拿该歌词的下一个歌词时间与进度匹配,如果进度>=该时间,那么就画该歌词在SurfaceView上;<则不画该歌词

一定要写SD卡读写权限!!!!!

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{
  SurfaceView surfaceView;
  SurfaceHolder surfaceHolder;
  MediaPlayer mediaPlayer = new MediaPlayer();
  ArrayList list=new ArrayList<>();
  Timer timer;
  int count = 0;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    surfaceView = findViewById(R.id.sv);

    surfaceHolder=surfaceView.getHolder();
    surfaceHolder.addCallback(this);

    timer = new Timer();
    timer.schedule(new TimerTask() {
      @Override
      public void run() {
        int currentPosition = mediaPlayer.getCurrentPosition();
        if(list.size()>0){
          if(currentPosition>=list.get(count+1).getTime()){
            count++;
          }
        }

      }
    },0,500);

    try {
      mediaPlayer.setDataSource("/sdcard/凤凰传奇 - 最炫民族风(Live).mp3");
      mediaPlayer.prepareAsync();
      mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
          mediaPlayer.start();
        }
      });
    } catch (IOException e) {
      e.printStackTrace();
    }

    try {
      StringBuffer stringBuffer = new StringBuffer();
      FileInputStream fileInputStream = new FileInputStream("/sdcard/最炫民族风");
      byte[] bytes = new byte[1024];
      int len = 0;
      while ((len=fileInputStream.read(bytes))!=-1) {
        stringBuffer.append(new String(bytes,0,len));
      }

      Gson gson = new Gson();
      JavaBean javaBean = gson.fromJson(stringBuffer.toString(), JavaBean.class);
      JavaBean.LrcBean lrc = javaBean.getLrc();
      String lyric = lrc.getLyric();

      String[] split = lyric.split("\n");
      for(int i=0;i=2){
          String s = split1[0];
          String substring1 = s.substring(1, 3);
          String substring2 = s.substring(4, 6);
          String substring3 = s.substring(7, 10);
          long start = Integer.parseInt(substring1)*60*1000+Integer.parseInt(substring2)*1000+Integer.parseInt(substring3);
          String text = split1[1];
          list.add(new Music(text,start));
        }
      }
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    new MyThread().start();
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {

  }

  class MyThread extends Thread {
    @Override
    public void run() {
      super.run();
      Paint paint = new Paint();
      paint.setColor(Color.RED);
      paint.setTextSize(50);
      while (true){
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Canvas canvas = surfaceHolder.lockCanvas();
        if(canvas==null){
          break;
        }
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        if(list.size()>0){
          canvas.drawText(list.get(count).getTitle(),100,100,paint);
        }
        surfaceHolder.unlockCanvasAndPost(canvas);
      }
    }
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 多项目环境下的代码复用策略
    在前端开发中,面对多个并行项目的场景,如何有效地实现代码复用成为了一个重要的议题。本文将探讨一种利用npm包管理来实现跨项目组件共享的方法。 ... [详细]
  • 本文探讨了JavaScript的基本概念,包括其作为解释型、脚本语言的特点,以及它支持的面向对象编程(OOP)原则。重点介绍了封装、继承和多态等OOP特性,并详细解释了如何在JavaScript中创建和使用对象。 ... [详细]
  • 深入解析链表成环问题:剑指Offer第22天的新视角
    本文将详细介绍链表成环问题的多种解法,包括哈希表法、JSON.stringify特殊解法及双指针法,并提供详尽的代码示例。阅读本文,你不仅能够掌握这一经典算法问题的核心技巧,还能了解到更多编程思维的拓展。 ... [详细]
  • MySQL 8.0 新特性详解:免费视频教程上线
    本文介绍了一套在慕课网上发布的免费视频教程,深入解析 MySQL 8.0 的核心新功能,包括增强的安全性、用户管理、新的索引类型、CTE 和窗口函数等。 ... [详细]
  • 优雅地记录API调用时长
    本文旨在探讨如何高效且优雅地记录API接口的调用时长,通过实际案例和代码示例,帮助开发者理解并实施这一技术,提高系统的可观测性和调试效率。 ... [详细]
  • 本文介绍了两个重要的Node.js库——cache-content-type和mime-types,它们在处理HTTP响应头时非常有用。cache-content-type是基于mime-types构建的,并且实现了缓存机制以提高性能。 ... [详细]
  • Python脚本实现批量删除多种类型文件的扩展名
    本文介绍了一个Python脚本,用于批量处理并移除指定目录下不同格式文件(如png、jpg、xml、json、txt、gt等)的文件扩展名。该方法通过递归遍历文件夹中的所有文件,并对每个文件执行重命名操作。 ... [详细]
  • 本文详细介绍了Golang中string类型的内部结构及其特性,包括字符串的定义、表示方式、数据结构以及相关的操作方法,如字符串拼接和类型转换等。 ... [详细]
  • 华为云openEuler环境下的Web应用部署实践
    本文详细记录了在华为云openEuler系统上进行Web应用部署的具体步骤,包括配置yum源、安装Apache、MariaDB、PHP及其相关组件,并完成WordPress的安装与配置过程。 ... [详细]
  • 现在的新手程序猿,动不动就是框架,就连外面培训的也是框架,我就问一句,没了框架是不是就啥也不会了 ... [详细]
  • RabbitMQ 核心组件解析
    本文详细介绍了RabbitMQ的核心概念,包括其基本原理、应用场景及关键组件,如消息、生产者、消费者、信道、交换机、路由键和虚拟主机等。 ... [详细]
  • 本文探讨了如何在Sitecore 9环境中通过Postman使用API密钥发送请求,包括解决常见错误的方法。 ... [详细]
  • iOS 小组件开发指南
    本文详细介绍了iOS小部件(Widget)的开发流程,从环境搭建、证书配置到业务逻辑实现,提供了一系列实用的技术指导与代码示例。 ... [详细]
  • Kubernetes Services详解
    本文深入探讨了Kubernetes中的服务(Services)概念,解释了如何通过Services实现Pods之间的稳定通信,以及如何管理没有选择器的服务。 ... [详细]
  • 本文详细介绍了如何在VSCode环境中配置Prettier工具以支持TypeScript项目,同时结合ESLint实现代码风格的一致性和自动化格式化。 ... [详细]
author-avatar
小甜蜜陈诗蓉_614
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有