Android类Reddit循环评论列表开发

最近因为项目需求,需要开发类似Reddit里面无限循环的评论列表,于是就开始研究其实现方式和可用性,reddit评论如下:


reddit评论

从最开始的印象看,我们可以看到这是一个树状列表,如果没有限制的话,可以无限循环嵌套,当然我看过的第一实现想法是LinearLayout无限循环的套


树状列表

这种方式最好实现,根据传输的数据结构,View一层层的add,就能实现;但是,由于嵌套过多,性能不行,故抛弃之。

一种方案不行,换一种方案,还是用LinearLayout实现,但是只有一层结构,不嵌套,通过数据结构来告知LinearLayout当前是第几层,根据不同层次来绘制布局,结果如图:

树状列表

大致原理如下:

  • 第一层绘制
1.png
  • 第二层绘制
2.png
  • 第三层绘制
3.png
  • ...

根据层数的不同在最左边添加竖线,以上的实现是行的通的,就是数据结构转换需要花点时间。
按理来说,事情到这已经结束了,但是...但是...通过测试,当加载数据有几百条的时候,会非常非常卡,会卡几秒才加载出来,这非常影响应用体验,抛弃之。

一种方案又不行,继续研究,这次使用RecyclerView形式,至于为什么使用RecyclerView呢,因为它不管你有多少数据,只会加载可见页面的数据,故而对于数据比较多的情况下,效率和体验都能不错(当然ListView应该也行),首先构建数据结构:

public class Comment {
/**
 * 孩子数量
 */
private List<Comment> children;
/**
 * 展示内容
 */
private String content;
/**
 * 是否可见,父节点收起后,子节点需要隐藏
 */
private boolean isVisible = true;
/**
 * 当前节点是否收起状态
 */
private boolean isExpand = true;
/**
 * 是否需要还原状态,父节点收起后会影响到子节点的状态
 */
private boolean isNeedReduction = false;
/**
 * 当前节点等级,根据等级不同绘制竖线
 */
private int level;


public boolean isNeedReduction() {
    return isNeedReduction;
}

public void setNeedReduction(boolean needReduction) {
    isNeedReduction = needReduction;
}

public int getLevel() {
    return level;
}

public void setLevel(int level) {
    this.level = level;
}

public boolean isExpand() {
    return isExpand;
}

public void setExpand(boolean expand) {
    isExpand = expand;
}

public List<Comment> getChildren() {
    return children;
}

public void setChildren(List<Comment> children) {
    this.children = children;
}

public String getContent() {
    return content;
}

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

public boolean isVisible() {
    return isVisible;
}

public void setVisible(boolean visible) {
    isVisible = visible;
}
}

伪造数据,此数据是嵌套的树状结构数据,显示时需要转换:

  private List<Comment> initComment() {
    List<Comment> comments1 = new ArrayList<>();

    List<Comment> comments2 = new ArrayList<>();
    Comment comment = new Comment();
    comment.setChildren(new ArrayList<Comment>());
    comment.setContent("可爱又迷人的反派角色");
    comments2.add(comment);

    List<Comment> comments3 = new ArrayList<>();
    Comment comment1 = new Comment();
    comment1.setChildren(comments2);
    comment1.setContent("贯彻爱与真实的邪恶");
    comments3.add(comment1);

    List<Comment> comments4 = new ArrayList<>();
    Comment comment2 = new Comment();
    comment2.setChildren(comments3);
    comment2.setContent("为了保护世界的和平");
    comments4.add(comment2);

    List<Comment> comments5 = new ArrayList<>();
    Comment comment3 = new Comment();
    comment3.setChildren(comments4);
    comment3.setContent("为了防止世界被破坏");
    comments5.add(comment3);

    Comment comment4 = new Comment();
    comment4.setChildren(new ArrayList<Comment>());
    comment4.setContent("武藏!小次郎!");
    comments5.add(comment4);

    Comment comment5 = new Comment();
    comment5.setChildren(new ArrayList<Comment>());
    comment5.setContent("喵!");
    comments5.add(comment5);

    Comment comment6 = new Comment();
    comment6.setChildren(comments5);
    comment6.setContent("我们就大发慈悲的回答你");
    comments1.add(comment6);

    return comments1;
}

转换数据,最重要的一步:

