直接上代码:
工具类
- import android.animation.Animator;
- import android.animation.ObjectAnimator;
- import android.annotation.SuppressLint;
- import android.content.Context;
- import android.os.Handler;
- import android.os.Message;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.View;
- import android.view.animation.LinearInterpolator;
- import android.widget.FrameLayout;
-
- import java.util.List;
-
- /**
- * 弹幕视图,使用属性动画
- */
- public class BarrageView extends FrameLayout {
- private String Tag = BarrageView.class.getSimpleName();
-
- private final int CODE_START = 1000;
- private final int CODE_NEXT = 1001;
- private final int CODE_END = 1002;
-
- //数据源
- private List<?> datas;
- private ViewHolder viewHolder;
- //控件宽
- private int barrageViewWidth;
- //控件高
- private int barrageViewHeight;
-
-
- //弹幕行数
- private int displayLines = 10;
- //是否循环显示
- private boolean isRepeat = true;
- //动画时间
- private long animationTime = 6 * 1000L;
-
- //两条弹幕最小间隔时间
- private long minIntervalTime = 1000L;
- //两条弹幕最大间隔时间
- private long maxIntervalTime = 3000L;
-
-
- //大当前弹幕索引
- private int currentIndex;
-
- //弹幕状态
- private boolean isStart;
-
- //上一次出现的行数
- private int lastLine = -1;
-
-
- @SuppressLint("HandlerLeak")
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case CODE_START:
- handler.sendEmptyMessage(CODE_NEXT);
- break;
- case CODE_NEXT:
- if (isStart && datas != null && currentIndex < datas.size()) {
- addView();
- currentIndex++;
- long interval = maxIntervalTime - minIntervalTime;
- long randomSleepTime = minIntervalTime + (long)(interval > 0 ? Math.random() * interval : 0);
- handler.sendEmptyMessageDelayed(CODE_NEXT, randomSleepTime);
- } else {
- handler.sendEmptyMessage(CODE_END);
- }
- break;
- case CODE_END:
- Log.d(Tag, "CODE_END");
- if (isRepeat) {
- if (currentIndex != 0) {
- currentIndex = 0;
- handler.sendEmptyMessage(CODE_NEXT);
- }
- }
- break;
- }
-
- }
- };
- private LinearInterpolator linearInterpolator;
-
- public BarrageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
-
- private void addView() {
- final View itemView = viewHolder.getItemView(getContext(), datas.get(currentIndex), currentIndex);
- addView(itemView);
- itemView.setY(getItemRandomY());
- itemView.measure(0, 0);
- int itemViewWidth = itemView.getMeasuredWidth();
- itemView.setX(this.barrageViewWidth);
-
- if (linearInterpolator == null) {
- linearInterpolator = new LinearInterpolator();
- }
-
- final ObjectAnimator anim = ObjectAnimator.ofFloat(itemView, "translationX", -itemViewWidth);
- anim.setDuration(animationTime);
- anim.setInterpolator(linearInterpolator);
- //释放资源
- anim.addListener(new Animator.AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- anim.cancel();
- itemView.clearAnimation();
- removeView(itemView);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- itemView.clearAnimation();
- removeView(itemView);
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
-
- }
- });
- anim.start();
- }
-
- /**
- * 获得随机的Y轴的值
- */
- private float getItemRandomY() {
-
- //随机选择弹幕出现的行数位置,跟上一条位置不同行
- int randomLine = lastLine;
- if (displayLines > 1) {
- while (randomLine == lastLine) {
- randomLine = (int) (Math.random() * displayLines + 1);
- }
- }
-
- lastLine =randomLine ;
- //当前itemView y值
- return (float) (barrageViewHeight*1.0 / displayLines * (randomLine - 1));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- barrageViewWidth = getWidth();
- barrageViewHeight = getHeight();
-
- }
-
- private int dip2px(Context context, float dpValue) {
- float scale = context.getResources().getDisplayMetrics().density;
- return (int) (dpValue * scale + 0.5f);
- }
-
- //设置数据
- public void setData(List<?> list, ViewHolder viewHolder) {
- datas = list;
- this.viewHolder = viewHolder;
- }
-
- public void start() {
- isStart = true;
- handler.sendEmptyMessage(CODE_START);
- }
-
-
- public void onResume() {
- if (!isStart) {
- isStart = true;
- handler.sendEmptyMessage(CODE_NEXT);
- }
- }
-
- public void onPause() {
- isStart = false;
- handler.removeMessages(CODE_NEXT);
- }
-
- public void cancle() {
- isStart = false;
- currentIndex = 0;
- if (datas != null) {
- datas.clear();
- }
- removeAllViews();
- handler.removeMessages(CODE_NEXT);
- }
-
- public void onDestroy() {
- cancle();
- }
-
-
- /**
- * 获取显示行数
- *
- * @return 行数
- */
- public int getDisplayLines() {
- return displayLines;
- }
-
- /**
- * 设置显示行数
- *
- * @param displayLines 行数
- */
- public void setDisplayLines(int displayLines) {
- if (displayLines <= 0) {
- return;
- }
- this.displayLines = displayLines;
- }
-
- /**
- * 是否重复
- *
- * @return 是否
- */
- public boolean isRepeat() {
- return isRepeat;
- }
-
- /**
- * 设置是否重复
- *
- * @param repeat 是否
- */
- public void setRepeat(boolean repeat) {
- isRepeat = repeat;
- }
-
- /**
- * 获取动画持续时间
- *
- * @return 时长ms
- */
- public long getAnimationTime() {
- return animationTime;
- }
-
- /**
- * 设置动画持续时长
- *
- * @param animationTime ms
- */
- public void setAnimationTime(long animationTime) {
- this.animationTime = animationTime;
- }
-
-
- /**
- * 获取最小间隔时间
- *
- * @return ms
- */
- public long getMinIntervalTime() {
- return minIntervalTime;
- }
-
- /**
- * 设置最小间间隔时间
- *
- * @param minIntervalTime ms
- */
- public void setMinIntervalTime(long minIntervalTime) {
- if (minIntervalTime <= 0) {
- return;
- }
- this.minIntervalTime = minIntervalTime;
- }
-
- /**
- * 获取最大间隔时间
- *
- * @return ms
- */
- public long getMaxIntervalTime() {
- return maxIntervalTime;
- }
-
- /**
- * 设置最大间间隔时间
- *
- * @param maxIntervalTime ms
- */
- public void setMaxIntervalTime(long maxIntervalTime) {
- if (maxIntervalTime <= 0) {
- return;
- }
- this.maxIntervalTime = maxIntervalTime;
- }
-
-
- public interface ViewHolder {
- View getItemView(Context context, Object item, int index);
- }
- }
- import java.io.Serializable;
-
- /**
- * 弹幕bean
- */
- public class BarrageViewBean implements Serializable {
- private String head;
- private String name;
- private String desc;
-
- public BarrageViewBean(String head, String name, String desc) {
- this.head = head;
- this.name = name;
- this.desc = desc;
- }
-
- public BarrageViewBean( String desc) {
- this.desc = desc;
- }
-
- public String getHead() {
- return head;
- }
-
- public void setHead(String head) {
- this.head = head;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
- }
布局:
- <com.xxx.BarrageView
- android:id="@+id/barrage_view"
- android:layout_width="match_parent"
- android:layout_height="92dp"
- android:layout_marginTop="25dp"
- app:layout_constraintBottom_toTopOf="@+id/iv_bg_tips"
- app:layout_constraintTop_toBottomOf="@+id/view_gray" />
调用:
- private fun showBarrageView(){
- val data = CommonUtils.getAssetsData(AppContext.appContext, "barrview.json")
- if (!TextUtils.isEmpty(data)) {
- val list :List
= Gson().fromJson( - data,
- object : TypeToken
>() {}.type
- )
-
- mBinding.barrageView.setData(list) { context, item, index ->
- getItem(context, item as VipUserBean)
- }
- mBinding.barrageView.displayLines = 2 //设置行数
- mBinding.barrageView.minIntervalTime = 800L //设置最小显示间隔时间
- mBinding.barrageView.maxIntervalTime = 1200L //设置最大显示间隔时间
- mBinding.barrageView.animationTime = 4000L //设置弹幕持续时长
- mBinding.barrageView.start()
- }
-
-
- }