话不多说,直接上代码
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;
}