前言
在开发弹幕之前我也搜索了很多文章来借鉴,但由于太多布局都不是自己想要的,而且相关引入也无法使用,在最后决定开发自定义弹幕。在网上搜索了大量的自定义布局,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);
}
//=====================================================================