Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 實現bilibili彈幕功能。

實現bilibili彈幕功能。

編輯:關於Android編程

在bilibili推出彈幕功能,我也愛上了邊看視頻邊看吐槽了,現在讓我們也來實現這一個功能吧。

首先我們要整理一下思緒我們大概需要實現哪個細節板塊呢。

我們最直觀的看來,彈幕就是總右往左出現到消失。我們要實現這個動畫,彈幕的大小,顏色,出現方式,加速,彈幕的不重疊(這個我想了好久還沒有實現,有實現方法可以聯系下我)。

我們先來了解一下等會程序裡面會用到的相關知識點,等會看代碼會更輕松一點。

/*getHeight跟getMeasureHeight的區別
         * 實際上在當屏幕可以包裹內容的時候,他們的值相等,只有當view超出屏幕後,才能看出他們的區別:
         * getMeasuredHeight()是實際View的大小,與屏幕無關,而getHeight的大小此時則是屏幕的大小。
         * 當超出屏幕後, getMeasuredHeight() 等於 getHeight()加上屏幕之外沒有顯示的大小 
         * 
         * */
/Activity生命周期中,onStart, onResume, onCreate都不是真正visible的時間點,真正的visible時間點是onWindowFocusChanged()函數被執行時。
    //當你屏幕的焦點發生變化時候,想要操作什麼也完全可以在這個方法裡面執行
        // Interpolator 被用來修飾動畫效果,定義動畫的變化率,可以使存在的動畫效果accelerated(加速),decelerated(減速),repeated(重復),bounced(彈跳)等。
        /*
         * AccelerateDecelerateInterpolator 在動畫開始與結束的地方速率改變比較慢,在中間的時候加速
          AccelerateInterpolator  在動畫開始的地方速率改變比較慢,然後開始加速
              AnticipateInterpolator 開始的時候向後然後向前甩
          AnticipateOvershootInterpolator 開始的時候向後然後向前甩一定值後返回最後的值
              BounceInterpolator   動畫結束的時候彈起
            CycleInterpolator 動畫循環播放特定的次數,速率改變沿著正弦曲線
          DecelerateInterpolator 在動畫開始的地方快然後慢
            LinearInterpolator   以常量速率改變
            OvershootInterpolator    向前甩一定值後再回到原來位置
         * fillBefore是指動畫結束時畫面停留在此動畫的第一幀;
           fillAfter是指動畫結束是畫面停留在此動畫的最後一幀。
       Java代碼設置如下:
      /*****動畫結束時,停留在最後一幀*********
          setFillAfter(true);
          setFillBefore(false);  
    /*****動畫結束時,停留在第一幀*********
       setFillAfter(false);
        setFillBefore(true);  
         *
       

下面我們就來看一下彈幕實現的效果。

\

彈幕會出現重疊,這個問題還未解決

讓我們開始看代碼結構吧。

\

我們字體顏色的xml都寫在了colors.xml中了,BarrageItem裡面存放著我們的一些變量,而核心代碼都在View中

BraagetItem.java

package com.example.bibibibibibibibi;

import android.widget.TextView;

import android.widget.TextView;

/**
 * Created by lixueyong on 16/2/19.
 */
public class BarrageItem {
	    public TextView textView;//文本框
    public int textColor;//文本顏色
    public String text;//文本對象
    public int textSize;//文本的大小
    public int moveSpeed;//移動速度
    public int verticalPos;//垂直方向顯示的位置
    public int textMeasuredWidth;//字體顯示占據的寬度
}

在BarrageItem裡面處理了彈幕的速度,大小,顏色,動畫,等事件 在這個文件中 我注釋的內容是我對彈幕重疊的操作代碼,但是除了問題,有興趣的可以看一下

package com.example.bibibibibibibibi;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import com.example.bibibibibibibibi.R.integer;
/**
 * Created by nzx on 16/5/30.
 */
