Android实现视频弹幕功能

本文实例为大家分享了Android视频弹幕的具体代码,供大家参考,具体内容如下

效果图:

上图:代码随机生成的弹幕及弹幕输入栏

下图:绿色框的弹幕为用户手动添加发送的弹幕

1.准备工作

准备一个视频文件,将该视频文件放到res/raw目录下。

需要将视频设置为横屏播放,即往配置文件中添加android:screenOrientation="landscape":

<activity android:name=".MainActivity"
  android:configChanges="keyboardHidden|orientation|screenLayout|screenSize"
  android:screenOrientation="landscape">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

这里用到了哔哩哔哩开源的弹幕效果库DanmakuFlameMaster,需要配置到模块的build.gradle的dependencies中

注:DanmakuFlameMaster的版本最好使用在0.9以上,否则会存在一些弹幕bug

2.布局

使用一个相对布局,弹幕浮于视频之上,底部是弹幕文字输入栏,右下角为弹幕发送按钮:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent" android:layout_height="match_parent"
  android:background="#000000">

  <VideoView
    android:id="@+id/video_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_centerInParent="true"/>

  <master.flame.danmaku.ui.widget.DanmakuView
    android:id="@+id/danmu"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  <LinearLayout
    android:id="@+id/operate_layout"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_alignParentBottom="true"
    android:visibility="gone">
    <EditText
      android:id="@+id/edit"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:layout_marginLeft="50dp"
      android:textColor="#ffffff"
      android:imeOptions="flagNoExtractUi"
      />
    <Button
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:id="@+id/send"
      android:textSize="20sp"
      android:background="#00000000"
      android:textColor="#ffffff"
      android:text="发送"/>
  </LinearLayout>
</RelativeLayout>

3.视频弹幕的实现

<1>播放视频使用VideoView来进行播放;

<2>关于弹幕库的使用,需要创建一个DanmakuContext的实例和一个弹幕的解析器(这里直接创建了一个全局的BaseDanmakuParser),创建完成后就可以调用DanmakuView的prepare()方法了,调用这一方法后会自动调用回调函数中的prepared()方法,在这个方法中调用了start方法,弹幕就此开始工作了;

<3>需要在onPause()、onResume()、onDestroy()方法中执行一些操作,以保证DanmakuView的资源可以得到释放。

下面附上完整代码

package com.mega.mydanmudemo;

import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
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 showDanma;
  private VideoView mVideoView;
  private DanmakuView mDanmu;
  private BaseDanmakuParser mBaseDanmakuParser = new BaseDanmakuParser() {//弹幕解析器
    @Override
    protected IDanmakus parse() {
      return new Danmakus();
    }
  };
  private DanmakuContext danmakuContext;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    init();
    setOnListener();
  }

  /***
   * 一些初始化工作
   */
  private void init() {
    String uri = "android.resource://" + getPackageName() + "/" + R.raw.danmu;
    mVideoView = (VideoView)findViewById(R.id.video_view);
    mVideoView.setVideoPath(uri);
    mVideoView.start();

    mDanmu = (DanmakuView)findViewById(R.id.danmu);
    mDanmu.enableDanmakuDrawingCache(true);
    danmakuContext = DanmakuContext.create();
    danmakuContext.setScaleTextSize(1.1f);
    mDanmu.prepare(mBaseDanmakuParser,danmakuContext);
  }

  /***
   * 弹幕的准备工作,发送按钮监听。。
   */
  private void setOnListener(){

    mDanmu.setCallback(new DrawHandler.Callback() {
      @Override
      public void prepared() {
        showDanma = true;
        mDanmu.start();//启动弹幕
        generateSomeDanmu();
      }

      @Override
      public void updateTimer(DanmakuTimer timer) {

      }

      @Override
      public void danmakuShown(BaseDanmaku danmaku) {

      }

      @Override
      public void drawingFinished() {

      }
    });

    final LinearLayout operate_view = (LinearLayout)findViewById(R.id.operate_layout);
    Button send = (Button)findViewById(R.id.send);
    final EditText editText = (EditText)findViewById(R.id.edit);

    mDanmu.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        if (operate_view.getVisibility() == View.GONE){
          operate_view.setVisibility(View.VISIBLE);
        }else{
          operate_view.setVisibility(View.GONE);
        }
      }
    });
    send.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        String content = editText.getText().toString();
        if (!TextUtils.isEmpty(content)){
          InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
          imm.hideSoftInputFromWindow(editText.getWindowToken(),0);
          addDamu(content,true);
          editText.setText("");
          operate_view.setVisibility(View.GONE);
        }
      }
    });

    getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
      @Override
      public void onSystemUiVisibilityChange(int visibility) {
        if (visibility == View.SYSTEM_UI_FLAG_VISIBLE){
          onWindowFocusChanged(true);
        }
      }
    });
  }

  /***
   * 随机产生一些弹幕
   */
  private void generateSomeDanmu() {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while (showDanma){
          int time = new Random().nextInt(300);
          String content = "" + time;
          addDamu(content,false);
          try {
            Thread.sleep(time);
          }catch (Exception e){
            e.printStackTrace();
          }
        }
      }
    }).start();
  }

  /***
   * 添加弹幕的方法
   * @param content 弹幕的内容
   * @param isSelf 是否是用户发送的弹幕
   */
  private void addDamu(String content,boolean isSelf) {
    BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
    danmaku.text = content;
    danmaku.padding = 5;
    danmaku.priority = 0;
    danmaku.textSize = sp2px(20);
    danmaku.setTime(mDanmu.getCurrentTime());
    danmaku.textColor = Color.argb(new Random().nextInt(256), new Random().nextInt(256),
        new Random().nextInt(256),new Random().nextInt(256));
    if (isSelf){
      danmaku.borderColor = Color.GREEN;
    }
    mDanmu.addDanmaku(danmaku);
  }

  private float sp2px(int i) {
    final float fontScale = getResources().getDisplayMetrics().scaledDensity;
    return (int)(i* fontScale +0.5f);
  }

  @Override
  protected void onPause() {
    super.onPause();
    if (mDanmu!= null && mDanmu.isPrepared()){
      mDanmu.pause();
    }
  }

  @Override
  protected void onResume() {
    super.onResume();
    if (mDanmu!=null&& mDanmu.isPrepared()&& mDanmu.isPaused()){
      mDanmu.resume();
    }
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    showDanma = false;
    if (mDanmu != null){
      mDanmu.release();
      mDanmu = null;
    }
  }

  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && Build.VERSION.SDK_INT>=19){
      View deview = getWindow().getDecorView();
      deview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
      |View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
      |View.SYSTEM_UI_FLAG_FULLSCREEN
      |View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
      |View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
      |View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }
  }
}

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

