android实现评论区功能
迪丽瓦拉
2025-05-30 14:49:48
0

效果

在这里插入图片描述

activity_detail.xml







































































DetailActivity

package com.coral3.ah.ui.activity;import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.coral3.ah.R;
import com.coral3.ah.adapter.DetailAdapter;
import com.coral3.ah.data.DataDetail;
import com.coral3.ah.data.DataOther;
import com.coral3.common_module.base.BaseActivity;
import com.coral3.common_module.utils.GlideUtil;
import com.coral3.common_module.utils.StatusBarUtil;
import com.coral3.common_module.utils.SysManager;public class DetailActivity extends BaseActivity {private ImageView userLogo;private View vStatusSeat;private ImageView ivBack;private RecyclerView recyclerView;private DetailAdapter detailAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_detail);StatusBarUtil.setLightStatusBar(this, true, true);initView();initListener();}@Overrideprotected void initView() {ivBack = findViewById(R.id.iv_back);recyclerView = findViewById(R.id.recyclerView);recyclerView.setLayoutManager(new GridLayoutManager(this, 6, GridLayoutManager.VERTICAL, false));detailAdapter = new DetailAdapter(this);recyclerView.setAdapter(detailAdapter);detailAdapter.addData(DataDetail.getData());userLogo = findViewById(R.id.iv_ta_logo);GlideUtil.loadImgCircle(this, R.mipmap.ic_head_default_girl, userLogo);vStatusSeat = findViewById(R.id.v_status_seat);int height = SysManager.getInstance().getStatusBarHeight(this);ViewGroup.LayoutParams params = vStatusSeat.getLayoutParams();params.height = height;vStatusSeat.setLayoutParams(params);}@Overrideprotected void initListener() {ivBack.setOnClickListener(this);// 滑动监听recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);// 判断是否到底部, offset是滑动距离, range是总的内容高度, Extent是控件高度if (recyclerView.getChildCount() > 0&& recyclerView.computeVerticalScrollOffset() + recyclerView.computeVerticalScrollExtent()>= recyclerView.computeVerticalScrollRange()) {if(detailAdapter.getLoadStatus() == 2) return;if(detailAdapter.getItemCount() < 25) {detailAdapter.addData(DataOther.getOtherList());}else {detailAdapter.setLoadStatus(2);detailAdapter.addData("没有更多数据");}}}});}@Overridepublic void onClick(View view) {if(view.getId() == R.id.iv_back) {finish();}}
}

DetailAdapter

package com.coral3.ah.adapter;import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.coral3.ah.R;
import com.coral3.ah.bean.BeanDetail;
import com.coral3.common_module.components.LayoutForTextView;
import com.coral3.common_module.components.TalkChildList;
import com.coral3.common_module.entity.bo.ListBO;
import com.coral3.common_module.base.BaseBuildInfo;
import com.coral3.common_module.utils.AppManager;
import com.coral3.common_module.utils.GlideUtil;
import com.coral3.common_module.utils.LogUtil;
import com.coral3.common_module.utils.WinManagerUtil;
import com.wang.avi.AVLoadingIndicatorView;
import com.youth.banner.Banner;
import com.youth.banner.indicator.CircleIndicator;
import java.util.ArrayList;
import java.util.List;/*** 复制型的Adapter*/public class DetailAdapter extends RecyclerView.Adapter {// 头部public static final int ITEM_HEAD = 0;// 信息public static final int ITEM_MESSAGE = 1;// 聊天列表public static final int ITEM_TALK = 2;// 没有更多public static final int ITEM_NO_MORE = 3;private int status;private Context context;private List datas = new ArrayList<>();public DetailAdapter(Context context) {this.context = context;}@Overridepublic void onAttachedToRecyclerView(RecyclerView recyclerView) {super.onAttachedToRecyclerView(recyclerView);RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();if (layoutManager instanceof GridLayoutManager) {GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {return 6;}});}}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if (viewType == ITEM_HEAD) {return new HeadViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recyclerview_item_talk_banner, parent, false));} else if (viewType == ITEM_MESSAGE) {return new MessageViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recyclerview_item_talk_center, parent, false));} else if (viewType == ITEM_TALK) {return new TalkViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recyclerview_item_talk_list, parent, false));} else if (viewType == ITEM_NO_MORE) {return new NoMoreViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_no_more, parent, false));} else {return null;}}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {//头部if (holder instanceof HeadViewHolder) {BeanDetail beanDetail = (BeanDetail) datas.get(position);Banner banner = ((HeadViewHolder) holder).banner;List images = new ArrayList<>();//通过Glide获取网络图片宽高Glide.with(context).asBitmap().load(BaseBuildInfo.BASE_URL + "/" + BaseBuildInfo.CONTEXT + "/userFile/image/test_xhs.jpg").into(new CustomTarget() {@Overridepublic void onResourceReady(@NonNull Bitmap bitmap, @Nullable Transition transition) {int width = bitmap.getWidth();int height = bitmap.getHeight();int screenW = WinManagerUtil.getWidth(AppManager.getInstance().getTopActivity());int bh = screenW * height / width;ViewGroup.LayoutParams params = banner.getLayoutParams();params.height = bh;banner.setLayoutParams(params);}@Overridepublic void onLoadCleared(@Nullable Drawable drawable) {}});images.add(BaseBuildInfo.BASE_URL + "/" + BaseBuildInfo.CONTEXT + "/userFile/image/test_xhs.jpg");images.add(BaseBuildInfo.BASE_URL + "/" + BaseBuildInfo.CONTEXT + "/userFile/image/test_xhs2.jpg");images.add(BaseBuildInfo.BASE_URL + "/" + BaseBuildInfo.CONTEXT + "/userFile/image/test_xhs1.jpg");banner.setAdapter(new ImageAdapter(images, context)).setIndicator(new CircleIndicator(context)).isAutoLoop(false);banner.setIndicatorSelectedColor(context.getResources().getColor(R.color.banner_Indicator_select));}//信息else if (holder instanceof MessageViewHolder) {BeanDetail beanDetail = (BeanDetail) datas.get(position);MessageViewHolder messageViewHolder = (MessageViewHolder) holder;GlideUtil.loadImgCircle(context, R.mipmap.ic_head_default, messageViewHolder.senderAvatar);}// 聊天列表else if (holder instanceof TalkViewHolder) {ListBO listBO = (ListBO) datas.get(position);TalkViewHolder talkViewHolder = (TalkViewHolder) holder;GlideUtil.loadImgCircle(context, R.mipmap.ic_head_default, talkViewHolder.ivAvatar);talkViewHolder.tvTalkContent.setText(listBO.getTalkContent());talkViewHolder.tvExpand.setOnClickListener(view -> {LogUtil.d("展开");talkViewHolder.tvExpand.setVisibility(View.GONE);talkViewHolder.tclList.addData(false);talkViewHolder.llExpandLoading.setVisibility(View.VISIBLE);new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1500);} catch (InterruptedException e) {e.printStackTrace();}((Activity)context).runOnUiThread(new Runnable() {@Overridepublic void run() {talkViewHolder.tvExpand.setText("展开更多回复");talkViewHolder.tvExpand.setVisibility(View.VISIBLE);talkViewHolder.llExpandLoading.setVisibility(View.GONE);}});}}).start();});talkViewHolder.tvExpand.setVisibility(View.VISIBLE);LayoutForTextView lf = talkViewHolder.layoutForTextView;LinearLayout parent = (LinearLayout) lf.getParent();parent.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {@Overridepublic boolean onPreDraw() {parent.getViewTreeObserver().removeOnPreDrawListener(this);ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) lf.getLayoutParams();layoutParams.width = parent.getWidth();lf.setLayoutParams(layoutParams);return true;}});}}@Overridepublic int getItemCount() {return datas.size();}@Overridepublic int getItemViewType(int position) {if (position == 0) {//确定第一个是头部return ITEM_HEAD;} else if (position == 1) {//确定第二个是信息return ITEM_MESSAGE;} else if (datas.get(position) instanceof ListBO) {return ITEM_TALK;} else {//确定String用户商品头部return ITEM_NO_MORE;}}/*** 头部*/public class HeadViewHolder extends RecyclerView.ViewHolder {public Banner banner;public HeadViewHolder(View itemView) {super(itemView);banner = itemView.findViewById(R.id.talk_banner);}}/*** 信息*/public class MessageViewHolder extends RecyclerView.ViewHolder {public ImageView senderAvatar;public MessageViewHolder(View itemView) {super(itemView);senderAvatar = itemView.findViewById(R.id.iv_my_logo);}}/*** 评论列表*/public class TalkViewHolder extends RecyclerView.ViewHolder {public ImageView ivAvatar;public TextView tvTalkContent, tvExpand;public TalkChildList tclList;public AVLoadingIndicatorView aviLoading;public LinearLayout llExpandLoading;public LayoutForTextView layoutForTextView;public TalkViewHolder(View itemView) {super(itemView);ivAvatar = itemView.findViewById(R.id.iv_user_avatar);tvTalkContent = itemView.findViewById(R.id.tv_talk_content);tclList = itemView.findViewById(R.id.tcl_list);tvExpand = itemView.findViewById(com.coral3.common_module.R.id.tv_expand);aviLoading = itemView.findViewById(com.coral3.common_module.R.id.avi_loading);llExpandLoading = itemView.findViewById(R.id.ll_expand_loading);layoutForTextView = itemView.findViewById(R.id.lf_talk_text);}}/*** 其他*/public class NoMoreViewHolder extends RecyclerView.ViewHolder {public TextView tv_name;public NoMoreViewHolder(View itemView) {super(itemView);tv_name = itemView.findViewById(R.id.tv_load_end);}}/*** 添加数据*/public void addData(BeanDetail detail) {//确定前两个的数据,就可以这样写datas.add(detail);datas.add(detail);datas.addAll(detail.getTalkList());notifyDataSetChanged();}/*** 添加数据*/public void addData(List others) {datas.addAll(others);notifyDataSetChanged();}/*** 添加数据*/public void addData(String noMore) {datas.add(noMore);notifyDataSetChanged();}public void setLoadStatus(int status){this.status = status;}public int getLoadStatus(){return status;}/*** 清除数据*/public void clearData() {datas.clear();notifyDataSetChanged();}
}
 

layout_recyclerview_item_talk_banner.xml




layout_recyclerview_item_talk_center.xml




layout_recyclerview_item_talk_list.xml




layout_recyclerview_item_talk_list_child.xml




TalkListChildAdapter

package com.coral3.common_module.adapter;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.coral3.common_module.R;
import com.coral3.common_module.components.LayoutForTextView;
import com.coral3.common_module.entity.bo.ListBO;
import com.coral3.common_module.utils.GlideUtil;
import com.coral3.common_module.utils.LogUtil;import java.util.ArrayList;
import java.util.List;/*** 复制型的Adapter*/public class TalkListChildAdapter extends RecyclerView.Adapter {// 聊天列表public static final int ITEM_TALK = 2;private Context context;private List datas = new ArrayList<>();public TalkListChildAdapter(Context context) {this.context = context;}@Overridepublic void onAttachedToRecyclerView(RecyclerView recyclerView) {super.onAttachedToRecyclerView(recyclerView);RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();if (layoutManager instanceof GridLayoutManager) {GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {return 6;}});}}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if (viewType == ITEM_TALK) {return new TalkViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_recyclerview_item_talk_list_child, parent, false));}return null;}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {if(holder instanceof TalkViewHolder) {TalkViewHolder talkViewHolder = (TalkViewHolder) holder;if (listener != null) {talkViewHolder.llTalkItem.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {listener.myClick(v,position);}});// set LongClicktalkViewHolder.llTalkItem.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {listener.mLongClick(v,position);return true;}});}ListBO listBO = (ListBO) datas.get(position);GlideUtil.loadImgCircle(context, R.mipmap.ic_head_default, talkViewHolder.ivAvatar);talkViewHolder.tvTalkContent.setText(listBO.getTalkContent());LayoutForTextView lf = talkViewHolder.layoutForTextView;LinearLayout parent = (LinearLayout) lf.getParent();parent.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {@Overridepublic boolean onPreDraw() {parent.getViewTreeObserver().removeOnPreDrawListener(this);ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) lf.getLayoutParams();layoutParams.width = parent.getWidth();lf.setLayoutParams(layoutParams);return true;}});}}private OnMyItemClickListener listener;public void setOnMyItemClickListener(OnMyItemClickListener listener){this.listener = listener;}public interface OnMyItemClickListener{void myClick(View v,int pos);void mLongClick(View v,int pos);}@Overridepublic int getItemCount() {return datas.size();}@Overridepublic int getItemViewType(int position) {if (datas.get(position) instanceof ListBO) {return ITEM_TALK;}return 0;}/*** 评论列表*/public class TalkViewHolder extends RecyclerView.ViewHolder {public ImageView ivAvatar;public TextView tvTalkContent;public LinearLayout llTalkItem;public LayoutForTextView layoutForTextView;public TalkViewHolder(View itemView) {super(itemView);ivAvatar = itemView.findViewById(R.id.iv_user_avatar);tvTalkContent = itemView.findViewById(R.id.tv_talk_content);llTalkItem = itemView.findViewById(R.id.ll_talk_item);layoutForTextView = itemView.findViewById(R.id.lf_talk_text);}}/*** 添加数据*/public void addData(ListBO listBO) {datas.add(listBO);notifyDataSetChanged();}/*** 清除数据*/public void clearData() {datas.clear();notifyDataSetChanged();}
}
 

TalkChildList

package com.coral3.common_module.components;import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.coral3.common_module.R;
import com.coral3.common_module.adapter.TalkListChildAdapter;
import com.coral3.common_module.entity.bo.ListBO;
import com.coral3.common_module.utils.LogUtil;public class TalkChildList extends LinearLayout{private Context mContext;private View view;// 列表相关private RecyclerView rv;private TalkListChildAdapter rvAdapter;public TalkChildList(Context context, @Nullable AttributeSet attrs) {super(context, attrs);mContext = context;initView();initListener();addData(true);}private void initView() {view = LayoutInflater.from(mContext).inflate(R.layout.layout_list, this);initRv();}private void initListener(){rvAdapter.setOnMyItemClickListener(new TalkListChildAdapter.OnMyItemClickListener() {@Overridepublic void myClick(View v, int pos) {LogUtil.d("onClick " + pos);}@Overridepublic void mLongClick(View v, int pos) {LogUtil.d("onClick " + pos);}});}private void initRv() {rv = view.findViewById(R.id.recyclerview);rv.setLayoutManager(new GridLayoutManager(mContext, 6, GridLayoutManager.VERTICAL, false));rvAdapter = new TalkListChildAdapter(mContext);rv.setAdapter(rvAdapter);}public void addData(Boolean isFirstLoad){if(isFirstLoad){rvAdapter.addData(new ListBO("牡丹的我 何必在意"));}else{new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1500);} catch (InterruptedException e) {e.printStackTrace();}((Activity)mContext).runOnUiThread(new Runnable() {@Overridepublic void run() {rvAdapter.addData(new ListBO("喜欢有时候不能超越一切"));rvAdapter.addData(new ListBO("总在不经意之间想起了你"));rvAdapter.addData(new ListBO("世间万物 春风细雨 开心你我"));rvAdapter.addData(new ListBO("总在寻找一个人 那人也在寻找我"));rvAdapter.addData(new ListBO("世间很多东西不是靠等 而是主动去把握 若喜欢大胆去追求吧 若竭尽所能未如愿 也无憾"));rvAdapter.notifyDataSetChanged();}});}}).start();}}
}

LayoutForTextView