private void convertComment(List<Comment> comments, int level) {
    for (int i = 0; i < comments.size(); i++) {
        Comment comment = comments.get(i);
        //数据转换时,层数计算,默认为1
        comment.setLevel(level);
        comment.setVisible(true);
        mComments.add(comment);

        if (comment.getChildren() != null && comment.getChildren().size() > 0) {
            //当前节点的子节点,比当前节点层数+1
            convertComment(comment.getChildren(), level + 1);
        }
    }
}

构建ViewHolder,这里需要2类ViewHolder,一个是正常显示内容的VH,一类是隐藏内容的VH(当父节点收起时,子节点需要隐藏)

  • 正常显示内容的VH

    public class CommentVH extends RecyclerView.ViewHolder {
      private ImageView mCommentAnimteIv;
      private LinearLayout mCommentLl;
      private TextView mCommentDescTv;
    
      public CommentVH(View itemView) {
          super(itemView);
          mCommentAnimteIv = (ImageView) itemView.findViewById(R.id.comment_animate_iv);
          mCommentDescTv = (TextView) itemView.findViewById(R.id.comment_desc_tv);
      }
    
      public void update(Comment comment,
                     Context context) {
          mCommentDescTv.setText(comment.getContent());
          mCommentLl = (LinearLayout) itemView;
          if (mCommentLl.getChildCount() > 2) {
              mCommentLl.removeViews(0, mCommentLl.getChildCount() - 2);
          }
          //根据节点等级增加竖线
          if (comment.getLevel() > 1) {
              RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,  ViewGroup.LayoutParams.MATCH_PARENT);
              LayoutInflater inflater = LayoutInflater.from(context);
              for (int j = 1; j < comment.getLevel(); j++) {
                  View lineView = inflater.inflate(R.layout.line_view, null);
                  mCommentLl.addView(lineView, 0, layoutParams);
              }
          }
      }
      //评论缩放处理
      public void addZoomListener(final TreeOperationListener treeOperationListener, final int pos) {
        if (mCommentAnimteIv != null) {
            mCommentAnimteIv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    treeOperationListener.zoom(pos);
                }
            });
        }
      }
      //增加评论处理
       public void addAddListener(final TreeOperationListener treeOperationListener, final int pos) {
        if (mCommentLl != null) {
            mCommentLl.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    treeOperationListener.add(pos);
                }
            });
        }
      }
    }
    

基础布局:

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

      <LinearLayout
          android:layout_width="wrap_content"
          android:layout_height="match_parent"
          android:paddingTop="16dp"
          android:orientation="vertical">

          <ImageView
              android:id="@+id/comment_animate_iv"
              android:layout_width="12dp"
              android:layout_height="14dp"
              android:src="@drawable/comment_direction"/>

          <View
              android:id="@+id/comment_divider_view"
              android:layout_width="1px"
              android:layout_height="match_parent"
              android:layout_marginLeft="6dp"
              android:background="#cccccc"/>

    </LinearLayout>

    <RelativeLayout
        android:id="@+id/comment_desc_rl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:layout_marginLeft="6dp">

        <TextView
            android:id="@+id/comment_more_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:textColor="#a39b9b"
            android:visibility="gone"
            android:text="点击加载更多回复"/>

        <TextView
            android:id="@+id/comment_desc_tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:includeFontPadding="false"
            android:textSize="14sp"
            android:textColor="#272727"
            android:text="为了防止世界被破坏"/>

        <LinearLayout
            android:id="@+id/comment_info_ll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/comment_desc_tv"
            android:layout_marginTop="12dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/comment_nickname_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="12sp"
                android:textColor="#a39b9b"
                android:text="全麦面包不加糖·"/>

            <TextView
                android:id="@+id/comment_like_count_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="12sp"
                android:textColor="#a39b9b"
                android:text="100"/>

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:src="@drawable/dislike"/>
        </LinearLayout>

    </RelativeLayout>
  </LinearLayout>

竖线布局:

  <?xml version="1.0" encoding="utf-8"?>
  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content">

      <View
          android:layout_width="1px"
          android:layout_height="match_parent"
          android:layout_marginLeft="6dp"
          android:layout_marginRight="6dp"
          android:background="#cccccc"/>
  </FrameLayout>
  • 隐藏内容的VH

     public class NullVH extends RecyclerView.ViewHolder {
    
       public NullVH(View itemView) {
           super(itemView);
       }
     }
    

到这里,基本算开发完成了,还要更深入的开发,就是给评论增加收起、放大功能,评论增加、删除了,原理也同样是数据转换。
源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容