1. 程式人生 > >Android直播中彈幕效果實現

Android直播中彈幕效果實現

       在B站或者其他視訊網站看視訊時,常常會開啟彈幕效果,邊看節目邊看大家的吐槽。彈幕看起來很有意思,今天我們就來實現一個簡單的彈幕效果。


從直觀上,彈幕效果就是在一個ViewGroup上增加一些View,然後讓這些View移動起來。所以,整體的實現思路大概是這樣的:

1、定義一個RelativeLayout,在裡面動態新增TextView。

2、這些TextView的字型大小、顏色、移動速度、初始位置都是隨機的。

3、將TextView新增到RelativeLayout的右邊緣,每隔一段時間新增一個。

4、對每個TextView做平移動畫,使得TextView從右向左移動。

5、當TextView從左邊移動出螢幕,將TextView從RelativeLayout中移除。

       有了思路下面就來看具體的程式碼。

首先定義BarrageItem,用來儲存每一個彈幕項的相關資訊,包括字型內容、字型大小顏色、移動速度、垂直方向的位置、字型佔據的寬度等。

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass BarrageItem {  
  2.     public TextView textView;  
  3.     publicint textColor;  
  4.     public String text;  
  5.     publicint textSize;  
  6.     publicint moveSpeed;//移動速度
  7.     publicint verticalPos;//垂直方向顯示的位置
  8.     publicint textMeasuredWidth;//字型顯示佔據的寬度
  9. }  
public class BarrageItem {
    public TextView textView;
    public int textColor;
    public String text;
    public int textSize;
    public int moveSpeed;//移動速度
    public int verticalPos;//垂直方向顯示的位置
    public int textMeasuredWidth;//字型顯示佔據的寬度
}

後定義BarrageView,由於彈幕的字型顏色大小和移動速度都是隨機的,需要定義最大最小值來限定它們的範圍,然後通過產生隨機數來設定它們在這個範圍內的值。另外還需要定義彈幕的文字內容,這裡是直接寫死的一些固定值。

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. private Context mContext;  
  2.     private BarrageHandler mHandler = new BarrageHandler();  
  3.     private Random random = new Random(System.currentTimeMillis());  
  4.     privatestaticfinallong BARRAGE_GAP_MIN_DURATION = 1000;//兩個彈幕的最小間隔時間
  5.     privatestaticfinallong BARRAGE_GAP_MAX_DURATION = 2000;//兩個彈幕的最大間隔時間
  6.     privateint maxSpeed = 10000;//速度,ms
  7.     privateint minSpeed = 5000;//速度,ms
  8.     privateint maxSize = 30;//文字大小,dp
  9.     privateint minSize = 15;//文字大小,dp
  10.     privateint totalHeight = 0;  
  11.     privateint lineHeight = 0;//每一行彈幕的高度
  12.     privateint totalLine = 0;//彈幕的行數
  13.     private String[] itemText = {“是否需要幫忙”“what are you 弄啥來”“哈哈哈哈哈哈哈”“搶佔沙發。。。。。。”“************”“是否需要幫忙”,“我不會輕易的狗帶”“嘿嘿”“這是我見過的最長長長長長長長長長長長的評論”};  
  14.     privateint textCount;  
  15. //    private List<BarrageItem> itemList = new ArrayList<BarrageItem>();
  16.     public BarrageView(Context context) {  
  17.         this(context, null);  
  18.     }  
  19.     public BarrageView(Context context, AttributeSet attrs) {  
  20.         this(context, attrs, 0);  
  21.     }  
  22.     public BarrageView(Context context, AttributeSet attrs, int defStyleAttr) {  
  23.         super(context, attrs, defStyleAttr);  
  24.         mContext = context;  
  25.         init();  
  26.     }  
    private Context mContext;
    private BarrageHandler mHandler = new BarrageHandler();
    private Random random = new Random(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 = {"是否需要幫忙", "what are you 弄啥來", "哈哈哈哈哈哈哈", "搶佔沙發。。。。。。", "************", "是否需要幫忙","我不會輕易的狗帶", "嘿嘿", "這是我見過的最長長長長長長長長長長長的評論"};
    private int textCount;
//    private List<BarrageItem> itemList = new ArrayList<BarrageItem>();

    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();
    }

