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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章