Android开发之满屏弹幕

前言

在开发弹幕之前我也搜索了很多文章来借鉴,但由于太多布局都不是自己想要的,而且相关引入也无法使用,在最后决定开发自定义弹幕。在网上搜索了大量的自定义布局,B站的弹幕也有很多大佬扒出来使用,到最后我在种种因素下开发出来简陋的自定义弹幕。能满足相关需求。

一.弹幕实体类

一般来说弹幕会有头像,昵称,评论内容,点赞数组成,当然有的设计会没有昵称而是显示定位内容。因此我们需要一个相关的数据类去存储弹幕的数据,因为弹幕肯定不止是一条啦~
以下是相关的弹幕实体类定义。

public class Danmu{


    private long id;//弹幕id
    private String headerUrl;//头像
    private String userContent;//内容
    private String userLocation;//定位
    private String userLike;//点赞数
    private String userId;//用户id
    private boolean isFirst;//用户是否第一次点击点赞,第二次点击取消点赞

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getHeaderUrl() {
        return headerUrl;
    }

    public void setHeaderUrl(String headerUrl) {
        this.headerUrl = headerUrl;
    }

    public String getUserContent() {
        return userContent;
    }

    public void setUserContent(String userContent) {
        this.userContent = userContent;
    }

    public String getUserLocation() {
        return userLocation;
    }

    public void setUserLocation(String userLocation) {
        this.userLocation = userLocation;
    }

    public String getUserLike() {
        return userLike;
    }

    public void setUserLike(String userLike) {
        this.userLike = userLike;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public boolean isFirst() {
        return isFirst;
    }

    public void setFirst(boolean first) {
        isFirst = first;
    }

}

二.自定义弹幕view

因为开发的是一个自定义的弹幕,所以说相关的布局都是由自己所编写了,因此在这里我们需要自定义一个弹幕view去满足自己的需求。
该弹幕布局由一个圆形头像CircleImageView,评价内容TextView,定位内容TextView以及点赞按钮组成,在这里惯性使用了TextView来编写这个点赞按钮,布局类的可以根据自己的需求来编写,不需要完全保持一致。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl_chat"
    android:layout_width="@dimen/dp_300"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@drawable/ig_snztzdms12">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/iv_user_icon"
        android:layout_width="@dimen/dp_48"
        android:layout_height="@dimen/dp_48"
        android:layout_marginStart="@dimen/dp_32"
        android:layout_marginTop="@dimen/dp_5"
        app:civ_border_color="@color/white"
        app:civ_border_width="1dp"
        android:src="@drawable/img_exam_ave_time_rocket" />


    <TextView
        android:id="@+id/tv_user_content"
        android:textSize="18sp"
        android:textColor="#13113A"
        android:text="这也太难了ba bhhhh"
        android:maxWidth="@dimen/dp_120"
        android:ellipsize="end"
        android:singleLine="true"
        android:layout_marginTop="5dp"
        android:layout_marginStart="8dp"
        android:layout_toEndOf="@id/iv_user_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/tv_user_location"
        android:textSize="16sp"
        android:textColor="#8013113A"
        android:text="广东省广州市"
        android:drawablePadding="5dp"
        android:drawableStart="@drawable/ig_snztzdms11"
        android:maxWidth="@dimen/dp_120"
        android:ellipsize="end"
        android:singleLine="true"
        android:layout_marginTop="2dp"
        android:layout_marginStart="8dp"
        android:layout_below="@id/tv_user_content"
        android:layout_toEndOf="@id/iv_user_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="HardcodedText,UseCompatTextViewDrawableXml" />

    <TextView
        android:id="@+id/tv_user_like"
        android:text="认同"
        android:textColor="#FF7F5D"
        android:textSize="20sp"
        android:textStyle="bold"
        android:paddingTop="4dp"
        android:paddingBottom="4dp"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:drawableStart="@drawable/ig_snztzdms10"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="14dp"
        android:layout_marginEnd="@dimen/dp_14"
        android:background="@drawable/bg_item_like_nor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="HardcodedText,UseCompatTextViewDrawableXml" />


</RelativeLayout>

在build.gradle(app)中引入该插件,即可使用上述布局中的圆形布局,还可带边框

implementation ‘de.hdodenhof:circleimageview:3.1.0’

在编写完布局文件之后,需要进一步开发自定义弹幕view,形成一个组件,可在项目中任何位置中调用。其实这个弹幕组件它的一个动起来的过程就是一个属性动画ValueAnimator刷起来的过程,因此它就是ValueAnimator+handler去异步加载每一个弹幕view显示到页面上,由于弹幕是一个接着一个被加载到页面上的 ,对于用户来说它就是在屏幕上飘了出来,也就是我们平时看视频时能经常看到的满屏加载的弹幕。在这里是设置了弹幕自动循环播放的,每一个弹幕间隔时长也有几秒钟,是为了可以点击弹幕事件而做的处理,因为有时候这样刷弹幕出来,刷新过快会导致弹幕速度过快,对于用户体验来说它就像是卡bug了一样,唰一下就没了,都来不及看清弹幕内容是什么;另外加载速度过快会导致数据量不够的情况下,很容易造成应用闪退,用户体验感极差。


import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
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.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.bumptech.glide.RequestManager;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import de.hdodenhof.circleimageview.CircleImageView;

public class DanmuView extends FrameLayout {

    private final String TAG = "DanmuView";
    private static final long DEFAULT_ANIM_DURATION = 18000; //默认每个动画的播放时长
    private static final long DEFAULT_QUERY_DURATION = 3000; //遍历弹幕的默认间隔
    private List<View> mViews = new ArrayList<>();//弹幕队列
    //记录当前仍在显示状态的弹幕的垂直方向位置(避免重复)
    private Set existMarginValues = new HashSet();
    private int nowIndex = 0;//下标
    private int mWidth;//弹幕的宽度
    private int mHeight;//弹幕的高度
    private boolean TopDirectionFixed;//弹幕顶部的方向是否固定
    private int mTopGravity = Gravity.CENTER_VERTICAL;//顶部方向固定时的默认对齐方式
    private RequestManager mGlide;//用于异步加载头像数据
    private OnClickListener mListener;//点击事件的监听
    private boolean IS_SHOW = true;//判断弹幕是否显示,默认是显示
    private final int TIP_SHOW_DANMU = 0x100;//显示弹幕
    private final int TIP_HIDE_DANMU = 0x101;//隐藏弹幕

    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, RequestManager glide) {
        this.mGlide = glide;
        //初始化下数据
        mViews = new ArrayList<>();
        existMarginValues = new HashSet<>();
        for (int i = 0; i < danmuList.size(); i++) {
            Danmu 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);
    }

    /**根据弹幕多少调整下刷新的时间
     * 尤其是弹幕数据少的时候;不然的话会造成刷新速度够快、添加弹幕失败,已经存在的弹幕还未结束动画,继续添加该弹幕数据会爆错,导致应用停止运行
     * addView(view) - The specified child already has a parent. You must call removeView() on the child s parent first.
     * */
    private long getDuration() {
        if (mViews != null) {
            if (mViews.size() == 1) {
                return 18300;
            } else if (mViews.size() == 2) {
                return 15000;
            } else if (mViews.size() == 3) {
                return 10000;
            } else if (mViews.size() == 4) {
                return 7000;
            } else if (mViews.size() == 5) {
                return 6000;
            } else if (mViews.size() == 6) {
                return 5500;
            } else {
                return DEFAULT_QUERY_DURATION;
            }
        }
        return DEFAULT_QUERY_DURATION;
    }