(0)

相关推荐

  • Android EasyBarrage实现轻量级弹幕效果

    本文介绍了Android EasyBarrage实现轻量级弹幕效果,分享给大家,具体如下: 概述 EasyBarrage是Android平台的一种轻量级弹幕效果目前支持以下设置: 自定义字体颜色,支持随机颜色: 自定义字体大小,支持随机字体大小: 支持边框显示,用于区分自己的弹幕和其他弹幕: 自定义边框颜色: 弹幕数据是否允许重复: 自定义单屏显示的最大弹幕数量: 数据不重叠: 支持动态添加弹幕: 不依赖VideoView,数据自动循环显示. github:https://github.com/

  • Android仿斗鱼直播的弹幕效果

    记得之前有位朋友在我的公众号里问过我,像直播的那种弹幕功能该如何实现?如今直播行业确实是非常火爆啊,大大小小的公司都要涉足一下直播的领域,用斗鱼的话来讲,现在就是千播之战.而弹幕则无疑是直播功能当中最为重要的一个功能之一,那么今天,我就带着大家一起来实现一个简单的Android端弹幕效果. 分析 首先我们来看一下斗鱼上的弹幕效果,如下图所示: 这是一个Dota2游戏直播的界面,我们可以看到,在游戏界面的上方有很多的弹幕,看直播的观众们就是在这里进行讨论的. 那么这样的一个界面该如何实现呢?其实并

  • 很棒的Android弹幕效果实例

    很多项目需要用到弹幕效果,尤其是在播放视频的时候需要一起显示别人发的弹幕,也包括自己的发的. 今天就试着写了一下这个效果. 思路就是将从右往左的动画效果,字体内容,字体大小,弹幕平移速度等属性一起与TextView封装成BarrageItem,并将控制效果与BarrageItem绑定在BarrageView进行显示.思路还是比较简单的.这里没有考虑到带有表情的弹幕,我会持续更新的. 先看效果: 项目目录结构: 接下来定义Barrageitem.class : 这个类就将TextView与从右往左

  • Android弹幕框架 黑暗火焰使基本使用方法

    今天我将分享由BiliBili开源的Android弹幕框架(DanmakuFlameMaster)的学习经验. 我是将整个框架以model的形式引入项目中的,这样更方便的观察源码.也可以通过依赖的方式注入进来 dependencies { compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3' } 先放一下我要做成的效果图: 页面分析 从上图来看,整个UI分成了三层.最下面是视频层,中间是弹幕层,顶层是控制层.现在市场上主流的视频直播软件大多都是这

  • Android自制精彩弹幕效果

    好久没有写过文章,最近发现直播特别的火,很多app都集成了直播的功能,发现有些直播是带有弹幕的,效果还不错,今天心血来潮,特地写了篇制作弹幕的文章. 今天要实现的效果如下: 1.弹幕垂直方向固定 2.弹幕垂直方向随机 上面效果图中白色的背景就是弹幕本身,是一个自定义的FrameLayout,我这里是为了更好的展示弹幕的位置才设置成了白色,当然如果是叠加在VideoView上的话,就需要设置成透明色了. 制作弹幕需要考虑以下几点问题: 1.弹幕的大小可以随意调整 2.弹幕内移动的item(或者称字

  • 实例解析如何在Android应用中实现弹幕动画效果

    在B站或者其他视频网站看视频时,常常会打开弹幕效果,边看节目边看大家的吐槽.弹幕看起来很有意思,今天我们就来实现一个简单的弹幕效果. 从直观上,弹幕效果就是在一个ViewGroup上增加一些View,然后让这些View移动起来.所以,整体的实现思路大概是这样的: 1.定义一个RelativeLayout,在里面动态添加TextView. 2.这些TextView的字体大小.颜色.移动速度.初始位置都是随机的. 3.将TextView添加到RelativeLayout的右边缘,每隔一段时间添加一个

  • Android自定义View实现弹幕效果

    在很多视频直播中都有弹幕功能,而安卓上没有简单好用的弹幕控件,本文介绍一个自定义弹幕view的demo. 效果图: 思路: 1.自定义Textitem类表示弹幕的信息 2.自定义view继承view,使用ArrayList保存每条Textitem 3.随机生成坐标点绘制每条TextItem,不断变换Text的横坐标实现弹幕的滚动 首先创建弹幕类,弹幕包括坐标,颜色,滚动速度,以及文字内容: public class Textitem { private String content; priva

  • Android 实现仿网络直播弹幕功能详解及实例

    Android 网络直播弹幕                最近看好多网络电视,播放器及直播都有弹幕功能,自己周末捣鼓下并实现,以下是网上的资料,大家可以看下. 现在网络直播越来越火,网络主播也逐渐成为一种新兴职业,对于网络直播,弹幕功能是必须要有的,如下图: 首先来分析一下,这个弹幕功能是怎么实现的,首先在最下面肯定是一个游戏界面View,然后游戏界面上有弹幕View,弹幕的View必须要做成完全透明的,这样即使覆盖在游戏界面的上方也不会影响到游戏的正常观看,只有当有人发弹幕消息时,再将消息绘

  • Android双重SurfaceView实现弹幕效果

    本文实例为大家分享了Android双重SurfaceView实现弹幕效果的具体代码,供大家参考,具体内容如下 页面布局 首先是XML的layout布局,这里的总的父布局是一个FrameLayout用于贴上两个SurfaceView,一个用来播放视频,一个用来显示弹幕 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.andro

  • Android简单实现弹幕效果

    本文实例为大家分享了Android实现弹幕效果的具体代码,供大家参考,具体内容如下 首先分析一下,他是由三层布局来共同完成的,第一层视频布局,第二层字幕布局,第三层输入框布局,要想让这三个布局在同一页面上,必须用相对布局或帧布局. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/

  • Android实现炫酷的网络直播弹幕功能

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

  • Android实现自定义的弹幕效果

    一.效果图 先来看看效果图吧~~ 二.实现原理方案 1.自定义ViewGroup-XCDanmuView,继承RelativeLayout来实现,当然也可以继承其他三大布局类哈 2.初始化若干个TextView(弹幕的item View,这里以TextView 为例,当然也可以其他了~),然后通过addView添加到自定义View中 3.通过addView添加到XCDanmuView中,位置在坐标,为了实现 从屏幕外移动进来的效果 我们还需要修改添加进来TextView的位置,以从右向左移动方向

  • Android编程实现简易弹幕效果示例【附demo源码下载】

    本文实例讲述了Android编程实现简易弹幕效果.分享给大家供大家参考,具体如下: 首先上效果图,类似于360检测到骚扰电话页面: 布局很简单,上面是一个RelativeLayout,下面一个Button. 功能: (1)弹幕生成后自动从右侧往左侧滚动(TranslateAnimation),弹幕消失后立刻被移除. (2)弹幕位置随机出现,并且不重复(防止文字重叠). (3)字体大小在一定范围内随机改变,字体颜色也可以设置. (4)自定义先减速,后加速的Interpolator,弹幕加速进入.减

随机推荐