Android弹幕实现:基于B站弹幕开源系统(6)带用户头像且头像从网络加载
在附录文章1,2,3,4,5基础上,实现一种特殊弹幕效果,实现弹幕带发表者头像。这种需求在一些开发场景中比较有用,比如在一些视频中,不同等级的用户显示不同的头像,或者本身发出来的弹幕就要求头像。
代码:
package zhangfei.danmaku; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.v4.graphics.drawable.RoundedBitmapDrawable; import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.BackgroundColorSpan; import android.text.style.ImageSpan; import android.util.Log; import android.view.View; import java.util.HashMap; import java.util.concurrent.Callable; import io.reactivex.Observable; import io.reactivex.ObservableSource; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.observers.DisposableObserver; import io.reactivex.schedulers.Schedulers; 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.BaseCacheStuffer; import master.flame.danmaku.danmaku.model.android.DanmakuContext; import master.flame.danmaku.danmaku.model.android.SpannedCacheStuffer; import master.flame.danmaku.ui.widget.DanmakuView; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class MainActivity extends AppCompatActivity { private DanmakuView mDanmakuView; private DanmakuContext mContext; private AcFunDanmakuParser mParser; private final String TAG = getClass().getSimpleName(); private OkHttpClient mOkHttpClient; private CompositeDisposable mCompositeDisposable = new CompositeDisposable(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mOkHttpClient = new OkHttpClient(); mContext = DanmakuContext.create(); mParser = new AcFunDanmakuParser(); initDanmakuView(); final String url="http://avatar.csdn.net/9/7/A/1_zhangphil.jpg"; findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { addItems(url); } }); } 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, 3) .setDuplicateMergingEnabled(false).setScrollSpeedFactor(1.2f).setScaleTextSize(1.2f) .setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer // .setCacheStuffer(new BackgroundCacheStuffer()) // // 绘制背景使用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 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 } }; private void addItems(final String url) { DisposableObserver disposableObserver = new DisposableObserver<Bitmap>() { @Override public void onNext(@NonNull Bitmap bmp) { addDanmaKuShowTextAndImage(bmp, "zhang phil @ csdn", Color.DKGRAY, Color.RED, false); } @Override public void onComplete() { } @Override public void onError(Throwable e) { Log.e(TAG, e.toString(), e); } }; mCompositeDisposable.add( getBitmapObservable(url) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(disposableObserver) ); } private Observable<Bitmap> getBitmapObservable(@NonNull final String url) { return Observable.defer(new Callable<ObservableSource<Bitmap>>() { @Override public ObservableSource<Bitmap> call() throws Exception { Bitmap bmp = null; //同步方法返回观察者需要的数据结果 //在这里处理线程化的操作 Request request = new Request.Builder().url(url).build(); Response response = mOkHttpClient.newCall(request).execute(); try { if (response.isSuccessful()) { byte[] bytes = response.body().bytes(); bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); } } catch (Exception e) { e.printStackTrace(); } return Observable.just(bmp); } }); } private void addDanmaKuShowTextAndImage(Bitmap bitmap, String msg, int textColor, int bgColor, boolean islive) { BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); if (danmaku == null) { Log.e(TAG, "BaseDanmaku空"); } //最里面的图像 RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(getResources(), bitmap); drawable.setCircular(true); drawable.setAntiAlias(true); drawable.setCornerRadius(Math.max(bitmap.getWidth(), bitmap.getHeight())); drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); SpannableStringBuilder spannable = createSpannable(drawable, msg, bgColor); danmaku.text = spannable; 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; mDanmakuView.addDanmaku(danmaku); } private SpannableStringBuilder createSpannable(Drawable drawable, String msg, int color) { String text = "image"; SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text); spannableStringBuilder.append(msg); ImageSpan span = new ImageSpan(drawable);// ImageSpan.ALIGN_BOTTOM); spannableStringBuilder.setSpan(span, 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannableStringBuilder.setSpan(new BackgroundColorSpan(color), 0, spannableStringBuilder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return spannableStringBuilder; } @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; } //取消所有Okhttp的网络请求 mOkHttpClient.dispatcher().cancelAll(); mCompositeDisposable.clear(); } @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/65936066
5,《Android弹幕实现:基于B站弹幕开源系统(5)-抽象和复用》链接:http://blog.csdn.net/zhangphil/article/details/69400428
作者:zhangphil 发表于2017/5/27 10:17:45 原文链接
阅读:74 评论:0 查看评论