如果彈幕顯示的垂直位置是隨機的,就會出現垂直方向上彈幕重疊的情況,所以需要根據高度對垂直方向按照彈幕高度的最大值等分,然後讓彈幕在這些指定的垂直位置隨機分佈。這個值在onWindowFocusChanged裡計算,因為在這個方法中通過View的getMeasuredHeight()得到的高度不為空。 [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. @Override
  2. publicvoid onWindowFocusChanged(boolean hasWindowFocus) {  
  3.     super.onWindowFocusChanged(hasWindowFocus);  
  4.     totalHeight = getMeasuredHeight();  
  5.     lineHeight = getLineHeight();  
  6.     totalLine = totalHeight / lineHeight;  
  7. }  
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        totalHeight = getMeasuredHeight();
        lineHeight = getLineHeight();
        totalLine = totalHeight / lineHeight;
    }

通過Handler的sendEmptyMessageDelayed每隔隨機的時間產生一個彈幕項。下面的程式碼設定彈幕項的屬性。

[java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. class BarrageHandler extends Handler {  
  2.     @Override
  3.     publicvoid handleMessage(Message msg) {  
  4.         super.handleMessage(msg);  
  5.         generateItem();  
  6.         //每個彈幕產生的間隔時間隨機
  7.         int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random());  
  8.         this.sendEmptyMessageDelayed(0, duration);  
  9.     }  
  10. }  
  11. privatevoid generateItem() {  
  12.     BarrageItem item = new BarrageItem();  
  13.     String tx = itemText[(int) (Math.random() * textCount)];  
  14.     int sz = (int) (minSize + (maxSize - minSize) * Math.random());  
  15.     item.textView = new TextView(mContext);  
  16.     item.textView.setText(tx);  
  17.     item.textView.setTextSize(sz);  
  18.     item.textView.setTextColor(Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256)));  
  19.     item.textMeasuredWidth = (int) getTextWidth(item, tx, sz);  
  20.     item.moveSpeed = (int) (minSpeed + (maxSpeed - minSpeed) * Math.random());  
  21.     if (totalLine == 0) {  
  22.         totalHeight = getMeasuredHeight();  
  23.         lineHeight = getLineHeight();  
  24.         totalLine = totalHeight / lineHeight;  
  25.     }  
  26.     item.verticalPos = random.nextInt(totalLine) * lineHeight;  
  27.     showBarrageItem(item);  
  28. }  
    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());
            this.sendEmptyMessageDelayed(0, duration);
        }
    }

    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;
        }
        item.verticalPos = random.nextInt(totalLine) * lineHeight;
        showBarrageItem(item);
    }

       將每一個彈幕項新增到檢視上,並給View新增一個TranslateAnimation動畫,當動畫結束時,將View從檢視上移除。 [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. privatevoid showBarrageItem(final BarrageItem item) {  
  2.         int leftMargin = this.getWidth();  
  3.         LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);  
  4.         params.addRule(RelativeLayout.ALIGN_PARENT_TOP);  
  5.         params.topMargin = item.verticalPos;  
  6.         this.addView(item.textView, params);  
  7.         Animation anim = generateTranslateAnim(item, leftMargin);  
  8.         anim.setAnimationListener(new Animation.AnimationListener() {  
  9.             @Override
  10.             publicvoid onAnimationStart(Animation animation) {  
  11.             }  
  12.             @Override
  13.             publicvoid onAnimationEnd(Animation animation) {  
  14.                 item.textView.clearAnimation();  
  15.                 BarrageView.this.removeView(item.textView);  
  16.             }  
  17.             @Override
  18.             publicvoid onAnimationRepeat(Animation animation) {  
  19.             }  
  20.         });  
  21.         item.textView.startAnimation(anim);  
  22.     }  
  23.     private TranslateAnimation generateTranslateAnim(BarrageItem item, int leftMargin) {  
  24.         TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 00);  
  25.         anim.setDuration(item.moveSpeed);  
  26.         anim.setInterpolator(new AccelerateDecelerateInterpolator());  
  27.         anim.setFillAfter(true);  
  28.         return anim;  
  29.     }  
private void showBarrageItem(final BarrageItem item) {

        int leftMargin = this.getWidth();

        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) {
        TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 0, 0);
        anim.setDuration(item.moveSpeed);
        anim.setInterpolator(new AccelerateDecelerateInterpolator());
        anim.setFillAfter(true);
        return anim;
    }
       這樣就完成了彈幕的功能,實現原理並不複雜。可以根據具體的需求來增加更多的控制,如控制每一行彈幕不重複,控制彈幕移動的Interpolator產生不同的滑動效果等等。

原始碼下載