最近由于项目中直播需要用到弹幕,网上搜一下,基本就哔哩哔哩开源的一个开源项目,用起来很简单,优化也做的比较好。但是引用到项目里的话跟我们的需求不符合。项目里的弹幕需要各种各样的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());}@Overrideprotected void onAttachedToWindow() { super.onAttachedToWindow(); flutter();}/** * 设置一行的宽高 * @param widthMeasureSpec * @param heightMeasureSpec */@Overrideprotected 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);}@Overrideprotected 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;@Overrideprotected 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"/>是不是很简单;觉得可以的话点个赞
新闻热点
疑难解答