好久没有写过文章,最近发现直播特别的火,很多app都集成了直播的功能,发现有些直播是带有弹幕的,效果还不错,今天心血来潮,特地写了篇制作弹幕的文章.
今天要实现的效果如下:
弹幕垂直方向固定
弹幕垂直方向随机
上面效果图中白色的背景就是弹幕本身,是一个自定义的FrameLayout,我这里是为了更好的展示弹幕的位置才设置成了白色,当然如果是叠加在VideoView上的话,就需要设置成透明色了.
制作弹幕需要考虑以下几点问题:
1.弹幕的大小可以随意调整
2.弹幕内移动的item(或者称字幕)出现的位置,水平方向是从屏幕右边移动到屏幕左边,垂直方向是不能超出弹幕本身的高度的.
3.字幕移除屏幕后,需要将对应item(字幕)从其父容器(弹幕)中移除.
4.如果字幕出现的垂直方向的高度是随机的,那么还需要避免字幕重叠的情况.
ok,下面是弹幕自定义view的代码:
public class DanmuView extends FrameLayout { private static final String TAG = "DanmuView"; private static final long DEFAULT_ANIM_DURATION = 6000; //默认每个动画的播放时长 private static final long DEFAULT_QUERY_DURATION = 3000; //遍历弹幕的默认间隔 private LinkedList<View> mViews = new LinkedList<>();//弹幕队列 private boolean isQuerying; private int mWidth;//弹幕的宽度 private int mHeight;//弹幕的高度 private Handler mUIHandler = new Handler(); private boolean TopDirectionFixed;//弹幕顶部的方向是否固定 private Handler mQueryHandler; private int mTopGravity = Gravity.CENTER_VERTICAL;//顶部方向固定时的默认对齐方式 public void setHeight(int height) { mHeight = height; } public void setWidth(int width) { mWidth = width; } public void setTopGravity(int gravity) { this.mTopGravity = gravity; } public void add(List<Danmu> danmuList) { for (int i = 0; i < danmuList.size(); i++) { Danmu danmu = danmuList.get(i); addDanmuToQueue(danmu); } } public void add(Danmu danmu) { addDanmuToQueue(danmu); } public DanmuView(Context context) { this(context, null); } public DanmuView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DanmuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); HandlerThread thread = new HandlerThread("query"); thread.start(); //循环取出弹幕显示 mQueryHandler = new Handler(thread.getLooper()) { @Override public void handleMessage(Message msg) { final View view = mViews.poll(); if (null != view) { mUIHandler.post(new Runnable() { @Override public void run() { //添加弹幕 showDanmu(view); } }); } sendEmptyMessageDelayed(0, DEFAULT_QUERY_DURATION); } }; } private void addDanmuToQueue(Danmu danmu) { if (null != danmu) { final View view = View.inflate(getContext(), R.layout.layout_danmu, null); TextView usernameTv = (TextView) view.findViewById(R.id.tv_username); TextView infoTv = (TextView) view.findViewById(R.id.tv_info); ImageView headerIv = (ImageView) view.findViewById(R.id.iv_header); usernameTv.setText(danmu.getUserName());//昵称 infoTv.setText(danmu.getInfo());//信息 Glide.with(getContext()).//头像 load(danmu.getHeaderUrl()). transform(new CropCircleTransformation(getContext())).into(headerIv); view.measure(0, 0); //添加弹幕到队列中 mViews.offerLast(view); } } public void startPlay(boolean topDirectionFixed) { this.TopDirectionFixed = topDirectionFixed; if (mWidth == 0 || mHeight == 0) { getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @SuppressLint("NewApi") @Override public void onGlobalLayout() { getViewTreeObserver().removeOnGlobalLayoutListener(this); if (mWidth == 0) mWidth = getWidth() - getPaddingLeft() - getPaddingRight(); if (mHeight == 0) mHeight = getHeight() - getPaddingTop() - getPaddingBottom(); if (!isQuerying) { mQueryHandler.sendEmptyMessage(0); } } }); } else { if (!isQuerying) { mQueryHandler.sendEmptyMessage(0); } } } private void showDanmu(final View view) { isQuerying = true; Log.d(TAG, "mWidth:" + mWidth + " mHeight:" + mHeight); final LayoutParams lp = new LayoutParams(view.getMeasuredWidth(), view.getMeasuredHeight()); lp.leftMargin = mWidth; if (TopDirectionFixed) { lp.gravity = mTopGravity | Gravity.LEFT; } else { lp.gravity = Gravity.LEFT | Gravity.TOP; lp.topMargin = getRandomTopMargin(view); } view.setLayoutParams(lp); view.setTag(lp.topMargin); //设置item水平滚动的动画 ValueAnimator animator = ValueAnimator.ofInt(mWidth, -view.getMeasuredWidth()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { lp.leftMargin = (int) animation.getAnimatedValue(); view.setLayoutParams(lp); } }); addView(view);//显示弹幕 animator.setDuration(DEFAULT_ANIM_DURATION); animator.setInterpolator(new LinearInterpolator()); animator.start();//开启动画 animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.clearAnimation(); existMarginValues.remove(view.getTag());//移除已使用过的顶部边距 removeView(view);//移除弹幕 animation.cancel(); } }); } //记录当前仍在显示状态的弹幕的垂直方向位置(避免重复) private Set<Integer> existMarginValues = new HashSet<>(); private int linesCount; private int range = 10; private int getRandomTopMargin(View view) { //计算可用的行数 linesCount = mHeight / view.getMeasuredHeight(); if (linesCount <= 1) { linesCount = 1; } Log.d(TAG, "linesCount:" + linesCount); //检查重叠 while (true) { int randomIndex = (int) (Math.random() * linesCount); int marginValue = randomIndex * (mHeight / linesCount); //边界检查 if (marginValue > mHeight - view.getMeasuredHeight()) { marginValue = mHeight - view.getMeasuredHeight() - range; } if (marginValue == 0) { marginValue = range; } if (!existMarginValues.contains(marginValue)) { existMarginValues.add(marginValue); Log.d(TAG, "marginValue:" + marginValue); return marginValue; } } }}
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756
相关文章
发现更多好内容猜你喜欢
AI推送时光机Android自制精彩弹幕效果
后端开发2023-05-30
Android实现自定义的弹幕效果
后端开发2022-06-06
很棒的Android弹幕效果实例
后端开发2022-06-06
WPF+SkiaSharp实现自绘弹幕效果
后端开发2024-04-02
Android仿斗鱼直播的弹幕效果
后端开发2022-06-06
怎么使用Android Flutter实现弹幕效果
后端开发2023-07-02
Android自定义弹框Dialog效果
后端开发2024-04-02
Android自定义弹窗提示效果
后端开发2024-04-02
Android自定义弹出框dialog效果
后端开发2023-05-31
Android自定义scrollview实现回弹效果
后端开发2024-04-02
Android怎么自定义弹框Dialog效果
后端开发2023-06-30
Android中怎么利用EasyBarrage实现一个弹幕效果
后端开发2023-05-30
Android仿Iphone屏幕底部弹出半透明PopupWindow效果
后端开发2023-05-30
Android自定义View实现弹性小球效果
后端开发2022-06-06
Android怎么自定义scrollview实现回弹效果
后端开发2023-06-29
Android Flutter实现自由落体弹跳动画效果
后端开发2022-11-13
Android实现横向无限循环滚动的单行弹幕效果
后端开发2024-04-02
Android自定义View实现竖向滑动回弹效果
后端开发2024-04-02
实例解析如何在Android应用中实现弹幕动画效果
后端开发2022-06-06
Android编程实现简易弹幕效果示例【附demo源码下载】
后端开发2022-06-06
咦!没有更多了?去看看其它编程学习网 内容吧