    @SuppressLint("HandlerLeak")
    private final Handler handler  = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == TIP_SHOW_DANMU) {
                removeMessages(msg.what);
                if (IS_SHOW) {
                    //循环取出弹幕显示
                    if (mViews.size() > 0) {
                        final View view = (View) mViews.get(nowIndex);
                        if (null != view) {
                            //添加弹幕
                            showDanmu(view);
                        }
                        nowIndex++;
                        if (nowIndex == mViews.size()) {
                            nowIndex = 0;
                        }
                        long time = getDuration();
                        sendEmptyMessageDelayed(TIP_SHOW_DANMU, time);
                    }
                }
            } else if (msg.what == TIP_HIDE_DANMU) {
                removeMessages(msg.what);
                nowIndex = 0;
            }
        }

    };

    /**
     * 将要展示的弹幕添加到队列中
     * @param danmu 弹幕数据
     */
    @SuppressLint("SetTextI18n")
    private void addDanmuToQueue(Danmu danmu) {

        if (null != danmu) {
            final View layout = View.inflate(getContext(), R.layout.layout_item_silde_content, null);
            //设置内容
            TextView userContent = layout.findViewById(R.id.tv_user_content);
            userContent.setText(danmu.getUserContent());

            //设置定位
            TextView userLocation = layout.findViewById(R.id.tv_user_location);
            String text = danmu.getUserLocation().isEmpty() ? "广东省" : danmu.getUserLocation();
            userLocation.setText(text);

            //设置点赞数
            TextView userLike = layout.findViewById(R.id.tv_user_like);
            if (danmu.getUserLike().equals("认同")) {
                userLike.setText(danmu.getUserLike());
            } else {
                userLike.setText("x" + danmu.getUserLike());
            }
            String uid = BaseUtil.getUid() + "";
            if (danmu.getUserId().equals(uid)) {
                layout.setBackgroundResource(R.drawable.ig_snztzdms09);
                userLike.setBackgroundResource(R.drawable.bg_item_like_ser);
            } else {
                layout.setBackgroundResource(R.drawable.ig_snztzdms12);
                userLike.setBackgroundResource(R.drawable.bg_item_like_nor);
            }
            //设置图片
            CircleImageView userIcon = layout.findViewById(R.id.iv_user_icon);

            LoadImage(mGlide, danmu.getHeaderUrl(), userIcon);

            layout.setOnClickListener(view -> {
                if (danmu.isFirst()) {
                    mListener.onItemClick(danmu.getId(), 1);
                    if (!userLike.getText().equals("认同")) {
                        int num = Integer.parseInt(danmu.getUserLike());
                        userLike.setText("x" + (num+1));
                    } else {
                        userLike.setText("x1");
                    }
                    danmu.setFirst(false);
                } else {
                    mListener.onItemClick(danmu.getId(), -1);
                    if (!userLike.getText().equals("x1")) {
                        userLike.setText("x" + danmu.getUserLike());
                    } else {
                        userLike.setText("认同");
                    }
                    danmu.setFirst(true);
                }
            } );

            layout.measure(0, 0);
            //添加弹幕到队列中
            mViews.add(layout);
        }

    }

    public interface OnClickListener {
        /**点击弹幕*/
        void onItemClick(long id, int status);

    }

    //实现这个View的监听器

    public void setOnClickListener(OnClickListener listener){

        this.mListener = listener;   //引用监听器类对象,在这里可以使用监听器类的对象

    }

    /**
     * 播放弹幕
     * @param topDirectionFixed 弹幕顶部的方向是否固定
     */
    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 (mViews.size() > 0) {
                        handler.sendEmptyMessage(TIP_SHOW_DANMU);
                    }
                }
            });
        } else {
            if (mViews.size() > 0) {
                handler.sendEmptyMessage(TIP_SHOW_DANMU);
            }
        }
    }

    /**
     * 显示弹幕,包括动画的执行
     * @param view view
     */
    @SuppressLint("RtlHardcoded")
    private void showDanmu(final View view) {
        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(animation -> {
            lp.leftMargin = (int) animation.getAnimatedValue();
            view.setLayoutParams(lp);
        });
        addView(view);//显示弹幕
        animator.setDuration(DEFAULT_ANIM_DURATION);
        animator.start();//开启动画
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                view.clearAnimation();
                existMarginValues.remove(view.getTag());//移除已使用过的顶部边距
                removeView(view);//移除弹幕
                animation.cancel();
            }
        });
    }

    private int getRandomTopMargin(View view) {

        //计算可用的行数
        int 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);
            //边界检查
            int range = 10;
            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;
            }
        }
    }