public class BarrageView extends RelativeLayout {
    private Context mContext;
    private BarrageHandler mHandler = new BarrageHandler();
    private Random random = new Random(System.currentTimeMillis());//System.currentTimeMillis()產生一個當前的毫秒
    private static final long BARRAGE_GAP_MIN_DURATION = 1000;//兩個彈幕的最小間隔時間
    private static final long BARRAGE_GAP_MAX_DURATION = 2000;//兩個彈幕的最大間隔時間
    private int maxSpeed = 10000;//速度,ms
    private int minSpeed = 5000;//速度,ms
    private int maxSize = 30;//文字大小,dp
    private int minSize = 15;//文字大小,dp
    private int totalHeight = 0;//整個的高度
    private int lineHeight = 0;//每一行彈幕的高度
    private int totalLine = 0;//彈幕的行數
    private String[] itemText = {"大頭死變態", "老圩人最屌了", "唉這把中單是火男,難玩了", "大頭是傻子", "世界上最長的路是套路", "英雄聯盟最強的是補丁",
            "我不會輕易的go die", "嘿嘿", "加班加班"};
    private int textCount;//文本的組數
  //private  RelativeLayout Rparams;
//    private List itemList = new ArrayList();
//實現RelativeLayout的重寫的構造方法。
   /*
    * //content 上下文 
	//AttributeSet 屬性集
	//defStyleAttr 預設樣式屬性集
	//defStyleRes 預設樣式資源屬性集
    * 
    * */
    public BarrageView(Context context) {
        this(context, null);
    }

    public BarrageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BarrageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init();
    }

    private void init() {
        textCount = itemText.length;
        int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());
        mHandler.sendEmptyMessageDelayed(0, duration);
    }

    @Override
    //Activity生命周期中,onStart, onResume, onCreate都不是真正visible的時間點,真正的visible時間點是onWindowFocusChanged()函數被執行時。
    //當你屏幕的焦點發生變化時候,想要操作什麼也完全可以在這個方法裡面執行。
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        totalHeight = getMeasuredHeight();
        /*getHeight跟getMeasureHeight的區別
         * 實際上在當屏幕可以包裹內容的時候,他們的值相等,只有當view超出屏幕後,才能看出他們的區別:
         * getMeasuredHeight()是實際View的大小,與屏幕無關,而getHeight的大小此時則是屏幕的大小。
         * 當超出屏幕後, getMeasuredHeight() 等於 getHeight()加上屏幕之外沒有顯示的大小 
         * 
         * */
        //獲取每一行彈幕的最大高度
        lineHeight = getLineHeight();
        //我們整個彈幕的高度view/每一行的最大彈幕高度=
        totalLine = totalHeight / lineHeight;
    }

    private void generateItem() {
        BarrageItem item = new BarrageItem();
        //把我們的每行彈幕的行數順序跟彈幕進行一個隨機
        String tx = itemText[(int) (Math.random() * textCount)];
        //隨機彈幕大小
        int sz = (int) (minSize + (maxSize - minSize) * Math.random());
        item.textView = new TextView(mContext);
        item.textView.setText(tx);
        item.textView.setTextSize(sz);
        item.textView.setTextColor(Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
         //這裡我們需要傳入三個參數 文本對象,文字行數跟大小
        item.textMeasuredWidth=(int) getTextWidth(item, tx, sz);
        //這是設置彈幕移動速度,實現有快有慢的感覺
        item.moveSpeed = (int) (minSpeed + (maxSpeed - minSpeed) * Math.random());
        
       //這裡為了實現一個彈幕循環播放的項目,在我們實際中看情況而定
        if (totalLine == 0) {
            totalHeight = getMeasuredHeight();
            lineHeight = getLineHeight();
            totalLine = totalHeight / lineHeight;
        }
        
        //彈幕在y軸上出現的位置
        item.verticalPos = random.nextInt(totalLine) * lineHeight;
//        itemList.add(item);
        showBarrageItem(item);
    }
  
    private void showBarrageItem(final BarrageItem item) {
//paddingLeft是設置布局裡面的內容左邊的距離,這樣我們這就可以讓這個彈幕的textview完全消失
        int leftMargin = this.getRight() - this.getLeft() - this.getPaddingLeft();
//這裡我們通過動態的方式去設置一些我們布局的屬性。
//        int verticalMargin = getRandomTopMargin();
//        item.textView.setTag(verticalMargin);

        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        params.topMargin = item.verticalPos;
        this.addView(item.textView, params);
        
        Animation anim = generateTranslateAnim(item, leftMargin);
        anim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

           }
            @Override
            //當我們動畫結束的時候,清除該條彈幕
            public void onAnimationEnd(Animation animation) {
                item.textView.clearAnimation();
                BarrageView.this.removeView(item.textView);
            }

            @Override
            //動畫被取消的時候出發
            public void onAnimationRepeat(Animation animation) {

            }
        });
        item.textView.startAnimation(anim);
    }
    
  //
    private TranslateAnimation generateTranslateAnim(BarrageItem item, int leftMargin) {
    	//這裡我們有四個參數(動畫開始的x點,結束點,開始y軸點,結束的y點)
        TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 0, 0);
         //我們設置動畫的持續時間,彈幕移動多久,我們就持續多久動畫
        anim.setDuration(item.moveSpeed);
        // Interpolator 被用來修飾動畫效果,定義動畫的變化率,可以使存在的動畫效果accelerated(加速),decelerated(減速),repeated(重復),bounced(彈跳)等。
        /*
         * AccelerateDecelerateInterpolator 在動畫開始與結束的地方速率改變比較慢,在中間的時候加速
          AccelerateInterpolator  在動畫開始的地方速率改變比較慢,然後開始加速
              AnticipateInterpolator 開始的時候向後然後向前甩
          AnticipateOvershootInterpolator 開始的時候向後然後向前甩一定值後返回最後的值
              BounceInterpolator   動畫結束的時候彈起
            CycleInterpolator 動畫循環播放特定的次數,速率改變沿著正弦曲線
          DecelerateInterpolator 在動畫開始的地方快然後慢
            LinearInterpolator   以常量速率改變
            OvershootInterpolator    向前甩一定值後再回到原來位置
         * */
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        /*
         * fillBefore是指動畫結束時畫面停留在此動畫的第一幀;
           fillAfter是指動畫結束是畫面停留在此動畫的最後一幀。
       Java代碼設置如下:
      /*****動畫結束時,停留在最後一幀*********
          setFillAfter(true);
          setFillBefore(false);  
    /*****動畫結束時,停留在第一幀*********
       setFillAfter(false);
        setFillBefore(true);  
         * 
         * */
        anim.setFillAfter(true);
        return anim;
    }

    /**
     * 計算TextView中字符串的長度
     *
     * @param text 要計算的字符串
     * @param Size 字體大小
     * @return TextView中字符串的長度
     */
    //因為我們的彈幕包裹在一個矩形中
    public float getTextWidth(BarrageItem item, String text, float Size) {
        Rect bounds = new Rect();
        TextPaint paint;
        paint = item.textView.getPaint();
        //這裡參數是獲取文本對象,開始的長度,結束的長度,我們繪制好的矩形框
        paint.getTextBounds(text, 0, text.length(), bounds);
        return bounds.width();
    }
   

    /**
     * 獲得每一行彈幕的最大高度
     *
     * @return
     */
    private int getLineHeight() {
        BarrageItem item = new BarrageItem();
        String tx = itemText[0];
        item.textView = new TextView(mContext);
        item.textView.setText(tx);
        item.textView.setTextSize(maxSize);
        Rect bounds = new Rect();
        TextPaint paint;
        paint = item.textView.getPaint();
        paint.getTextBounds(tx, 0, tx.length(), bounds);
        return bounds.height();
    }

    class BarrageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            generateItem();
            //每個彈幕產生的間隔時間隨機
            int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());
            //多個消息可以使用同一個handler, 通過what不同區分不同的消息來源, 從而獲取消息內容
            this.sendEmptyMessageDelayed(0, duration);
        }
    }
    	//記錄一下當前在顯示彈幕的高度,避免彈幕出現重疊
    private Set existMarginValues = new HashSet<>();
    private int linesCount;
