Android弹幕实现:基于B站弹幕开源系统(7)QQ、微信聊天气泡样式的弹幕
在附录文章得基础上,改进普通文本弹幕,实现一种特殊效果的文本弹幕,像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
作者:zhangphil 发表于2017/6/8 16:46:55 原文链接
阅读:126 评论:0 查看评论