• Android 弹幕功能实现


    直接上代码

    工具类

    1. import android.animation.Animator;
    2. import android.animation.ObjectAnimator;
    3. import android.annotation.SuppressLint;
    4. import android.content.Context;
    5. import android.os.Handler;
    6. import android.os.Message;
    7. import android.util.AttributeSet;
    8. import android.util.Log;
    9. import android.view.View;
    10. import android.view.animation.LinearInterpolator;
    11. import android.widget.FrameLayout;
    12. import java.util.List;
    13. /**
    14. * 弹幕视图,使用属性动画
    15. */
    16. public class BarrageView extends FrameLayout {
    17. private String Tag = BarrageView.class.getSimpleName();
    18. private final int CODE_START = 1000;
    19. private final int CODE_NEXT = 1001;
    20. private final int CODE_END = 1002;
    21. //数据源
    22. private List<?> datas;
    23. private ViewHolder viewHolder;
    24. //控件宽
    25. private int barrageViewWidth;
    26. //控件高
    27. private int barrageViewHeight;
    28. //弹幕行数
    29. private int displayLines = 10;
    30. //是否循环显示
    31. private boolean isRepeat = true;
    32. //动画时间
    33. private long animationTime = 6 * 1000L;
    34. //两条弹幕最小间隔时间
    35. private long minIntervalTime = 1000L;
    36. //两条弹幕最大间隔时间
    37. private long maxIntervalTime = 3000L;
    38. //大当前弹幕索引
    39. private int currentIndex;
    40. //弹幕状态
    41. private boolean isStart;
    42. //上一次出现的行数
    43. private int lastLine = -1;
    44. @SuppressLint("HandlerLeak")
    45. private Handler handler = new Handler() {
    46. @Override
    47. public void handleMessage(Message msg) {
    48. switch (msg.what) {
    49. case CODE_START:
    50. handler.sendEmptyMessage(CODE_NEXT);
    51. break;
    52. case CODE_NEXT:
    53. if (isStart && datas != null && currentIndex < datas.size()) {
    54. addView();
    55. currentIndex++;
    56. long interval = maxIntervalTime - minIntervalTime;
    57. long randomSleepTime = minIntervalTime + (long)(interval > 0 ? Math.random() * interval : 0);
    58. handler.sendEmptyMessageDelayed(CODE_NEXT, randomSleepTime);
    59. } else {
    60. handler.sendEmptyMessage(CODE_END);
    61. }
    62. break;
    63. case CODE_END:
    64. Log.d(Tag, "CODE_END");
    65. if (isRepeat) {
    66. if (currentIndex != 0) {
    67. currentIndex = 0;
    68. handler.sendEmptyMessage(CODE_NEXT);
    69. }
    70. }
    71. break;
    72. }
    73. }
    74. };
    75. private LinearInterpolator linearInterpolator;
    76. public BarrageView(Context context, AttributeSet attrs) {
    77. super(context, attrs);
    78. }
    79. private void addView() {
    80. final View itemView = viewHolder.getItemView(getContext(), datas.get(currentIndex), currentIndex);
    81. addView(itemView);
    82. itemView.setY(getItemRandomY());
    83. itemView.measure(0, 0);
    84. int itemViewWidth = itemView.getMeasuredWidth();
    85. itemView.setX(this.barrageViewWidth);
    86. if (linearInterpolator == null) {
    87. linearInterpolator = new LinearInterpolator();
    88. }
    89. final ObjectAnimator anim = ObjectAnimator.ofFloat(itemView, "translationX", -itemViewWidth);
    90. anim.setDuration(animationTime);
    91. anim.setInterpolator(linearInterpolator);
    92. //释放资源
    93. anim.addListener(new Animator.AnimatorListener() {
    94. @Override
    95. public void onAnimationStart(Animator animation) {
    96. }
    97. @Override
    98. public void onAnimationEnd(Animator animation) {
    99. anim.cancel();
    100. itemView.clearAnimation();
    101. removeView(itemView);
    102. }
    103. @Override
    104. public void onAnimationCancel(Animator animation) {
    105. itemView.clearAnimation();
    106. removeView(itemView);
    107. }
    108. @Override
    109. public void onAnimationRepeat(Animator animation) {
    110. }
    111. });
    112. anim.start();
    113. }
    114. /**
    115. * 获得随机的Y轴的值
    116. */
    117. private float getItemRandomY() {
    118. //随机选择弹幕出现的行数位置,跟上一条位置不同行
    119. int randomLine = lastLine;
    120. if (displayLines > 1) {
    121. while (randomLine == lastLine) {
    122. randomLine = (int) (Math.random() * displayLines + 1);
    123. }
    124. }
    125. lastLine =randomLine ;
    126. //当前itemView y值
    127. return (float) (barrageViewHeight*1.0 / displayLines * (randomLine - 1));
    128. }
    129. @Override
    130. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    131. super.onLayout(changed, left, top, right, bottom);
    132. barrageViewWidth = getWidth();
    133. barrageViewHeight = getHeight();
    134. }
    135. private int dip2px(Context context, float dpValue) {
    136. float scale = context.getResources().getDisplayMetrics().density;
    137. return (int) (dpValue * scale + 0.5f);
    138. }
    139. //设置数据
    140. public void setData(List<?> list, ViewHolder viewHolder) {
    141. datas = list;
    142. this.viewHolder = viewHolder;
    143. }
    144. public void start() {
    145. isStart = true;
    146. handler.sendEmptyMessage(CODE_START);
    147. }
    148. public void onResume() {
    149. if (!isStart) {
    150. isStart = true;
    151. handler.sendEmptyMessage(CODE_NEXT);
    152. }
    153. }
    154. public void onPause() {
    155. isStart = false;
    156. handler.removeMessages(CODE_NEXT);
    157. }
    158. public void cancle() {
    159. isStart = false;
    160. currentIndex = 0;
    161. if (datas != null) {
    162. datas.clear();
    163. }
    164. removeAllViews();
    165. handler.removeMessages(CODE_NEXT);
    166. }
    167. public void onDestroy() {
    168. cancle();
    169. }
    170. /**
    171. * 获取显示行数
    172. *
    173. * @return 行数
    174. */
    175. public int getDisplayLines() {
    176. return displayLines;
    177. }
    178. /**
    179. * 设置显示行数
    180. *
    181. * @param displayLines 行数
    182. */
    183. public void setDisplayLines(int displayLines) {
    184. if (displayLines <= 0) {
    185. return;
    186. }
    187. this.displayLines = displayLines;
    188. }
    189. /**
    190. * 是否重复
    191. *
    192. * @return 是否
    193. */
    194. public boolean isRepeat() {
    195. return isRepeat;
    196. }
    197. /**
    198. * 设置是否重复
    199. *
    200. * @param repeat 是否
    201. */
    202. public void setRepeat(boolean repeat) {
    203. isRepeat = repeat;
    204. }
    205. /**
    206. * 获取动画持续时间
    207. *
    208. * @return 时长ms
    209. */
    210. public long getAnimationTime() {
    211. return animationTime;
    212. }
    213. /**
    214. * 设置动画持续时长
    215. *
    216. * @param animationTime ms
    217. */
    218. public void setAnimationTime(long animationTime) {
    219. this.animationTime = animationTime;
    220. }
    221. /**
    222. * 获取最小间隔时间
    223. *
    224. * @return ms
    225. */
    226. public long getMinIntervalTime() {
    227. return minIntervalTime;
    228. }
    229. /**
    230. * 设置最小间间隔时间
    231. *
    232. * @param minIntervalTime ms
    233. */
    234. public void setMinIntervalTime(long minIntervalTime) {
    235. if (minIntervalTime <= 0) {
    236. return;
    237. }
    238. this.minIntervalTime = minIntervalTime;
    239. }
    240. /**
    241. * 获取最大间隔时间
    242. *
    243. * @return ms
    244. */
    245. public long getMaxIntervalTime() {
    246. return maxIntervalTime;
    247. }
    248. /**
    249. * 设置最大间间隔时间
    250. *
    251. * @param maxIntervalTime ms
    252. */
    253. public void setMaxIntervalTime(long maxIntervalTime) {
    254. if (maxIntervalTime <= 0) {
    255. return;
    256. }
    257. this.maxIntervalTime = maxIntervalTime;
    258. }
    259. public interface ViewHolder {
    260. View getItemView(Context context, Object item, int index);
    261. }
    262. }

    bean类:

    1. import java.io.Serializable;
    2. /**
    3. * 弹幕bean
    4. */
    5. public class BarrageViewBean implements Serializable {
    6. private String head;
    7. private String name;
    8. private String desc;
    9. public BarrageViewBean(String head, String name, String desc) {
    10. this.head = head;
    11. this.name = name;
    12. this.desc = desc;
    13. }
    14. public BarrageViewBean( String desc) {
    15. this.desc = desc;
    16. }
    17. public String getHead() {
    18. return head;
    19. }
    20. public void setHead(String head) {
    21. this.head = head;
    22. }
    23. public String getName() {
    24. return name;
    25. }
    26. public void setName(String name) {
    27. this.name = name;
    28. }
    29. public String getDesc() {
    30. return desc;
    31. }
    32. public void setDesc(String desc) {
    33. this.desc = desc;
    34. }
    35. }

    布局:

    1. <com.xxx.BarrageView
    2. android:id="@+id/barrage_view"
    3. android:layout_width="match_parent"
    4. android:layout_height="92dp"
    5. android:layout_marginTop="25dp"
    6. app:layout_constraintBottom_toTopOf="@+id/iv_bg_tips"
    7. app:layout_constraintTop_toBottomOf="@+id/view_gray" />

    调用:

    1. private fun showBarrageView(){
    2. val data = CommonUtils.getAssetsData(AppContext.appContext, "barrview.json")
    3. if (!TextUtils.isEmpty(data)) {
    4. val list :List = Gson().fromJson(
    5. data,
    6. object : TypeToken>() {}.type
    7. )
    8. mBinding.barrageView.setData(list) { context, item, index ->
    9. getItem(context, item as VipUserBean)
    10. }
    11. mBinding.barrageView.displayLines = 2 //设置行数
    12. mBinding.barrageView.minIntervalTime = 800L //设置最小显示间隔时间
    13. mBinding.barrageView.maxIntervalTime = 1200L //设置最大显示间隔时间
    14. mBinding.barrageView.animationTime = 4000L //设置弹幕持续时长
    15. mBinding.barrageView.start()
    16. }
    17. }

  • 相关阅读:
    axios 基本使用与学习
    算法训练 | 贪心算法Part1 | 455.分发饼干、376.摆动序列、53.最大子序和
    基于随机油漆优化器 (MOSPO)求解多目标优化问题附matlab代码
    学生HTML游戏网页作业作品——HTML+CSS+JavaScript魔域私服游戏HTML(1个页面)
    SpringBoot统一功能处理
    ddddocr1.4.8失效的解决方法
    Spark SQL内置函数
    【面试宝典】吐血整理的100道Java多线程&并发面试题
    前端异常监控方案
    【ElementUI优化】el-table展开行 ==> :expand-row-keys=“expands“ 刷新表格,保存当前操作展开行
  • 原文地址:https://blog.csdn.net/qq_41620230/article/details/133923805