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

reddit评论

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

树状列表

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

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

树状列表

大致原理如下:

第一层绘制

1.png

第二层绘制

2.png

第三层绘制

3.png

...

根据层数的不同在最左边添加竖线,以上的实现是行的通的,就是数据结构转换需要花点时间。

按理来说,事情到这已经结束了,但是...但是...通过测试,当加载数据有几百条的时候,会非常非常卡,会卡几秒才加载出来,这非常影响应用体验,抛弃之。

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

public class Comment {

/**

* 孩子数量

*/

private List 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 getChildren() {

return children;

}

public void setChildren(List 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 initComment() {

List comments1 = new ArrayList<>();

List comments2 = new ArrayList<>();

Comment comment = new Comment();

comment.setChildren(new ArrayList());

comment.setContent("可爱又迷人的反派角色");

comments2.add(comment);

List comments3 = new ArrayList<>();

Comment comment1 = new Comment();

comment1.setChildren(comments2);

comment1.setContent("贯彻爱与真实的邪恶");

comments3.add(comment1);

List comments4 = new ArrayList<>();

Comment comment2 = new Comment();

comment2.setChildren(comments3);

comment2.setContent("为了保护世界的和平");

comments4.add(comment2);

List comments5 = new ArrayList<>();

Comment comment3 = new Comment();

comment3.setChildren(comments4);

comment3.setContent("为了防止世界被破坏");

comments5.add(comment3);

Comment comment4 = new Comment();

comment4.setChildren(new ArrayList());

comment4.setContent("武藏!小次郎!");

comments5.add(comment4);

Comment comment5 = new Comment();

comment5.setChildren(new ArrayList());

comment5.setContent("喵!");

comments5.add(comment5);

Comment comment6 = new Comment();

comment6.setChildren(comments5);

comment6.setContent("我们就大发慈悲的回答你");

comments1.add(comment6);

return comments1;

}

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

private void convertComment(List 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);

}

});

}

}

}

基础布局:

xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:paddingTop="16dp"

android:orientation="vertical">

android:id="@+id/comment_animate_iv"

android:layout_width="12dp"

android:layout_height="14dp"

android:src="@drawable/comment_direction"/>

android:id="@+id/comment_divider_view"

android:layout_width="1px"

android:layout_height="match_parent"

android:layout_marginLeft="6dp"

android:background="#cccccc"/>

android:id="@+id/comment_desc_rl"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:paddingTop="16dp"

android:layout_marginLeft="6dp">

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="点击加载更多回复"/>

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="为了防止世界被破坏"/>

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">

android:id="@+id/comment_nickname_tv"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textSize="12sp"

android:textColor="#a39b9b"

android:text="全麦面包不加糖·"/>

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"/>

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="4dp"

android:src="@drawable/dislike"/>

竖线布局:

android:layout_width="wrap_content"

android:layout_height="wrap_content">

android:layout_width="1px"

android:layout_height="match_parent"

android:layout_marginLeft="6dp"

android:layout_marginRight="6dp"

android:background="#cccccc"/>

隐藏内容的VH

public class NullVH extends RecyclerView.ViewHolder {

public NullVH(View itemView) {

super(itemView);

}

}

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

源码