/**
*暴露给外面调用-可隐藏或者是显示弹幕 ,达到一键开启的效果
*/

    public void setShow(boolean IS_SHOW) {
        this.IS_SHOW = IS_SHOW;
        if (IS_SHOW) {
            handler.removeMessages(TIP_HIDE_DANMU);
            handler.removeMessages(TIP_SHOW_DANMU);
            handler.sendEmptyMessage(TIP_SHOW_DANMU);
        } else {
            handler.removeMessages(TIP_SHOW_DANMU);
            handler.removeMessages(TIP_HIDE_DANMU);
            handler.sendEmptyMessage(TIP_HIDE_DANMU);
        }
    }

    /**判断下是否有视图*/
    public boolean hasViews() {
        if (mViews != null) {
            return mViews.size() > 0;
        }
        return false;
    }

    /**释放线程资源,防止占用内存 -- 暴露给其他页面调用释放资源*/
    public void removeHandler() {
        handler.removeCallbacksAndMessages(null);
    }

	/**
     * 使用glide加载图片,避免出现Android Glide You cannot start a load for a destroyed activity 的异常,其原因是由于Activity/Fragment 已经 destroy,而程序代码中依然在使用 Glide 加载图片导致的
     * @param glide RequestManager 是帮助管理生命周期的,使得 Glide 的生命周期与 Activity/Fragment 保持同步,如果 Activity/Fragment 销毁,相关 Glide 加载也会进行销毁,从而达到不浪费内存的目的
     * @param url 图片url
     * @param view 加载的视图view
     */
    public void LoadImage(RequestManager glide, String url, ImageView view) {
        glide.load(url).placeholder(R.drawable.img_home_head_portrait1)//i避免没有解析到头像
                .error(R.drawable.img_home_head_portrait1)
                .into(view);
    }
}

三.相关调用

    <DanmuView
        android:id="@+id/layout_danmu"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent"/>
//加载弹幕数据到页面上 -- 初始化弹幕
  if (data != null && data.size() > 0) {
            ArrayList<Danmu> danmuList = new ArrayList<>();
            for (int i = 0; i < data.size(); i++) {
                AppPaperChatDetails detail = data.get(i);
                Danmu danmu = new Danmu();
                danmu.setId(detail.getId());
                danmu.setUserId(detail.getUid()+"");
                danmu.setHeaderUrl(detail.getAvatar());
                danmu.setUserContent(detail.getAssess());
                if (detail.getAssessLike() == 0) {
                    danmu.setUserLike("认同");
                } else {
                    danmu.setUserLike(""+detail.getAssessLike());
                }
                danmu.setUserLocation(detail.getProvince());
                danmu.setFirst(true);
                danmuList.add(danmu);
            }

            mBinding.layoutDanmu.add(danmuList, Glide.with(this));
            mBinding.layoutDanmu.setShow(true);
            mBinding.layoutDanmu.startPlay(false);
            mBinding.layoutDanmu.setOnClickListener((id, status) -> {
                if (id != 0) {
                    mPresenter.postBarrageStatus(id, status);//点赞
                }
            });
            mBinding.layoutDanmu.setVisibility(View.VISIBLE);
        } else {
            mBinding.layoutDanmu.setVisibility(View.GONE);
        }



//一键开关弹幕
      if (mBinding.tvChatClose.getVisibility() == View.VISIBLE) {
        if (mBinding.layoutDanmu.hasViews()) {
               mBinding.layoutDanmu.setShow(false);
               if (mBinding.layoutDanmu.getChildCount() > 0) {
                   mBinding.layoutDanmu.removeAllViews();
               }
               mBinding.layoutDanmu.setVisibility(View.GONE);
               mBinding.tvChatClose.setVisibility(View.GONE);
               mBinding.tvChatOpen.setVisibility(View.VISIBLE);
           } else {
               Toast.makeText(this, "暂无弹幕哦~", Toast.LENGTH_SHORT).show();
           }
       } else {
           if (mBinding.layoutDanmu.hasViews()) {
               mBinding.layoutDanmu.setShow(true);
               mBinding.layoutDanmu.setVisibility(View.VISIBLE);
               mBinding.tvChatClose.setVisibility(View.VISIBLE);
               mBinding.tvChatOpen.setVisibility(View.GONE);
           } else {
               Toast.makeText(this, "暂无弹幕哦~", Toast.LENGTH_SHORT).show();
           }
      }

效果图
大体框架就是如此,感兴趣的可以去操作试一试。
有些内容

后序

这样一个简单的自定义弹幕就开发完成了,基本上是拿过去就可以使用哦,相关的弹幕刷屏动画时间等,点击事件都可以根据自己的需求去更改,变得更加的多样化。有不足之处,欢迎指正,大家共同进步。

番外篇

弹幕的上下滚动内容

在布局文件中直接使用一个Linearlayout控件。


    <!--标签滚动-->
    <LinearLayout
        android:id="@+id/ll_label_ques"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="end"
        android:layout_marginEnd="@dimen/dp_15"
        android:padding="15dp"
        android:visibility="visible"
        android:showDividers="middle"
        />

可直接在activity里直接编写内容。


//在handler里刷新页面
if (msg.what == 0) {
       removeMessages(msg.what);
       if (isShow) {
           mBinding.llLabelQues.setVisibility(View.VISIBLE);
           TextView textView = obtainTextView();
           mBinding.llLabelQues.addView(textView);
           sendEmptyMessageDelayed(0, 2000);
           index++;
           if (texts != null && index == texts.length) {
               index = 0;
           }
           if (mBinding.llLabelQues.getChildCount() == 3) {
               mHandler.sendEmptyMessage(1);
           }
       }
   } else if (msg.what == 1) {
       removeMessages(msg.what);
       //给展示的第一个view增加渐变透明动画
       mBinding.llLabelQues.getChildAt(0).animate().alpha(0).setDuration(500).start();
       sendEmptyMessageDelayed(2, 3000);
   } else if (msg.what == 2) {
       removeMessages(msg.what);
       //删除顶部view
       if  (mBinding.llLabelQues.getChildCount() > 0) {
           mBinding.llLabelQues.removeViewAt(0);
       }
   } else if (msg.what == 3) {
       removeMessages(msg.what);
       if  (mBinding.llLabelQues.getChildCount() > 0) {
           mBinding.llLabelQues.removeAllViews();
       }
       mBinding.llLabelQues.setVisibility(View.GONE);
   }





