第一步、添加弹幕库依赖
第四步、处理图片和文字同时显示效果
compile 'com.github.ctiao:DanmakuFlameMaster:0.8.3'
第二步、弹幕适配不同机型
/**
* 对数值进行转换,适配手机,必须在初始化之前,否则有些数据不会起作用
*/
private void setSize(Context context) {
BITMAP_WIDTH = TCUtils.dp2pxConvertInt(context, BITMAP_HEIGHT);
BITMAP_HEIGHT = TCUtils.dp2pxConvertInt(context, BITMAP_HEIGHT);
DANMU_PADDING = TCUtils.dp2pxConvertInt(context, DANMU_PADDING);
DANMU_PADDING_INNER = TCUtils.dp2pxConvertInt(context, DANMU_PADDING_INNER);
DANMU_RADIUS = TCUtils.dp2pxConvertInt(context, DANMU_RADIUS);
DANMU_TEXT_SIZE = TCUtils.sp2px(context, DANMU_TEXT_SIZE);
}
public static int dp2pxConvertInt(Context context, float dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
}
public static float sp2px(Context context, float spValue) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());
}
/**
* 初始化配置
*/
private void initDanmuConfig() {
// 设置最大显示行数
HashMap<Integer, Integer> maxLinesPair = new HashMap<Integer, Integer>();
maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 5); // 滚动弹幕最大显示5行
// 设置是否禁止重叠
HashMap<Integer, Boolean> overlappingEnablePair = new HashMap<Integer, Boolean>();
overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);
overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);
mDanmakuContext = DanmakuContext.create();
mDanmakuContext
.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_NONE) // 弹幕样式
.setDuplicateMergingEnabled(true) // 合并重复弹幕
.setScrollSpeedFactor(1.2f) //越大速度越慢
.setScaleTextSize(1.2f) // 弹幕文字大小
.setCacheStuffer(new BackgroundCacheStuffer(), mCacheStufferAdapter) // 缓存绘制
.setMaximumLines(maxLinesPair) // 弹幕最大行数
.preventOverlapping(overlappingEnablePair); // 防止弹幕重叠
}
/**
* 绘制背景(自定义弹幕样式)
*/
private class BackgroundCacheStuffer extends SpannedCacheStuffer {
// 通过扩展SimpleTextCacheStuffer或SpannedCacheStuffer个性化你的弹幕样式
final Paint paint = new Paint();
@Override
public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {
//danmaku.padding = 20; // 在背景绘制模式下增加padding
super.measure(danmaku, paint, fromWorkerThread);
}
@Override
public void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
//由于该库并没有提供margin的设置,所以我这边试出这种方法:将danmaku.padding也就是内间距设置大一点,并在这里的RectF中设置绘制弹幕的位置,就可以形成类似margin的效果
canvas.drawRoundRect(new RectF(left + DANMU_PADDING_INNER, top + DANMU_PADDING_INNER
, left + danmaku.paintWidth - DANMU_PADDING_INNER + 6,
top + danmaku.paintHeight - DANMU_PADDING_INNER + 6),//+6 主要是底部被截得太厉害了,+6是增加padding的效果
DANMU_RADIUS, DANMU_RADIUS, paint);
}
@Override
public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left, float top, Paint paint) {
// 禁用描边绘制
}
}
private BaseCacheStuffer.Proxy mCacheStufferAdapter = new BaseCacheStuffer.Proxy() {
@Override
public void prepareDrawing(final BaseDanmaku danmaku, boolean fromWorkerThread) {
// 根据你的条件检查是否需要需要更新弹幕
}
@Override
public void releaseResource(BaseDanmaku danmaku) {
// TODO 重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable
if (danmaku.text instanceof Spanned) {
danmaku.text = "";
}
}
};
第四步、处理图片和文字同时显示效果
public class TCCircleDrawable extends Drawable {
private Paint mPaint;
private Bitmap mBitmap;
private Bitmap mBitmapHeart;
private boolean mHasHeart;
private static final int BLACK_COLOR = 0xb2000000;//黑色 背景
private static final int BLACKGROUDE_ADD_SIZE = 4;//背景比图片多出来的部分
public TCCircleDrawable(Bitmap bitmap) {
mBitmap = bitmap;
BitmapShader bitmapShader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(bitmapShader);
}
/**
* 右下角包含一个‘心’的圆形drawable
*
* @param context context
* @param bitmap btimap
* @param hasHeart hasHeart
*/
public TCCircleDrawable(Context context, Bitmap bitmap, boolean hasHeart) {
this(bitmap);
mHasHeart = hasHeart;
if (hasHeart) {
setBitmapHeart(context);
}
}
private void setBitmapHeart(Context context) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_liked);
if (bitmap != null) {
Matrix matrix = new Matrix();
matrix.postScale(0.8f, 0.8f);
mBitmapHeart = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}
@Override
public void draw(Canvas canvas) {
if (mHasHeart && mBitmapHeart != null) {
//设置背景
Paint backgroundPaint = new Paint();
backgroundPaint.setAntiAlias(true);
backgroundPaint.setColor(BLACK_COLOR);
canvas.drawCircle(getIntrinsicWidth() / 2 + BLACKGROUDE_ADD_SIZE, getIntrinsicHeight() / 2 + BLACKGROUDE_ADD_SIZE,
getIntrinsicWidth() / 2 + BLACKGROUDE_ADD_SIZE, backgroundPaint);
//先将画布平移,防止图片不在正中间,然后绘制图片
canvas.translate(BLACKGROUDE_ADD_SIZE, BLACKGROUDE_ADD_SIZE);
canvas.drawCircle(getIntrinsicWidth() / 2, getIntrinsicHeight() / 2, getIntrinsicWidth() / 2, mPaint);
//在右下角绘制‘心’
Rect srcRect = new Rect(0, 0, mBitmapHeart.getWidth(), mBitmapHeart.getHeight());
Rect desRect = new Rect(getIntrinsicWidth() - mBitmapHeart.getWidth() + BLACKGROUDE_ADD_SIZE * 2,
getIntrinsicHeight() - mBitmapHeart.getHeight() + BLACKGROUDE_ADD_SIZE * 2,
getIntrinsicWidth() + BLACKGROUDE_ADD_SIZE * 2, getIntrinsicHeight() + BLACKGROUDE_ADD_SIZE * 2);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawBitmap(mBitmapHeart, srcRect, desRect, paint);
} else {
canvas.drawCircle(getIntrinsicWidth() / 2, getIntrinsicHeight() / 2, getIntrinsicWidth() / 2, mPaint);
}
}
@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
@Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
private void addDanmu(String headUrl, String sender, String text) {
if (mDanmakuView == null) {
return;
}
BaseDanmaku danmaku = mDanmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
if (danmaku == null) {
return;
}
danmaku.userId = 0;
danmaku.isGuest = false;// isGuest此处用来判断是赞还是评论
SpannableStringBuilder spannable;
Bitmap headBitmap = null;
if (!TextUtils.isEmpty(headUrl)) {
RequestManager req = Glide.with(mContext);
try {
headBitmap = req.load(headUrl).asBitmap().centerCrop().into(BITMAP_WIDTH, BITMAP_HEIGHT).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
if (headBitmap == null) {
headBitmap = getDefaultBitmap(R.drawable.ic_circle_empty);
}
TCCircleDrawable circleDrawable = new TCCircleDrawable(mContext, headBitmap, danmaku.isGuest);
circleDrawable.setBounds(0, 0, BITMAP_WIDTH, BITMAP_HEIGHT);
spannable = createSpannable(circleDrawable, sender, text);
danmaku.text = spannable;
danmaku.padding = DANMU_PADDING;
danmaku.priority = 0; // 1:一定会显示, 一般用于本机发送的弹幕,但会导致行数的限制失效
danmaku.isLive = false;
danmaku.time = mDanmakuView.getCurrentTime() + ADD_DANMU_TIME;
// danmaku.textSize = DANMU_TEXT_SIZE *
// (mDanmakuContext.getDisplayer().getDensity() - 0.6f);
danmaku.textSize = DANMU_TEXT_SIZE;
danmaku.textColor = Color.WHITE;
danmaku.textShadowColor = 0; // 重要:如果有图文混排,最好不要设置描边(设textShadowColor=0),否则会进行两次复杂的绘制导致运行效率降低
mDanmakuView.pause();
mDanmakuView.addDanmaku(danmaku);
mDanmakuView.resume();
}
DFM弹幕库的使用和配置在DEMO的TCDanmuMgr都有展示,欢迎下载体验。