//    private int getRandomTopMargin()
//    {
//    	//計算彈幕的空間高度
//    	if(totalLine==0)
//    	{
//    		totalLine=Rparams.getBottom()-Rparams.getTop()-Rparams.getPaddingTop()
//    				-Rparams.getPaddingBottom();
//    		if (totalHeight==0) {
//    			 totalHeight = getMeasuredHeight();
//    	            lineHeight = getLineHeight();
//    	            totalLine = totalHeight / lineHeight;
//			}
//    		//檢查重疊
//            while (true) {
//                int randomIndex =  (int) (Math.random() * linesCount);
//                int marginValue = (int) (randomIndex * (totalLine / linesCount));
//     
//                if (!existMarginValues.contains(marginValue)) {
//                    existMarginValues.add(marginValue);
//                    return marginValue;
//                }
//            }
//
//    		
	
		
   // }

}

BarrageActivity.java

在這個類裡面我們可以去進行一些事件,但是我這裡沒有去處理,大家按自己的需求來。

package com.example.bibibibibibibibi;

import android.app.Activity;
import android.os.Bundle;

/**
 * Created by lixueyong on 16/2/19.
 */
public class BarrageActivity extends Activity {

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

}

還有一些關於顏色的xml 大家可以通過demo去看下了,這樣我們就實現了類似於bibibi彈幕的功能,是不是很簡單.

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved