最近由于项目中直播需要用到弹幕,网上搜一下,基本就哔哩哔哩开源的一个开源项目,用起来很简单,优化也做的比较好。但是引用到项目里的话跟我们的需求不符合。项目里的弹幕需要各种各样的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"/>
是不是很简单;觉得可以的话点个赞