package com.coral3.common_module.components;import android.content.Context;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;/*** Created by 蓝之静云 on 2023/03/10*/
public class LayoutForTextView extends ViewGroup {//单行显示private static final int SINGLE_LINE = 0x01;//多行显示private static final int MULTI_LINE = 0x02;//显示到下一行private static final int NEXT_LINE = 0x03;//显示样式private int type;//绘制文字最后一行的顶部坐标private int lastLineTop;//绘制文字最后一行的右边坐标private float lastLineRight;public LayoutForTextView(Context context) {super(context);}public LayoutForTextView(Context context, AttributeSet attrs) {super(context, attrs);}public LayoutForTextView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int childCount = getChildCount();int w = MeasureSpec.getSize(widthMeasureSpec);if (childCount == 2) {TextView tv = null;if(getChildAt(0) instanceof  TextView){tv = (TextView) getChildAt(0);initTextParams(tv.getText(), tv.getMeasuredWidth(), tv.getPaint());}else{throw new RuntimeException("LayoutForTextView first child view not a TextView");}View sencodView = getChildAt(1);//测量子view的宽高measureChildren(widthMeasureSpec, heightMeasureSpec);//两个子view宽度相加小于该控件宽度的时候if (tv.getMeasuredWidth() + sencodView.getMeasuredWidth() <= w) {int width = tv.getMeasuredWidth()+sencodView.getMeasuredWidth();//计算高度int height = Math.max(tv.getMeasuredHeight(), sencodView.getMeasuredHeight());//设置该viewgroup的宽高setMeasuredDimension(width, height);type = SINGLE_LINE;return;}if (getChildAt(0) instanceof TextView) {//最后一行文字的宽度加上第二个view的宽度大于viewgroup宽度时第二个控件换行显示if (lastLineRight + sencodView.getMeasuredWidth() > w) {setMeasuredDimension(tv.getMeasuredWidth(), tv.getMeasuredHeight() + sencodView.getMeasuredHeight());type = NEXT_LINE;return;}int height = Math.max(tv.getMeasuredHeight(), lastLineTop + sencodView.getMeasuredHeight());setMeasuredDimension(tv.getMeasuredWidth(), height);type = MULTI_LINE;}} else {throw new RuntimeException("LayoutForTextView child count must is 2");}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (type == SINGLE_LINE || type == MULTI_LINE) {TextView tv = (TextView) getChildAt(0);View v1 = getChildAt(1);//设置第二个view在Textview文字末尾位置tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());int left = (int) lastLineRight;int top  = lastLineTop;//最后一行的高度 注:通过staticLayout得到的行高不准确故采用这种方式int lastLineHeight = tv.getBottom()-tv.getPaddingBottom() -lastLineTop;//当第二view高度小于单行文字高度时竖直居中显示if(v1.getMeasuredHeight() < lastLineHeight){top = lastLineTop + (lastLineHeight - v1.getMeasuredHeight())/2;}v1.layout(left, top, left + v1.getMeasuredWidth(), top+v1.getMeasuredHeight());} else if (type == NEXT_LINE) {View v0 = getChildAt(0);View v1 = getChildAt(1);//设置第二个view换行显示v0.layout(0, 0, v0.getMeasuredWidth(), v0.getMeasuredHeight());v1.layout(0, v0.getMeasuredHeight(), v1.getMeasuredWidth(), v0.getMeasuredHeight() + v1.getMeasuredHeight());}}/*** 得到Textview绘制文字的基本信息* @param text Textview的文字内容* @param maxWidth Textview的宽度* @param paint 绘制文字的paint*/private void initTextParams(CharSequence text, int maxWidth, TextPaint paint) {StaticLayout staticLayout = new StaticLayout(text, paint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);int lineCount = staticLayout.getLineCount();lastLineTop = staticLayout.getLineTop(lineCount - 1);lastLineRight = staticLayout.getLineRight(lineCount - 1);}}

推荐的一款开源加载动画插件

https://gitcode.net/mirrors/81813780/avloadingindicatorview?utm_source=csdn_github_accelerator

相关内容