Android 弹幕的实现其实很简单

话不多说,直接上代码



package com.weizhong.shuowan.view;


import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;


import com.weizhong.shuowan.bean.Danmaku;
import com.weizhong.shuowan.config.Constants;
import com.weizhong.shuowan.manager.DanMuManager;
import com.weizhong.shuowan.observer.ExitActivityObserver;
import com.weizhong.shuowan.utils.PreferenceWrapper;
import com.weizhong.shuowan.utils.RandomUtils;


import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;


/**
 * Created by lzc on 2017/4/6.
 * 弹幕视图
 */


public class DanmakuView extends View{




    private static final int IDEL = 0;
    private static final int START = IDEL + 1;
    private int mState = IDEL;


    private MyHandler mHandler;
    private List<Danmaku> mDanmakus = new ArrayList<Danmaku>();
    private List<Danmaku> mLive = new LinkedList<Danmaku>();
    private int mRefreshTime = 10;//每条弹幕的间隔时间


    private float mXRatio = 35;
    private float mYRatio = 13;
    private PreferenceWrapper mPreferenceWrapper;
    private boolean mIsScreenLock;//屏幕是否锁屏
    private BroadcastReceiver mBroadcastReceiver;


    private int mStrokeColor;


    public DanmakuView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHandler = new MyHandler(this);
        mPreferenceWrapper = new PreferenceWrapper();
        registerReceiver();
        mStrokeColor = Color.parseColor("#282828");
    }


    public DanmakuView(Context context){
        super(context);
        mHandler = new MyHandler(this);
        mPreferenceWrapper = new PreferenceWrapper();
        registerReceiver();
        mStrokeColor = Color.parseColor("#282828");
    }


    private void registerReceiver(){
        mBroadcastReceiver = new BroadcastReceiver() {//屏幕状态接收器
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                    mIsScreenLock = true;
                } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                    mIsScreenLock = false;
                }
            }
        };
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        getContext().registerReceiver(mBroadcastReceiver, filter);
    }


    @Override
    public void onReceiveDanMu(String danMu) {
        addDanmaku(danMu);
    }


    public void setRatio(float xRatio, float yRatio){
        mXRatio = xRatio;
        mYRatio  = yRatio;
        postInvalidate();
    }


    private static class MyHandler extends Handler {


        private WeakReference<DanmakuView> wr;


        public MyHandler(DanmakuView danmakuView) {
            wr = new WeakReference<DanmakuView>(danmakuView);
        }


        @Override
        public void handleMessage(Message msg) {
            if (wr != null && wr.get() != null) {
                wr.get().handleMessage(msg);
            }
        }
    }


    public synchronized void handleMessage(Message msg) {
        switch (mState) {
            case START:
                if (!mDanmakus.isEmpty()) {
                    Danmaku danmaku = mDanmakus.get(0);
                    foundStaticLayout(danmaku);
                    boolean contains = false;
                    float left = danmaku.left;
                    float top = danmaku.top;
                    float right = danmaku.left + danmaku.width;
                    float bottom = danmaku.top + danmaku.layout.getHeight();


                    if (!mLive.isEmpty()) {
                        Danmaku item = mLive.get(mLive.size() - 1);


                        item.left = item.left * getWidth() / item.disWidth;
                        item.top = item.top * getHeight() / item.disHeight;
                        item.disWidth = getWidth();
                        item.disHeight = getHeight();


                        RectF rectF = new RectF(item.left, item.top, item.left + item.width, item.top + item.layout.getHeight());
                        contains = rectF.contains(left, top) ||
                                rectF.contains(right, top) ||
                                rectF.contains(left, bottom) ||
                                rectF.contains(right, bottom);//加载距右侧1/2
                    }
                    if (!contains) {
                        mDanmakus.remove(0);
                        mLive.add(danmaku);
                    }
                }
                if (!mLive.isEmpty()) {
                    sendMessage(mState);
                }else if(mDanmakus.isEmpty()){
                    sendMessage(IDEL);
                }
                break;


        }
        invalidate();
    }


    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mLive == null) {
            mLive = new LinkedList<Danmaku>();
        }


        if (mDanmakus == null) {
            mDanmakus = new LinkedList<Danmaku>();
        }


        if (mHandler == null) {
            mHandler = new MyHandler(this);
        }
    }


    @Override
    public void onActivityDestory() {
        DanMuManager.getInstance().unregisterDanMuListener(this);
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
            mHandler = null;
        }
        if (mDanmakus != null) {
            mDanmakus.clear();
            mDanmakus = null;
        }
        if (mLive != null) {
            mLive.clear();
            mLive = null;
        }
    }


    private synchronized void addDanmaku(String danMu) {
        if(mHandler != null && this.isShown() && mPreferenceWrapper.getBooleanValue(Constants.DAN_MU_SWITCH,true)){
            if(!mIsScreenLock){
                Danmaku danmaku = new Danmaku();
                danmaku.sequence = danMu;
                danmaku.padding = 5;
                int []colorAndSize = RandomUtils.nextColorAndSize();
                danmaku.textSize = colorAndSize[1];
                danmaku.textColor = colorAndSize[0];
                danmaku.textShadowColor = Color.BLACK;
                mDanmakus.add(danmaku);
                if (mState == IDEL) {
                    sendMessage(START);
                }
            }
        }
    }






    @Override
    protected synchronized void onDraw(Canvas canvas) {
        if (mState != IDEL && mPreferenceWrapper.getBooleanValue(Constants.DAN_MU_SWITCH,true)) {
            for (int i = 0; i < mLive.size(); i++) {
                Danmaku item = mLive.get(i);
                canvas.save();
                item.left = item.left * getWidth() / item.disWidth;
                item.top = item.top * getHeight() / item.disHeight;
                item.disWidth = getWidth();
                item.disHeight = getHeight();
                canvas.translate(item.left, item.top);
                item.layout.draw(canvas);
                int padding = 4;
                item.left -= padding;
                if (item.left + item.width <= 0) {
                    mLive.remove(i);
                    i--;
                }
                canvas.restore();
            }
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(width,(int)(width * mYRatio/mXRatio));
    }


    private void sendMessage(int what) {
        if (mHandler != null) {
            mState = what;
            mHandler.removeMessages(mState);
            mHandler.sendEmptyMessageDelayed(what, mRefreshTime);
        }
    }


    private TextPaint foundPaint(Danmaku item) {
        TextPaint paint = new TextPaint();
        paint.setColor(item.textColor);
        paint.setShadowLayer(0.7f, 0.7f, 0.7f, item.textShadowColor);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setTextSize(item.textSize);
        return paint;
    }


    private void foundStaticLayout(Danmaku item) {
        TextPaint paint = foundPaint(item);
        if (item.layout == null) {
            int width = (int) StaticLayout.getDesiredWidth(item.sequence, paint);
            StaticLayout layout = new StaticLayout(item.sequence, paint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
            item.layout = layout;
            item.width = width;
        }
        item.left = getWidth();
        int height = getHeight() - item.layout.getHeight();
        if (height < 0) {
            height = 0;
        }
        item.top = RandomUtils.nextInt(height);
        item.disWidth = getWidth();
        item.disHeight = getHeight();
    }


}







弹幕数据格式

package com.weizhong.shuowan.bean;


import android.graphics.RectF;
import android.text.StaticLayout;
import android.text.TextPaint;


/**
 * Created by lzc on 2017/4/6.
 * 弹幕数据结构
 */


public class Danmaku {


    public CharSequence sequence;
    public int padding;
    public float textSize;
    public int textColor;
    public int textShadowColor;


    public int disWidth;//将弹幕文字展示成一行所需要的宽
    public int disHeight;


    public StaticLayout layout;
    public float width;
    public float left;
    public float top;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闽农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值