96SEO 2026-02-23 15:01 5
给产品估个两天时间#xff0c;摸一天半的鱼不过分吧#xff08;手动斜眼#xff09;

这种大家…先上个效果图没有UI将就看吧写代码的整个过程花了4个小时左右相比当初自己开发需求已经快了很多了哈。
默认展示一级评论和二级评论中的热评可以上拉加载更多。
二级评论超过两条时可以点击展开加载更多二级评论展开后可以点击收起折叠到初始状态。
回复评论后插入到该评论的下方。
为啥要只用一个RecyclerView最重要的原因就是在RecyclerView中嵌套同方向RecyclerView会有性能问题和滑动冲突。
其次当下声明式UI正是各方大佬推崇的最佳开发实践之一虽然我们没有使用声明式UI基础上开发的Compose/Flutter技术但其构建思想仍然对我们的开发具有一定的指导意义。
我猜测androidx.recyclerview.widget.ListAdapter可能也是响应声明式UI号召的一个针对RecyclerView的解决方案吧。
既然选用了ListAdapter那么我们就不应该再手动操作adapter的数据再用各种notifyXxx方法来更新列表了。
更提倡的做法是基于data
**用Collection操作符对数据源的进行转换然后将转换后的数据提交到adapter。
为了提高数据转换性能我们可以基于协程进行异步处理。
Kotlin中提供了大量非常好用的Collection操作符能灵活使用的话非常有利于咱们向声明式UI转型。
前面我提到了groupBy和flatMap这两个操作符。
怎么使用呢
以这个需求为例我们需要显示一级评论、二级评论和展开更多按钮想要分别用一个data
class来表示但是后端返回的数据中又没有“展开更多”这样的数据就可以这样处理
从后端获取的数据List包括有一级评论和二级评论二级评论的parentId就等于一级评论的id
CommentItem.Level2)?.parentId?:
IllegalArgumentException(invalid
展开前面的map展开时就可以在每级一级评论的二级评论后面添加一个控制“展开更多”的Itemit.value
前面我们描述的数据源的转换过程在Kotlin中可以简单地被抽象为一个操作
ListCommentItem对于这个需求数据源转换操作就包括了分页加载展开二级评论收起二级评论回复评论等。
按照惯例抽象一个接口出来。
既然我们要在协程框架下进行异步处理需要给这个操作加一个suspend关键字。
}为啥我给这个接口取名Reducer如果你知道它的意思说明你可能已经了解过MVI架构了如果你还不知道它的意思说明你可以去了解一下MVI了。
哈哈
不过今天不谈MVI对于这样一个小Demo完全没必要上架构。
但是优秀架构为我们提供的代码构建思路是有必要的
前面谈到异步我们印象中可能主要是网络请求、数据库/文件读写等IO操作。
Activity的startActivityForResult/onActivityResultDialog的拉起/回调其实也可以看着是异步操作。
异步与是否在主线程无关而在于是否是实时返回结果。
毕竟在主线程上跳转到其他页面获取数据再回调回去使用也是花了时间的啊。
所以在协程的框架下有一个更适合描述异步的词语挂起(suspend)。
说这有啥用呢仍以这个需求为例我们点击“回复”后拉起一个对话框输入评论确认后回调给Activity再进行网络请求
{super.onCreate(savedInstanceState)setContentView(R.layout.dialog_reply)val
findViewByIdEditText(R.id.content)findViewByIdButton(R.id.submit).setOnClickListener
(editText.text.toString().isBlank())
Toast.LENGTH_SHORT).show()returnsetOnClickListener}callback.invoke(editText.text.toString())dismiss()}}
由于整个转换过程是在IO线程进行Dialog相关操作需要转换到主线程操作suspendCoroutine
{continuation.resume(it)}.show()}}...进行其他操作如网络请求
}技术选型或者说技术框架咱们就实现了甚至还谈到了部分细节了。
接下来进行完整实现细节分享。
基于上一章节的技术选型咱们的MainActivity的完整代码就是这样了。
{super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val
findViewByIdRecyclerView(R.id.recyclerView)commentAdapter
{lifecycleScope.launchWhenResumed
{reduce.invoke(commentAdapter.currentList)}val
1commentAdapter.submitList(newList)
这里是为了处理submitList后列表滑动位置不对的问题if
{recyclerView.scrollToPosition(0)}
commentAdapter.currentList.indexOf(thisCommentAdapter.folding)recyclerView.scrollToPosition(index)}}}}recyclerView.adapter
}给RecyclerView设置一个CommentAdapter就行了回调时也只需要把回调过来的Reducer调度到IO线程跑一下得到新的数据list再submitList就完事了。
如果不是submitList后有列表的定位问题代码还能更精简。
如果有知道更好的解决办法的朋友麻烦留言分享一下感谢
Adapter和ViewHolder都是UI组件我们也需要尽量保持它们的清洁。
DiffUtil.ItemCallbackCommentItem()
{submitList(listOf(CommentItem.Loading(page
CommentItem.Loading.State.IDLE)))}override
LayoutInflater.from(parent.context)return
Level1VH(inflater.inflate(R.layout.item_comment_level_1,
Level2VH(inflater.inflate(R.layout.item_comment_level_2,
false),reduceBlock)TYPE_LOADING
LoadingVH(inflater.inflate(R.layout.item_comment_loading,parent,false),
FoldingVH(inflater.inflate(R.layout.item_comment_folding,
{holder.onBind(getItem(position))}override
}可以看到就是一个简单的多ItemType的Adapter唯一需要注意的就是在Activity里传入的reduceBlock:
itemView.findViewById(R.id.avatar)private
itemView.findViewById(R.id.username)private
itemView.findViewById(R.id.content)private
itemView.findViewById(R.id.reply)override
item.contentreply.setOnClickListener
{reduceBlock.invoke(ReplyReducer(item,
}也是很简单唯一特别一点的处理就是在onClickListener中让reduceBlock去invoke一个Reducer实现。
刚才在技术选型章节已经提前展示了“回复评论”这一操作的Reducer实现了其他Reducer也差不多比如展开评论操作也封装在一个Reducer实现ExpandReducer中以下是完整代码
FakeApi.getLevel2Comments(folding.parentId,
folding.pageSize).getOrNull()?.map(mapper::invoke)
emptyList()toMutableList().apply
CommentItem.Folding.State.LOADED_ALL
CommentItem.Folding.State.IDLEit.copy(page
list假数据通过mapper转换成显示用的Item数据list将Item数据插入到“展开更多”按钮前面最后根据二级评论加载是否完成将“展开更多”的状态置为IDLE或LOADED_ALL
entity.contentmakeHot()就是用buildSpannedString来实现的
}这里可以提一句尽量用CharSequence来抽象表示字符串可以方便我们灵活地使用Span来减少UI代码。
class也是可以抽象的。
但这边我处理不是很严谨比如CommentItem我把userId和userName也抽象出来了其实不应该抽象出来。
在基于Reducer的框架下最好是把data
数据驱动UI对业务的精准抽象对异步的延伸理解灵活使用Collection操作符没有UI和PM写代码真TM爽
Framework底层原理篇https://qr18.cn/AQpN4J
Jetpack全家桶篇内含Composehttps://qr18.cn/A0gajp
车载开发岗位面试习题https://qr18.cn/FTlyCJ
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback