安卓实现直播弹幕

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/diaoling1990/article/details/57499545

最近由于项目中直播需要用到弹幕,网上搜一下,基本就哔哩哔哩开源的一个开源项目,用起来很简单,优化也做的比较好。但是引用到项目里的话跟我们的需求不符合。项目里的弹幕需要各种各样的view,但是bilibili主要是针对文字的,对其他的view支持并不好,所以就自己根据项目需求自定义了一个弹幕view,当然,可能比起哔哩哔哩的开源项目来说,性能方面可能有所不足,但好在能满足需求,也比较轻量级,在这里做个记录,分享。

先说说思路吧,弹幕总共是有3行,可以指定添加的某一行,也可不指定,随机加到某一行。

BarrageLine 此类是一行弹幕的类, addBarrage(View view)方法是向queue队列里添加view,使用handler.postdelay的方法实现一个定时器,使用setTranslationX的方法实现view向左移动的效果。当一行里有空间时,barrageline会添加一个view。当一个view移出barrageline时,清空view。
啥也不用多说,上代码:
package com.example.administrator.barrage_test;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

import java.util.concurrent.ConcurrentLinkedQueue;

/**
* Created by Administrator on 2017/2/26.
*/

public class BarrageLine extends FrameLayout {

private Handler mHandler;
private ConcurrentLinkedQueue<View> mQueue = new ConcurrentLinkedQueue<>();
private int mWidth;
private int PADDING = 20;
private int HEIGHT = 100;

// /**
// * 统一线程池
// */
// public static Executor mExecutor = Executors.newCachedThreadPool();

public BarrageLine(Context context) {
    this(context,null);
}

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

public BarrageLine(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    init();

}

private void init() {
    mHandler = new Handler(Looper.getMainLooper());
}

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    flutter();
}

/**
 * 设置一行的宽高
 * @param widthMeasureSpec
 * @param heightMeasureSpec
 */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    this.mWidth = MeasureSpec.getSize(widthMeasureSpec);
    setMeasuredDimension(mWidth,HEIGHT);
}

/**
 * 网队列里添加弹幕view
 * @param view
 */
public void addBarrage(View view){
    mQueue.offer(view);
}

private class AddBarrageTask implements Runnable{
    View view;
    public AddBarrageTask(View view){
        this.view = view;
    }

    @Override
    public void run() {
        mQueue.offer(view);
    }
}

/**
 * 开始执行动画
 */
private void flutter(){
    mHandler.post(mFlutterTask);
}

private Runnable mFlutterTask = new Runnable() {
    @Override
    public void run() {
        addBarrageView();
        moveView();
        mHandler.postDelayed(this,5);
    }
};

/**
 * 判断每一行是否要添加view
 */
private void addBarrageView() {
    if (getChildCount() == 0){
        addNextView();
        return;
    }
    View lastChild = this.getChildAt(getChildCount()-1);
    int lastChildRight = (int) (lastChild.getTranslationX()+(int)lastChild.getTag());
    if (lastChildRight+PADDING>=mWidth)
        return;
    addNextView();
}

/**
 * 给每一行添加view
 */
private void addNextView(){
    if (mQueue.isEmpty())
        return;
    View view = mQueue.poll();
    view.measure(0,0);
    view.setTag(view.getMeasuredWidth());
    addView(view);
    view.setTranslationX(mWidth);
}

/**
 * 通过handler.post执行,形成动画
 */
private void moveView() {
    if (this.getChildCount()==0)
        return;
    for (int i=0;i<getChildCount();i++){
        View view = this.getChildAt(i);
        view.setTranslationX(view.getTranslationX()-3);
        if (view.getTranslationX()+(int)view.getTag()<=0)
            removeBarrageView(view);
    }
}

/**
 * 当view移出弹幕行,删除
 * @param view
 */
private void removeBarrageView(View view){
    view.setVisibility(GONE);
    this.removeView(view);
    view = null;
}

/**
 * 停止发消息,取消动画
 */
private void stop(){
    if (mHandler!=null)
        mHandler.removeCallbacks(mFlutterTask);
}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    stop();
}

}

然后是barrageview类,此类包含了三个barrageline,然后调用barrageline.addbarrage的方法向其中添加view,可指定添加到某一行,也可以随机添加的某一行,说不清,直接看代码

package com.example.administrator.barrage_test;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.Random;

/**
* Created by Administrator on 2017/2/26.
*/

public class BarrageView extends LinearLayout {
private ArrayList mBarrages = new ArrayList<>();
private Random mRandom;

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

/**
 * 总共三行弹幕
 */
private void init() {
    mRandom = new Random();
    setOrientation(LinearLayout.VERTICAL);
    for (int i = 0; i < 3; i++) {
        BarrageLine bl = new BarrageLine(getContext());
        LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        bl.setLayoutParams(param);
        this.addView(bl);
        mBarrages.add(bl);
    }
}

/**
 * 随机添加弹幕到某一行
 * @param view
 */
public void addBarrage(View view) {
    mBarrages.get(mRandom.nextInt(3)).addBarrage(view);
}

/**
 * 指定添加弹幕到某一行
 * @param view
 * @param line
 */
public void addBarrage(View view,int line){
    mBarrages.get(line).addBarrage(view);
}

}

然后调用很简单

public class MainActivity extends AppCompatActivity {

  private BarrageView bv;

int count;

private Button btn_send;

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

    bv = (BarrageView) findViewById(R.id.bv);

    btn_send = (Button) findViewById(R.id.btn_send);
    btn_send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            addBarrage();
        }
    });
}

private View getTextView() {
    TextView textview = (TextView) View.inflate(this,R.layout.textview,null);
    textview.setText("弹幕"+count++);
    return textview;
}

private View getImageView(){
    ImageView imageview = (ImageView) View.inflate(this,R.layout.imageview,null);
    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(100,100);
    imageview.setLayoutParams(params);
    return imageview;
}

public void addBarrage(){
    bv.addBarrage(getTextView());
    bv.addBarrage(getImageView());
}
}

以下是布局文件

<com.example.administrator.barrage_test.BarrageView
    android:layout_marginTop="200dp"
    android:background="#f0f0f0"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/bv"/>

是不是很简单;觉得可以的话点个赞

猜你喜欢

转载自blog.csdn.net/diaoling1990/article/details/57499545