//==============================提示的标签信息============================

    private String[] texts;
    private int index = 0;
    private boolean isShow = false;

    Pools.SimplePool<TextView> textViewSimplePool;

    private TextView obtainTextView() {

        if (textViewSimplePool != null) {
            TextView textView = textViewSimplePool.acquire();
            if (textView == null) {
                textView = new TextView(this);
                textView.setPadding(dp2px(10), dp2px(5), dp2px(10), dp2px(5));
                textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) textView.getLayoutParams();
                lp.setMargins(0, 10, 0, 10);
                textView.setLayoutParams(lp);
                textView.setTextColor(Color.parseColor("#FF7F5D"));
                textView.setMaxLines(1);
                textView.setEllipsize(TextUtils.TruncateAt.END);
                textView.setGravity(Gravity.CENTER);
                textView.setTextSize(15);
                textView.setBackgroundResource(R.drawable.bg_label);
            }
            textView.setText(texts[index]);
            return textView;
        }

        return new TextView(this);

    }


    private int dp2px(float dp) {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
    }


    @SuppressLint("SetTextI18n")
    @Override
    public void initLabelData(T record) {


        if (record != null) {

            List<String> arrs = record.getAvatars();
            if (arrs != null) {
                int i = 0;
                for (String url : arrs) {
                    if (i == 0) {
                        LoadImage(Glide.with(this), url, mBinding.ivUserImg1);
                    } else if (i == 1){
                        LoadImage(Glide.with(this), url, mBinding.ivUserImg2);
                    } else if (i == 2) {
                        LoadImage(Glide.with(this), url, mBinding.ivUserImg3);
                        break;
                    }
//                    else if (i == 3) {//弃用
//                        AppTestUtil.loadImage(Glide.with(this), url, mBinding.ivUserImg4);
//                        break;
//                    }
                    i++;
                }
            }

            if (record.getMember() == 0) {
                Random random = new Random();
                int num = random.nextInt(100)+1;
                mBinding.tvUserTip.setText("有"+ num + "人正在挑战");
            } else {
                mBinding.tvUserTip.setText("有"+AppTestUtil.toNumber(record.getMember())+"正在挑战");
            }

            if (record.getContent() != null && record.getContent().size() > 0) {
                texts = new String[record.getContent().size()];
                int i = 0;
                for (String text : record.getContent()) {
                    if (!text.isEmpty()) {
                        //正则表达式 提取字符串中的数字
                        Pattern pattern = Pattern.compile("\\d+");
                        Matcher matcher = pattern.matcher(text);
                        String result = null;
                        while (matcher.find()) {
                            result = matcher.group(0);
                        }
                        if (!"".equals(result)) {
                            assert result != null;
                            int day = Integer.parseInt(result) / 1440;
                            String date = "";
                            if (day < 1) {
                                int hour = Integer.parseInt(result) / 60;
                                if (hour < 1) {
                                    texts[i] = text;
                                } else {
                                    date = " " + hour + "小时前";
                                    String str1 = text.replaceAll("分钟前", "");
                                    texts[i] = str1.replaceAll(result, date);
                                }
                            } else {
                                date = " " + day + "天前";
                                String str1 = text.replaceAll("分钟前", "");
                                texts[i] = str1.replaceAll(result, date);
                            }
                        }
                        i++;
                    }
                }
            } else {
                texts = new String[] {"美美 10分钟前答对了该题", "小明 8分钟前答对了该题", "黄兴雅 6分钟前答对了该题", "凡凡 4分钟前答对了该题", "张馨雅 2分钟前答对了该题"};
            }

            textViewSimplePool = new Pools.SimplePool<>(texts.length);


            transition = new LayoutTransition();
            //添加动画
            ObjectAnimator valueAnimator = ObjectAnimator.ofFloat(null, " aloha", 0, 1);
            valueAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    //当前展示超过四条,执行删除动画
                    if (mBinding.llLabelQues.getChildCount() == 2) {
                        mHandler.sendEmptyMessage(1);
                    }
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    if (mBinding.llLabelQues.getChildCount() == 3) {
                        //动画执行完毕,删除view
                        mHandler.sendEmptyMessage(2);
                    }
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
            transition.setAnimator(LayoutTransition.APPEARING, valueAnimator);
            //删除动画
            PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0, 0);
            ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(null, new PropertyValuesHolder[]{alpha}).setDuration(transition.getDuration(LayoutTransition.DISAPPEARING));
            transition.setAnimator(LayoutTransition.DISAPPEARING, objectAnimator);

            mBinding.llLabelQues.setLayoutTransition(transition);
//            mHandler.sendEmptyMessage(0);
        }


    }


    /**
     * 使用glide加载图片,避免出现Android Glide You cannot start a load for a destroyed activity 的异常,其原因是由于Activity/Fragment 已经 destroy,而程序代码中依然在使用 Glide 加载图片导致的
     * @param glide RequestManager 是帮助管理生命周期的,使得 Glide 的生命周期与 Activity/Fragment 保持同步,如果 Activity/Fragment 销毁,相关 Glide 加载也会进行销毁,从而达到不浪费内存的目的
     * @param url 图片url
     * @param view 加载的视图view
     */
    public void LoadImage(RequestManager glide, String url, ImageView view) {
        glide.load(url).placeholder(R.drawable.img_home_head_portrait1)//i避免没有解析到头像
                .error(R.drawable.img_home_head_portrait1)
                .into(view);
    }


    //=====================================================================
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值