一、效果图展示

二、思路分析

首先,上效果图,这样的评论界面很常见,日常新闻,社交等类的app中都有评论功能,今天就来看看怎么实现?

实现这种效果并不难,首先最外层肯定是Recycleview,然后设置他的Adapter来实现,重点在于Adapter的实现。

三、准备

1.首先,得有一个评论javaBean类,用来封装评论信息,当然这些信息在开发中都是从服务器端获取的,这个类为CommentBean,代码如下:

public class CommentBean {

    /**
     * author : xiaowei
     * content : 正解啊,比如中午登机,早上不吃空腹上,到时候飞机餐就算是两个圆圆的小面包都吃得有滋有味!开始怀疑,到底是飞机旅行、真是高大上呀!??哈哈…我见不少人特意留着飞机上发的食物带回去,落地后送人吃,很拽的来一句:飞机食品哦!…笑得不行鸟
     * avatar : http://pic1.zhimg.com/da8e974dc_im.jpg
     * time : 1413603692
     * reply_to : {"content":"习惯就好了。。 国内的航空公司基本都刷过。。 掌握了\u201c让飞机餐变得非常好吃\u201d的秘诀 同学们准备好 秘诀就是〈饿半天肚子登机〉 等到你吃到飞机餐那刻会泪流满面\u2026\u2026 好吧说了那么多其实也就因为六个字 没钱\u2026没钱\u2026没钱 所以,努力当大爷自己买小飞机吧!","status":0,"id":545589,"author":"Samuelback"}
     * id : 545838
     * likes : 2
     */

    private List<CommentsBean> comments;

    public List<CommentsBean> getComments() {
        return comments;
    }

    public void setComments(List<CommentsBean> comments) {
        this.comments = comments;
    }

    public static class CommentsBean {
        private String author;
        private String content;
        private String avatar;
        private int time;
        /**
         * content : 习惯就好了。。 国内的航空公司基本都刷过。。 掌握了“让飞机餐变得非常好吃”的秘诀 同学们准备好 秘诀就是〈饿半天肚子登机〉 等到你吃到飞机餐那刻会泪流满面…… 好吧说了那么多其实也就因为六个字 没钱…没钱…没钱 所以,努力当大爷自己买小飞机吧!
         * status : 0
         * id : 545589
         * author : Samuelback
         */

        private ReplyToBean reply_to;
        private int id;
        private int likes;

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }

        public String getAvatar() {
            return avatar;
        }

        public void setAvatar(String avatar) {
            this.avatar = avatar;
        }

        public int getTime() {
            return time;
        }

        public void setTime(int time) {
            this.time = time;
        }

        public ReplyToBean getReply_to() {
            return reply_to;
        }

        public void setReply_to(ReplyToBean reply_to) {
            this.reply_to = reply_to;
        }

        public int getId() {
            return id;
        }

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

        public int getLikes() {
            return likes;
        }

        public void setLikes(int likes) {
            this.likes = likes;
        }

        public static class ReplyToBean {
            private String content;
            private int status;
            private int id;
            private String author;

            private int expandState = 0;

            public int getExpandState() {
                return expandState;
            }

            public void setExpandState(int expandState) {
                this.expandState = expandState;
            }

            public String getContent() {
                return content;
            }

            public void setContent(String content) {
                this.content = content;
            }

            public int getStatus() {
                return status;
            }

            public void setStatus(int status) {
                this.status = status;
            }

            public int getId() {
                return id;
            }

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

            public String getAuthor() {
                return author;
            }

            public void setAuthor(String author) {
                this.author = author;
            }
        }
    }
}

2.然后,开始编写itme_comment.xml文件,用来提供给Adapter的每一个item

xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:paddingTop="12dp"
    android:paddingLeft="12dp"
    android:paddingRight="12dp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.codeest.geeknews.widget.CircleImageView
        android:id="@+id/civ_comment_face"
        android:layout_marginEnd="10dp"
        android:layout_width="@dimen/comment_face_size"
        android:layout_height="@dimen/comment_face_size"/>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/tv_comment_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textStyle="bold"/>
        <TextView
            android:id="@+id/tv_comment_content"
            android:textColor="@color/comment_text"
            android:textSize="14sp"
            android:layout_marginTop="8dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:lineSpacingMultiplier="1.3"
            />
        <TextView
            android:id="@+id/tv_comment_reply"
            android:textColor="@color/comment_reply"
            android:ellipsize="end"
            android:layout_marginTop="8dp"
            android:background="@drawable/reply_bg"
            android:paddingStart="6dp"
            android:lineSpacingMultiplier="1.2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:layout_marginBottom="12dp"
            >
            <TextView
                android:id="@+id/tv_comment_time"
                android:layout_alignParentStart="true"
                android:textColor="@color/comment_btn"
                android:layout_centerVertical="true"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <TextView
                android:layout_toEndOf="@id/tv_comment_time"
                android:layout_marginStart="10dp"
                android:id="@+id/tv_comment_expand"
                android:text="@string/comment_expand"
                android:layout_centerVertical="true"
                android:textColor="@color/comment_btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
            <TextView
                android:id="@+id/tv_comment_like"
                android:layout_alignParentEnd="true"
                android:layout_centerVertical="true"
                android:layout_marginEnd="10dp"
                android:textColor="@color/comment_btn"
                android:drawableLeft="@mipmap/ic_daily_like"
                android:drawablePadding="5dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </RelativeLayout>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="@color/comment_line"/>
    </LinearLayout>
</LinearLayout>

