原文链接:
http://blog.csdn.net/zhangphil/article/details/72926538
在附录文章得基础上,改进普通文本弹幕,实现一种特殊效果的文本弹幕,像QQ、微信一样的带有气泡背景的弹幕。实现的重点是在SpannedCacheStuffer。同时要准备若干需要衬在文本弹幕背景部分的.9.png图片。
上层Java代码:
- package zhangfei.danmaku;
- import android.content.res.Configuration;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Rect;
- import android.graphics.drawable.Drawable;
- import android.support.v4.content.ContextCompat;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.text.TextPaint;
- import android.util.Log;
- import android.view.View;
- import com.github.lzyzsd.randomcolor.RandomColor;
- import java.util.HashMap;
- import master.flame.danmaku.controller.IDanmakuView;
- import master.flame.danmaku.danmaku.model.BaseDanmaku;
- import master.flame.danmaku.danmaku.model.DanmakuTimer;
- import master.flame.danmaku.danmaku.model.IDanmakus;
- import master.flame.danmaku.danmaku.model.IDisplayer;
- import master.flame.danmaku.danmaku.model.android.DanmakuContext;
- import master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer;
- import master.flame.danmaku.ui.widget.DanmakuView;
- public class MainActivity extends AppCompatActivity {
- private DanmakuView mDanmakuView;
- private DanmakuContext mContext;
- private AcFunDanmakuParser mParser;
- private AppCompatActivity mActivity;
- private final String TAG = getClass().getSimpleName();
- private BackgroundCacheStuffer mBackgroundCacheStuffer = new BackgroundCacheStuffer();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mActivity = this;
- setContentView(R.layout.activity_main);
- mContext = DanmakuContext.create();
- mParser = new AcFunDanmakuParser();
- initDanmakuView();
- findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- addItems();
- }
- });
- }
- private void initDanmakuView() {
- // 设置最大显示行数
- HashMap<Integer, Integer> maxLinesPair = new HashMap<>();
- 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);
- mDanmakuView = (DanmakuView) findViewById(R.id.sv_danmaku);
- mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 10)
- .setDuplicateMergingEnabled(false)
- .setScrollSpeedFactor(1.2f)
- .setScaleTextSize(1.0f)
- .setCacheStuffer(mBackgroundCacheStuffer, null)
- // 绘制背景使用BackgroundCacheStuffer
- .setMaximumLines(maxLinesPair)
- .preventOverlapping(overlappingEnablePair).setDanmakuMargin(40);
- if (mDanmakuView != null) {
- mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {
- @Override
- public void updateTimer(DanmakuTimer timer) {
- }
- @Override
- public void drawingFinished() {
- }
- @Override
- public void danmakuShown(BaseDanmaku danmaku) {
- }
- @Override
- public void prepared() {
- mDanmakuView.start();
- }
- });
- mDanmakuView.setOnDanmakuClickListener(new IDanmakuView.OnDanmakuClickListener() {
- @Override
- public boolean onDanmakuClick(IDanmakus danmakus) {
- BaseDanmaku latest = danmakus.last();
- if (null != latest) {
- return true;
- }
- return false;
- }
- @Override
- public boolean onDanmakuLongClick(IDanmakus danmakus) {
- return false;
- }
- @Override
- public boolean onViewClick(IDanmakuView view) {
- return false;
- }
- });
- mDanmakuView.prepare(mParser, mContext);
- // mDanmakuView.showFPS(true);
- mDanmakuView.enableDanmakuDrawingCache(true);
- }
- }
- private void addItems() {
- RandomColor randomColor = new RandomColor();
- int id = (int) (Math.random() * 10) % 3;
- int resId;
- switch (id) {
- case 0:
- resId = R.drawable.bg_01;
- break;
- case 1:
- resId = R.drawable.bg_02;
- break;
- case 2:
- resId = R.drawable.bg_03;
- break;
- default:
- resId = R.drawable.bg_01;
- break;
- }
- String s = "";
- int count = (int) (Math.random() * 100) % 10 + 1;
- for (int i = 0; i < count; i++) {
- s = s + i;
- }
- addDanmaKuTextWithBackgroundImage(resId, s, randomColor.randomColor(), false);
- }
- /**
- * 绘制背景(自定义弹幕样式)
- */
- private class BackgroundCacheStuffer extends SpannedCacheStuffer {
- @Override
- public void measure(BaseDanmaku danmaku, TextPaint paint, boolean fromWorkerThread) {
- danmaku.padding = 50; // 在背景绘制模式下增加padding
- super.measure(danmaku, paint, fromWorkerThread);
- }
- @Override
- public void drawBackground(BaseDanmaku danmaku, Canvas canvas, float left, float top) {
- Object object = danmaku.tag;
- if (object instanceof DanmakuTag) {
- DanmakuTag danmakuTag = (DanmakuTag) object;
- Drawable drawable = ContextCompat.getDrawable(mActivity, danmakuTag.bitmapResId);
- float height = danmaku.paintHeight;
- float width = danmaku.paintWidth;
- Rect rect = new Rect(0, 0, (int) width, (int) height);
- drawable.setBounds(rect);
- drawable.draw(canvas);
- }
- }
- @Override
- public void drawStroke(BaseDanmaku danmaku, String lineText, Canvas canvas, float left,
- float top, Paint paint) {
- // 禁用描边绘制
- }
- }
- private void addDanmaKuTextWithBackgroundImage(int bitmap_resId, String msg,
- int textColor, boolean islive) {
- BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
- if (danmaku == null) {
- Log.e(TAG, "BaseDanmaku空");
- }
- DanmakuTag danmakuTag = new DanmakuTag();
- danmakuTag.bitmapResId = bitmap_resId;
- danmaku.setTag(danmakuTag);
- danmaku.text = " " + msg + " ";
- // danmaku.padding = 5;
- danmaku.priority = 1; // 一定会显示, 一般用于本机发送的弹幕
- danmaku.isLive = islive;
- danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);
- danmaku.textSize = 25f * (mParser.getDisplayer().getDensity() - 0.6f);
- danmaku.textColor = textColor;
- danmaku.textShadowColor = 0; // 重要:如果有图文混排,最好不要设置描边(设textShadowColor=0),否则会进行两次复杂的绘制导致运行效率降低
- // danmaku.underlineColor = Color.GREEN;
- // danmaku.borderColor=new RandomColor().randomColor();
- mDanmakuView.addDanmaku(danmaku);
- }
- private class DanmakuTag {
- public int bitmapResId;
- }
- @Override
- protected void onPause() {
- super.onPause();
- if (mDanmakuView != null && mDanmakuView.isPrepared()) {
- mDanmakuView.pause();
- }
- }
- @Override
- protected void onResume() {
- super.onResume();
- if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {
- mDanmakuView.resume();
- }
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mDanmakuView != null) {
- // dont forget release!
- mDanmakuView.release();
- mDanmakuView = null;
- }
- }
- @Override
- public void onBackPressed() {
- super.onBackPressed();
- if (mDanmakuView != null) {
- // dont forget release!
- mDanmakuView.release();
- mDanmakuView = null;
- }
- }
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
- mDanmakuView.getConfig().setDanmakuMargin(20);
- } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mDanmakuView.getConfig().setDanmakuMargin(40);
- }
- }
- }
附录:
1,《Android弹幕实现:基于B站弹幕开源系统(1)》链接:http://blog.csdn.net/zhangphil/article/details/68067100
2,《Android弹幕实现:基于B站弹幕开源系统(2)》链接:http://blog.csdn.net/zhangphil/article/details/68114226
3,《Android弹幕实现:基于B站弹幕开源系统(3)-文本弹幕的完善和细节调整》链接:http://blog.csdn.net/zhangphil/article/details/68485505
4,《Android弹幕实现:基于B站弹幕开源系统(4)-重构》链接:http://blog.csdn.net/zhangphil/article/details/68947236
5,《Android弹幕实现:基于B站弹幕开源系统(5)-抽象和复用》链接:http://blog.csdn.net/zhangphil/article/details/69400428
6,《Android弹幕实现:基于B站弹幕开源系统(6)带用户头像且头像从网络加载》链接:http://blog.csdn.net/zhangphil/article/details/72778984