1. 程式人生 > >安卓漫漫路之實現簡單的彈幕.

安卓漫漫路之實現簡單的彈幕.

直播和看視訊中越來越火的控制元件---彈幕(Danmaku)

本文即介紹怎樣實現簡單的彈幕效果:咱們使用的是嗶哩嗶哩開源的彈幕效果庫 DanmakuFlameMaster.


必需:首先咱們在專案主工程app/build.gradle中的dependencies閉包中新增如下依賴:

	compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3'
這樣我們就將DanmakuFlameMaster庫引入到當前專案中了.

開始展示Demo的程式碼給大家:


首先看咱們的佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<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/operation_layout"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:background="#fff"
        android:visibility="gone">

        <EditText
            android:id="@+id/edit_text"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="傳送" />
    </LinearLayout>

</RelativeLayout>
.
背景色為黑色,承載我是用的Video來播放本地或者網路視訊,DanmakuView即是咱們的彈幕控制元件,LinearLayout內包含輸入框和傳送按鈕.當然這些都是最普通的控制元件,最終的介面當然是由您來設定.

接下來看咱們的MainActivity的程式碼,如下:
.
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
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 showDanmaku;
    //彈幕控制元件
    private DanmakuView danmakuView;
    //DanmakuContext  字型例項
    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控制元件
        final VideoView videoView = (VideoView) findViewById(R.id.video_view);
        //指定好VideoView的本地路徑地址  SD卡根目錄的xxx.mp4檔案
        videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/xxx.mp4");
        //訪問網路視訊
        //Uri uri = Uri.parse("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4");
        //設定視訊控制器
        //videoView.setMediaController(new MediaController(this));
        //設定視訊路徑
        // videoView.setVideoURI(uri);
        //開始播放
        videoView.start();

        new android.os.Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                videoView.pause();
            }
        }, 0);

        //初始化彈幕控制元件
        danmakuView = (DanmakuView) findViewById(R.id.danmaku_view);
        //預設為true 在模擬器上執行有問題
        danmakuView.enableDanmakuDrawingCache(true);
        //看原始碼得知是一個介面    怎麼實現還是要咱們去重寫其中的方法
        danmakuView.setCallback(new DrawHandler.Callback() {
            @Override
            public void prepared() {
                //把變數置為 true
                showDanmaku = true;
                //開始執行彈幕控制元件
                danmakuView.start();
                //隨機生成一些彈幕內容以供測試
                generateSomeDanmaku();
            }

            @Override
            public void updateTimer(DanmakuTimer timer) {

            }

            @Override
            public void danmakuShown(BaseDanmaku danmaku) {

            }

            @Override
            public void drawingFinished() {

            }
        });
        //呼叫  DanmakuContext.create() 完成DanmakuContext的例項化.
        danmakuContext = DanmakuContext.create();
        danmakuView.prepare(parser, danmakuContext);
        //初始化含有輸入框和按鈕的線性佈局
        final LinearLayout operationLayout = (LinearLayout) findViewById(R.id.operation_layout);
        //初始化傳送按鈕
        final Button send = (Button) findViewById(R.id.send);
        //輸入框輸入內容
        final EditText editText = (EditText) findViewById(R.id.edit_text);
        //適時的讓輸入框顯現或隱藏
        danmakuView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (operationLayout.getVisibility() == View.GONE) {
                    operationLayout.setVisibility(View.VISIBLE);
                } else {
                    operationLayout.setVisibility(View.GONE);
                }
            }
        });
        //傳送按鈕的點選事件
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String content = editText.getText().toString();
                if (!TextUtils.isEmpty(content)) {
                    //因為是自己的內容,所以傳一個true過去,方法內部會判斷這個變數
                    addDanmaku(content, true);
                    //再把輸入框置為空
                    editText.setText("");
                }
            }
        });
        //獲取到窗體的頂級父類並設定狀態列的顯示隱藏
        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                if (visibility == View.SYSTEM_UI_FLAG_VISIBLE) { //顯示狀態列,Activity不全屏顯示
                    onWindowFocusChanged(true);
                }
            }
        });
    }

    /**
     * 向彈幕View中新增一條彈幕 
     *
     * @param content    彈幕的具體內容
     * @param withBorder 彈幕是否有邊框
     */
    private void addDanmaku(String content, boolean withBorder) {
        //BaseDanmaku 您可以點選進入檢視原始碼實現
        // 彈幕的相關設定:彈幕優先順序  顏色  時長  文字  Z軸  Y軸  陰影  描邊
        //                下劃線  內邊距  寬度  高度  存活時間  是否是直播彈幕
        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()); //顯示時長 偏移時間
        //如果是true 證明是自己的彈幕,那麼就可以更改自己想要的顏色了
        if (withBorder) {
            danmaku.borderColor = Color.GREEN;
        }
        //呼叫底層程式碼 把彈幕內容新增到LinkedList<Long> mDrawTimes;
        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//表示Activity正在停止.
    protected void onPause() {
        super.onPause();
        //如果彈幕控制元件不為空 && 彈幕控制元件的執行緒還存活
        if (danmakuView != null && danmakuView.isPrepared()) {
            //暫停執行彈幕控制元件
            danmakuView.pause();
        }
    }

    @Override//表示Activity前臺並可與使用者互動.
    protected void onResume() {
        super.onResume();
        if (danmakuView != null && danmakuView.isPrepared() && danmakuView.isPaused()) {
            danmakuView.resume();
        }
    }

    @Override//表示Activity即將被銷燬.
    protected void onDestroy() {
        super.onDestroy();
        //把變數置為false
        showDanmaku = false;
        //如果彈幕控制元件還存在.呼叫release(); 底層呼叫stop(),並把底層的LinkedList<Long> mDrawTimes 置為空;
        if (danmakuView != null) {
            danmakuView.release();
            danmakuView = null;
        }
    }

    @Override//都說這個函式才會使使用者可以與應用真正開始進行互動.
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        //看了底層後得知  Build.VERSION.SDK_INT == 20 ;
        if (hasFocus && Build.VERSION.SDK_INT >= 19) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                        //這個標誌來幫助你的應用維持一個穩定的佈局.
                     View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        //Activity全屏顯示,但狀態列不會被隱藏覆蓋,狀態列依然可見,Activity頂端佈局部分會被狀態遮住。
                   | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        //ctivity全屏顯示,但狀態列不會被隱藏覆蓋,狀態列依然可見,Activity頂端佈局部分會被狀態遮住.
                   | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        //ctivity全屏顯示,但狀態列不會被隱藏覆蓋,狀態列依然可見,Activity頂端佈局部分會被狀態遮住.
                   | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        //Activity全屏顯示,且狀態列被隱藏覆蓋掉.
                   | View.SYSTEM_UI_FLAG_FULLSCREEN
                        //安卓4.4 新增.
                   | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    }
}





如上,在安卓裝置上,一個簡單的彈幕Demo就可以實現了.

如有問題請多指正,您的指正使我更正確的前行.