• Android的弹幕功能实现(三)


    实现弹幕效果

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

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

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

    话说现在使用Android Studio来引入一些开源库真的非常方便,只需要在build.gradle文件里面添加开源库的依赖就可以了。那么我们修改app/build.gradle文件,并在dependencies闭包中添加如下依赖:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:24.2.1'
        testCompile 'junit:junit:4.12'
        compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3'
    }

    这样我们就将DanmakuFlameMaster库引入到当前项目中了。然后修改activity_main.xml中的代码,如下所示:

    <RelativeLayout
        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">
    
        <VideoView
            android:id="@+id/video_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"/>
    
        <master.flame.danmaku.ui.widget.DanmakuView
            android:id="@+id/danmaku_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </RelativeLayout>

    可以看到,这里在RelativeLayout中加入了一个DanmakuView控件,这个控件就是用于显示弹幕信息的了。注意一定要将DanmakuView写在VideoView的下面,因为RelativeLayout中后添加的控件会被覆盖在上面。

    接下来修改MainActivity中的代码,我们在这里加入弹幕显示的逻辑,如下所示:

    public class MainActivity extends AppCompatActivity {
    
        private boolean showDanmaku;
    
        private DanmakuView danmakuView;
    
        private DanmakuContext danmakuContext;
    
        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() + "/Pixels.mp4");
            videoView.start();
            danmakuView = (DanmakuView) findViewById(R.id.danmaku_view);
            danmakuView.enableDanmakuDrawingCache(true);
            danmakuView.setCallback(new DrawHandler.Callback() {
                @Override
                public void prepared() {
                    showDanmaku = true;
                    danmakuView.start();
                    generateSomeDanmaku();
                }
    
                @Override
                public void updateTimer(DanmakuTimer timer) {
    
                }
    
                @Override
                public void danmakuShown(BaseDanmaku danmaku) {
    
                }
    
                @Override
                public void drawingFinished() {
    
                }
            });
            danmakuContext = DanmakuContext.create();
            danmakuView.prepare(parser, danmakuContext);
        }
    
        /**
         * 向弹幕View中添加一条弹幕
         * @param content
         *          弹幕的具体内容
         * @param  withBorder
         *          弹幕是否有边框
         */
        private void addDanmaku(String content, boolean withBorder) {
            BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
            danmaku.text = content;
            danmaku.padding = 5;
            danmaku.textSize = sp2px(20);
            danmaku.textColor = Color.WHITE;
            danmaku.setTime(danmakuView.getCurrentTime());
            if (withBorder) {
                danmaku.borderColor = Color.GREEN;
            }
            danmakuView.addDanmaku(danmaku);
        }
    
        /**
         * 随机生成一些弹幕内容以供测试
         */
        private void generateSomeDanmaku() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(showDanmaku) {
                        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 (danmakuView != null && danmakuView.isPrepared()) {
                danmakuView.pause();
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (danmakuView != null && danmakuView.isPrepared() && danmakuView.isPaused()) {
                danmakuView.resume();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            showDanmaku = false;
            if (danmakuView != null) {
                danmakuView.release();
                danmakuView = null;
            }
        }
    
        ......
    
    }

    可以看到,在onCreate()方法中我们先是获取到了DanmakuView控件的实例,然后调用了enableDanmakuDrawingCache()方法来提升绘制效率,又调用了setCallback()方法来设置回调函数。

    接着调用DanmakuContext.create()方法创建了一个DanmakuContext的实例,DanmakuContext可以用于对弹幕的各种全局配置进行设定,如设置字体、设置最大显示行数等。这里我们并没有什么特殊的要求,因此一切都保持默认。

    另外我们还需要创建一个弹幕的解析器才行,这里直接创建了一个全局的BaseDanmakuParser。

    有了DanmakuContext和BaseDanmakuParser,接下来我们就可以调用DanmakuView的prepare()方法来进行准备,准备完成后会自动调用刚才设置的回调函数中的prepared()方法,然后我们在这里再调用DanmakuView的start()方法,这样DanmakuView就可以开始正常工作了。

    虽说DanmakuView已经在正常工作了,但是屏幕上没有任何弹幕信息的话我们也看不出效果,因此我们还要增加一个添加弹幕消息的功能。

    观察addDanmaku()方法,这个方法就是用于向DanmakuView中添加一条弹幕消息的。其中首先调用了createDanmaku()方法来创建一个BaseDanmaku实例,TYPE_SCROLL_RL表示这是一条从右向左滚动的弹幕,然后我们就可以对弹幕的内容、字体大小、颜色、显示时间等各种细节进行配置了。注意addDanmaku()方法中有一个withBorder参数,这个参数用于指定弹幕消息是否带有边框,这样才好将自己发送的弹幕和别人发送的弹幕进行区分。

    这样我们就把最基本的弹幕功能就完成了,现在只需要当在接收到别人发送的弹幕消息时,调用addDanmaku()方法将这条弹幕添加到DanmakuView上就可以了。但接收别人发送来的消息又涉及到了即时通讯技术,显然这一篇文章中不可能将复杂的即时通讯技术也进行讲解,因此这里我专门写了一个generateSomeDanmaku()方法来随机生成一些弹幕消息,这样就可以模拟出和斗鱼类似的弹幕效果了。

    除此之外,我们还需要在onPause()、onResume()、onDestroy()方法中进行一些逻辑处理,以保证DanmakuView的资源可以得到释放。

    现在重新运行一下程序,效果如下图所示:

     

     这样我们就把第二步的功能也实现了。

  • 相关阅读:
    PetaLinux 生成 Zynq 操作系统
    单片机、微控制器和微处理器有什么区别
    嵌入式基础概念系列(1) —— GPIO
    学中杂记
    Spring学习笔记
    jdbc一点小笔记
    JSP学习
    Servlet学习的一些笔记
    接触Struts2的ModelDriven<>接口
    android-dialog的位置
  • 原文地址:https://www.cnblogs.com/wangdayang/p/14913306.html
Copyright © 2020-2023  润新知