四,重点CommentAdapter

public class CommentAdapter extends RecyclerView.Adapter<CommentAdapter.ViewHolder>{

    private LayoutInflater inflater;
    private List<CommentBean.CommentsBean> mList;
    private Context mContext;

    private static final int STATE_NULL = 0;    //未知
    private static final int STATE_NONE = 1;    //无需展开
    private static final int STATE_EXPAND = 2;  //已展开
    private static final int STATE_SHRINK = 3;  //已收缩

    private static final int MAX_LINE = 2;  //起始最多显示2行

    public CommentAdapter(Context mContext,List<CommentBean.CommentsBean> mList) {
        this.mList = mList;
        this.mContext = mContext;
        inflater = LayoutInflater.from(mContext);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ViewHolder(inflater.inflate(R.layout.item_comment,parent,false));
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        CommentBean.CommentsBean info = mList.get(position);
        ImageLoader.load(mContext,info.getAvatar(),holder.civFace);
        holder.tvName.setText(info.getAuthor());
        holder.tvContent.setText(info.getContent());
        holder.tvTime.setText(DateUtil.formatTime2String(info.getTime()));
        holder.tvLike.setText(String.valueOf(info.getLikes()));
        if (info.getReply_to() != null && info.getReply_to().getId() != 0) {
            holder.tvReply.setVisibility(View.VISIBLE);
            SpannableString ss = new SpannableString("@" + info.getReply_to().getAuthor() + ": " + info.getReply_to().getContent());
            ss.setSpan(new ForegroundColorSpan(ContextCompat.getColor(mContext,R.color.comment_at)), 0,info.getReply_to().getAuthor().length() + 2 , Spanned.SPAN_INCLUSIVE_INCLUSIVE);
//            holder.tvReply.setText(String.format("@%s: %s",info.getReply_to().getAuthor(),info.getReply_to().getContent()));
            holder.tvReply.setText(ss);
            if (info.getReply_to().getExpandState() == STATE_NULL) {    //未知
                holder.tvReply.post(new Runnable() {
                    @Override
                    public void run() {
                        if (holder.tvReply.getLineCount() > MAX_LINE) {
                            holder.tvReply.setMaxLines(MAX_LINE);
                            holder.tvExpand.setVisibility(View.VISIBLE);
                            holder.tvExpand.setText("展开");
                            mList.get(holder.getAdapterPosition()).getReply_to().setExpandState(STATE_SHRINK);
                            holder.tvExpand.setOnClickListener(new OnStateClickListener(holder.getAdapterPosition(), holder.tvReply));
                        } else {
                            holder.tvExpand.setVisibility(View.GONE);
                            mList.get(holder.getAdapterPosition()).getReply_to().setExpandState(STATE_NONE);
                        }
                    }
                });
            } else if(info.getReply_to().getExpandState() == STATE_NONE) {  //无需展开
                holder.tvExpand.setVisibility(View.GONE);
            } else if(info.getReply_to().getExpandState() == STATE_EXPAND) {    //已展开
                holder.tvReply.setMaxLines(Integer.MAX_VALUE);
                holder.tvExpand.setText("收起");
                holder.tvExpand.setVisibility(View.VISIBLE);
                holder.tvExpand.setOnClickListener(new OnStateClickListener(holder.getAdapterPosition(), holder.tvReply));
            } else {    //已收缩
                holder.tvReply.setMaxLines(MAX_LINE);
                holder.tvExpand.setText("展开");
                holder.tvExpand.setVisibility(View.VISIBLE);
                holder.tvExpand.setOnClickListener(new OnStateClickListener(holder.getAdapterPosition(), holder.tvReply));
            }
        } else {
            holder.tvReply.setVisibility(View.GONE);
            holder.tvExpand.setVisibility(View.GONE);
        }
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    private class OnStateClickListener implements View.OnClickListener {

        TextView replyView;
        int position;

        public OnStateClickListener(int position,TextView replyView) {
            this.position = position;
            this.replyView = replyView;
        }

        @Override
        public void onClick(View view) {
            TextView tv = (TextView) view;
            if (mList.get(position).getReply_to().getExpandState() == STATE_SHRINK) {
                tv.setText("收起");
                replyView.setMaxLines(Integer.MAX_VALUE);
                replyView.setEllipsize(null);
                mList.get(position).getReply_to().setExpandState(STATE_EXPAND);
            } else {
                tv.setText("展开");
                replyView.setMaxLines(MAX_LINE);
                replyView.setEllipsize(TextUtils.TruncateAt.END);
                mList.get(position).getReply_to().setExpandState(STATE_SHRINK);
            }
        }
    }

    public static class ViewHolder extends RecyclerView.ViewHolder{

        @BindView(R.id.civ_comment_face)
        CircleImageView civFace;
        @BindView(R.id.tv_comment_name)
        TextView tvName;
        @BindView(R.id.tv_comment_content)
        TextView tvContent;
        @BindView(R.id.tv_comment_time)
        TextView tvTime;
        @BindView(R.id.tv_comment_expand)
        TextView tvExpand;
        @BindView(R.id.tv_comment_like)
        TextView tvLike;
        @BindView(R.id.tv_comment_reply)
        TextView tvReply;

        public ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this,itemView);
        }
    }

}

然后,常规操作调用

mList = new ArrayList<>();
        mAdapter = new CommentAdapter(mContext,mList);
        rvCommentList.setLayoutManager(new LinearLayoutManager(mContext));
        rvCommentList.setAdapter(mAdapter);

完成。。。