Android开发类似直播APP的弹幕和悬浮窗播放功能

Android开发类似直播APP的弹幕和悬浮窗播放功能

闲来无事,最近自己查网上资料开发可以发送弹幕和悬浮窗播放功能的APP,写的不好,轻喷。

一、弹幕功能主要使用哔哩哔哩的弹幕库进行开发的,可以发送自己输入的弹幕文字,还做了弹幕是否显示的开关。上代码:

package com.barrage.barragetest.activity;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.support.design.button.MaterialButton;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.VideoView;

import com.barrage.barragetest.R;

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 PlayVideoActivity extends Activity{

    private boolean showDanmaku;
    private DanmakuView danmakuView;
    private DanmakuContext danmakuContext;
    private VideoView videoView;
    private LinearLayout ll_linearlayout;
    private EditText et_write;
    private MaterialButton mb_send,mb_close;
    //视频地址
//    private String file_path = Environment.getExternalStorageDirectory() + "/DCIM/Camera/shipin.mp4";
    private String file_path = "android.resource://com.barrage.barragetest/" + R.raw.shipin;

    private BaseDanmakuParser parser = new BaseDanmakuParser() {
        @Override
        protected IDanmakus parse() {
            return new Danmakus();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //隐去标题栏(应用程序的名字)
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //隐去状态栏部分(电池等图标和一起修饰部分)
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏
        setContentView(R.layout.activity_play_video);
        initView();
    }

    //初始化控件
    private void initView(){
        ll_linearlayout = (LinearLayout) findViewById(R.id.ll_linearlayout);  //弹幕输入框
        et_write = (EditText) findViewById(R.id.et_write);  //弹幕输入框
        mb_send = (MaterialButton) findViewById(R.id.mb_send);  //发送弹幕
        mb_close = (MaterialButton) findViewById(R.id.mb_close);  //关闭或打开弹幕
        videoView = (VideoView) findViewById(R.id.video_view);
        videoView.setVideoPath(file_path);  //加载视频
        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);

        initEvent();
    }

    //点击事件
    private void initEvent(){
        //发送弹幕
        mb_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!"".equals(et_write.getText().toString())) {
                    addDanmaku(et_write.getText().toString(),true);
                    et_write.setText("");
                }
            }
        });
        //控制弹幕开关
        mb_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //关闭弹幕
                if ("关闭".equals(mb_close.getText().toString())) {
                    showDanmaku = false;
                    mb_close.setText("打开");
                }else{//打开弹幕
                    showDanmaku = true;
                    mb_close.setText("关闭");
                    generateSomeDanmaku();
                }
            }
        });
        danmakuView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (ll_linearlayout.getVisibility()== View.VISIBLE) { //已显示
                    ll_linearlayout.setVisibility(View.GONE);
                }else{ //隐藏
                    ll_linearlayout.setVisibility(View.VISIBLE);
                }
            }
        });
    }

    /**
     * 向弹幕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;
            danmaku.textColor = Color.BLUE;
        }
        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;
        }
    }
}

页面布局activity_play_video.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" />

    <LinearLayout
        android:id="@+id/ll_linearlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:layout_margin="8dp"
        android:background="#ffffff"
        android:visibility="gone">
        <EditText
            android:id="@+id/et_write"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <android.support.design.button.MaterialButton
            android:id="@+id/mb_send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送"
            android:theme="@style/Theme.MaterialComponents.Light"/>
        <android.support.design.button.MaterialButton
            android:id="@+id/mb_close"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="关闭"
            android:theme="@style/Theme.MaterialComponents.Light"/>
    </LinearLayout>

</RelativeLayout>

注意:1、不要忘了权限申请;2、这里我把视频文件放在项目里,所以视频路径是项目资源文件路径。
效果图如下:
在这里插入图片描述
二、悬浮窗播放功能。该功能需要悬浮窗权限SYSTEM_ALERT_WINDOW,这是两大特殊权限之一,需要手动设置。

写个按钮直接调用showWindow()方法就行,完整的页面代码我就不写出来了

private WindowManager mWindowManager;
private WindowManager.LayoutParams mLayout;
// 窗口宽高值
private float x, y;
//悬浮窗口布局
private View mWindowsView;
//显示悬浮窗口
public void showWindow() {
    //先检查是否具有悬浮窗权限
    if (!Settings.canDrawOverlays(this)) {
        Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
        startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 0);
    } else {
        // 取得系统窗体
        mWindowManager = (WindowManager) getApplicationContext()
                .getSystemService(WINDOW_SERVICE);
        // 窗体的布局样式
        mLayout = new WindowManager.LayoutParams();
        // 设置窗体显示类型——TYPE_SYSTEM_ALERT(系统提示)
        mLayout.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        // 设置窗体焦点及触摸:
        // FLAG_NOT_FOCUSABLE(不能获得按键输入焦点)
        mLayout.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        // 设置显示的模式
        mLayout.format = PixelFormat.RGBA_8888;
        // 设置对齐的方法
        mLayout.gravity = Gravity.TOP | Gravity.LEFT;
        // 设置窗体宽度和高度
        // 设置视频的播放窗口大小
        mLayout.width = 700;
        mLayout.height = 400;
        mLayout.x = 300;
        mLayout.y = 300;
        //将指定View解析后添加到窗口管理器里面
        mWindowsView = View.inflate(this, R.layout.layout_window, null);
        VideoView vv_float_video = (VideoView) mWindowsView.findViewById(R.id.vv_float_video);
        mWindowsView.findViewById(R.id.iv_close).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                closeWindow();
            }
        });
        playVideo(vv_float_video);
        mWindowManager.addView(mWindowsView, mLayout);
        mWindowsView.setOnTouchListener(new View.OnTouchListener() {
            float mTouchStartX;
            float mTouchStartY;
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                x = event.getRawX();
                y = event.getRawY() - 25;//25状态栏大小
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        mTouchStartX = event.getX();
                        mTouchStartY = event.getY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        //原始坐标减去移动坐标
                        mLayout.x = (int) (x - mTouchStartX);
                        mLayout.y = (int) (y - mTouchStartY);
                        mWindowManager.updateViewLayout(mWindowsView, mLayout);
                        Log.i("main", "x值=" + x + "\ny值=" + y + "\nmTouchX" + mTouchStartX + "\nmTouchY=" + mTouchStartY);
                        break;
                }
                return true;
            }
        });
    }
}

//播放视频
private void playVideo(VideoView videoView) {
    //获取本地视频文件进行播放
    ContentResolver resolver = getContentResolver();
    Cursor c = resolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
    if (c.moveToNext()) {
        String path = c.getString(c.getColumnIndex(MediaStore.Video.Media.DATA));
        videoView.setVideoPath(path);
        videoView.requestFocus();
        videoView.start();
    }
}

//关闭窗口点击事件
public void closeWindow() {
    mWindowManager.removeView(mWindowsView);
}

窗口页面布局layout_window.xml

<?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">

    <VideoView
        android:id="@+id/vv_float_video"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/iv_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:src="@mipmap/ic_close" />
</RelativeLayout>

效果图如下:

在这里插入图片描述
这是完整代码地址:https://github.com/HaiTaoFeng/BarrageTest

发布了20 篇原创文章 · 获赞 26 · 访问量 9448

猜你喜欢

转载自blog.csdn.net/weixin_42574892/article/details/95965091