android开发评论列表,Android类Reddit循环评论列表开发相关推荐

  1. python 类中定义列表_Python-从类定义中的列表理解访问类变量

    小编典典 类范围和列表,集合或字典的理解以及生成器表达式不混合. 为什么:或者,官方用词 在Python 3中,为列表理解赋予了它们自己的适当范围(本地名称空间),以防止其局部变量渗入周围的范围内(即 ...

  2. Android开发系列(十二) QQ联系人列表升级版——ListView和ScrollView高阶使用方法...

    今天继续进行QQ界面的开发工作.前一段时间讲过ExpandableListView的使用并且设置了一个比较简单的具有子菜单效果的联系人列表,本节添加进ScrollView控件,对QQ2013版的联系人 ...

  3. Android实战开发-Kotlin教程(入门篇-RecyclerView数据列表的实现)

    在移动应用开发中,很多时候我们需要去展示数据记录,如电商应用中的商品列表.订单列表等等一系列的数据记录的展示时,我们需要用到Android的列表组件展示这些数据,列表组件应该是我们遇到到比较常见也是必 ...

  4. Android开发实战---一个汽车销售APP,有汽车列表页、汽车详情页、贷款计算页3个界面。

    Android开发实战设计并实现一个汽车销售APP,要求至少有汽车列表页.汽车详情页.贷款计算页3个界面. 已开源:https://github.com/yan123666/wlf 实现结果: 1.计 ...

  5. 移动开发技术【Android】:【part 2】——RecyclerView实现列表

    上一篇请参见:移动开发技术[安卓]--Android_Studio[Part 1] 在上一篇中,我们说到,如何初步的使用Android Studio ,同时完成了基础微信页面的模仿设计.同时我们上一篇 ...

  6. android开发之网络棋牌类在线游戏开发心得(服务器端、Java) 好文章值得收藏...

    标签: android服务器 2013-10-09 17:28 3618人阅读 评论(0) 收藏 举报 分类: android (11) 转自:http://blog.csdn.net/bromon/ ...

  7. android列表【android开发记录片】android下实现圆角列表布局控件

    每日一贴,明天的内容关键字为android列表 引子 明天闲来做了一个类似iphone的圆角列表,先看效果. 图片中绿色线条的是列表头文字,红色的是列表题名文字.此两处都可以显示/隐藏及动态改变值.对 ...

  8. Android之使用AlertDialog.Builder类创建带列表的对话框和带自己所布局视图的对话框

    1.首先,新建一个安卓项目,项目名称为DialogTest1,首先,进行界面布局,打开布局文件res下的layout文件夹下的activity_main.xml文件,附上activity_main的代 ...

  9. Android多媒体功能开发(11)——使用AudioRecord类录制音频

    AudioRecord类优点是能录制到缓冲区,能够实现边录边播(AudioRecord + AudioTrack)以及对音频的实时处理(如QQ电话).缺点是输出是PCM格式的原始采集数据,如果直接保存 ...

最新文章

  1. activiti集成spring
  2. python主题壁纸_Python教你如何下载你喜欢的桌面壁纸
  3. wordpress 新建php文件大小,WordPress最大上传文件大小限制修改
  4. 单向环形列表及应用场景(约瑟夫环)
  5. java jar包示例_Java包getImplementationTitle()方法和示例
  6. 当60亿次攻击来袭,人机联合打了一场漂亮的防御战
  7. 国资委发文!10本书讲透数字化时代新机遇
  8. A股开盘:深证区块链50指数涨0.18%,概念股涨多跌少
  9. 剑指offer之构建乘积数组
  10. 思必驰AI芯片发布:内置完整语音交互方案,支持离线模式,All in One
  11. python执行不了elif_浅谈对python中if、elif、else的误解
  12. 草根力量:同学聚会(嘉定二中校庆60周年)
  13. matlab——knnsearch用法介绍
  14. 换信科技B2B供应链平台“换易宝”1.0产品上线发布会盛大召开
  15. 服务器机柜设备信息卡,信息机房标识标准V.doc
  16. 缺少tlsys.conf文件
  17. 必备技能10:管理学类重要期刊名录 -- UTD24 和 FT50
  18. bilibili弹幕游戏
  19. 只用html+js+css实现2048小游戏(带源码)
  20. 听觉外围分析matlab,扫描附近的低功耗 Bluetooth 外围设备 - MATLAB blelist - MathWorks 中国...

热门文章

  1. 10种流行的机器学习算法进行泰坦尼克幸存者分析
  2. 如何利用EXCEL生成任意自由度任意显著因子的F分布表
  3. emqx使用自制CA证书登录配置(双向认证)
  4. EXCEL2010数据挖掘插件 下载地址
  5. mysql时间戳与PHP时间戳,php – 当mysql时间戳gt; 20分钟时删除时间戳比较
  6. python tell方法_Python File tell() 方法
  7. 前淘宝技术专家谈12306:这个网站很神奇
  8. zzulioj:1168: 账单(指针专题)
  9. 百度离线瓦片地图原理解析(附C#源码,可下载带样式地图)
  10. java 替换文件中的字符串