Android UI开发根本:RecyclerView与ListView的优化

[复制链接]
发表于 2025-10-20 04:46:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
RecyclerView与ListView的优化

一、根本概念对比

1.1 ListView与RecyclerView概述

ListView和RecyclerView都是Android中用于展示列表数据的告急控件,但RecyclerView是更当代化的办理方案,提供了更多的机动性和性能上风。
ListView特点



  • Android早期提供的列表控件
  • 利用简单,上手容易
  • 内置了常见的分割线、选择模式等功能
  • 性能优化相对有限
RecyclerView特点



  • Android Support Library(现AndroidX)提供的当代列表控件
  • 更加机动,支持多种结构方式
  • 欺凌利用ViewHolder模式
  • 提供了丰富的动画API
  • 通过LayoutManager实现差别的结构结果
1.2 根本利用对比

ListView根本利用

  1. // 定义Adapter
  2. class MyListAdapter(context: Context, data: List<String>) :
  3.     ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, data) {
  4.     // 可以重写getView方法进行优化
  5. }
  6. // 在Activity中使用
  7. val listView = findViewById<ListView>(R.id.list_view)
  8. val data = listOf("Item 1", "Item 2", "Item 3")
  9. listView.adapter = MyListAdapter(this, data)
复制代码
RecyclerView根本利用

  1. // 定义ViewHolder和Adapter
  2. class MyAdapter(private val data: List<String>) :
  3.     RecyclerView.Adapter<MyAdapter.ViewHolder>() {
  4.    
  5.     class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
  6.         val textView: TextView = view.findViewById(R.id.text_view)
  7.     }
  8.    
  9.     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
  10.         val view = LayoutInflater.from(parent.context)
  11.             .inflate(R.layout.item_view, parent, false)
  12.         return ViewHolder(view)
  13.     }
  14.    
  15.     override fun onBindViewHolder(holder: ViewHolder, position: Int) {
  16.         holder.textView.text = data[position]
  17.     }
  18.    
  19.     override fun getItemCount() = data.size
  20. }
  21. // 在Activity中使用
  22. val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
  23. recyclerView.layoutManager = LinearLayoutManager(this)
  24. val data = listOf("Item 1", "Item 2", "Item 3")
  25. recyclerView.adapter = MyAdapter(data)
复制代码
二、ListView优化本领

2.1 ViewHolder模式

在ListView中,ViewHolder模式不是欺凌的,但利用它可以显着进步性能:
  1. class OptimizedAdapter(context: Context, private val data: List<String>) :
  2.     ArrayAdapter<String>(context, R.layout.list_item, data) {
  3.    
  4.     private class ViewHolder {
  5.         var textView: TextView? = null
  6.     }
  7.    
  8.     override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
  9.         var view = convertView
  10.         val holder: ViewHolder
  11.         
  12.         if (view == null) {
  13.             view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
  14.             holder = ViewHolder()
  15.             holder.textView = view.findViewById(R.id.text_view)
  16.             view.tag = holder
  17.         } else {
  18.             holder = view.tag as ViewHolder
  19.         }
  20.         
  21.         holder.textView?.text = data[position]
  22.         return view!!
  23.     }
  24. }
复制代码
2.2 其他ListView优化本领


  • 公道设置ListView高度

    • 制止在ScrollView中嵌套ListView
    • 利用android:layout_height="wrap_content"时需审慎

  • 镌汰getView()中的复杂利用

    • 制止在getView()中举行耗时利用
    • 图片加载利用异步方式

  • 利用分页加载

    • 实现滑动到底部加载更多数据
    • 制止一次性加载大量数据

三、RecyclerView优化本领

3.1 结构优化


  • 利用DiffUtil举行高效更新
  1. class MyDiffCallback(private val oldList: List<String>, private val newList: List<String>) :
  2.     DiffUtil.Callback() {
  3.    
  4.     override fun getOldListSize() = oldList.size
  5.    
  6.     override fun getNewListSize() = newList.size
  7.    
  8.     override fun areItemsTheSame(oldPos: Int, newPos: Int) =
  9.         oldList[oldPos] == newList[newPos]
  10.    
  11.     override fun areContentsTheSame(oldPos: Int, newPos: Int) =
  12.         oldList[oldPos] == newList[newPos]
  13. }
  14. // 使用DiffUtil更新数据
  15. fun updateData(newData: List<String>) {
  16.     val diffResult = DiffUtil.calculateDiff(MyDiffCallback(data, newData))
  17.     data.clear()
  18.     data.addAll(newData)
  19.     diffResult.dispatchUpdatesTo(this)
  20. }
复制代码

  • 利用setHasFixedSize
  1. recyclerView.setHasFixedSize(true) // 当列表项大小固定时使用
复制代码

  • 利用RecycledViewPool共享ViewHolder
  1. // 在嵌套RecyclerView的场景中
  2. val viewPool = RecyclerView.RecycledViewPool()
  3. parentRecyclerView.setRecycledViewPool(viewPool)
复制代码
3.2 数据处置处罚优化


  • 异步加载数据
  1. CoroutineScope(Dispatchers.IO).launch {
  2.     val data = loadDataFromDatabase()
  3.     withContext(Dispatchers.Main) {
  4.         adapter.updateData(data)
  5.     }
  6. }
复制代码

  • 分页加载
利用Paging库实现高效分页:
  1. // 定义DataSource
  2. class MyDataSource : PageKeyedDataSource<Int, Item>() {
  3.     override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Item>) {
  4.         // 加载初始数据
  5.         val items = fetchDataFromNetwork(1, params.requestedLoadSize)
  6.         callback.onResult(items, null, 2)
  7.     }
  8.    
  9.     override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Item>) {
  10.         // 加载后续页面
  11.         val items = fetchDataFromNetwork(params.key, params.requestedLoadSize)
  12.         callback.onResult(items, params.key + 1)
  13.     }
  14.    
  15.     override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Item>) {
  16.         // 通常不需要实现
  17.     }
  18. }
复制代码
3.3 滑动优化


  • 预加载
  1. recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
  2.     override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
  3.         super.onScrolled(recyclerView, dx, dy)
  4.         val layoutManager = recyclerView.layoutManager as LinearLayoutManager
  5.         val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
  6.         val totalItemCount = layoutManager.itemCount
  7.         
  8.         // 当滑动到倒数第5个item时预加载下一页数据
  9.         if (lastVisibleItem >= totalItemCount - 5) {
  10.             loadMoreData()
  11.         }
  12.     }
  13. })
复制代码

  • 滑动时停息加载
  1. recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
  2.     override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
  3.         super.onScrollStateChanged(recyclerView, newState)
  4.         if (newState == RecyclerView.SCROLL_STATE_IDLE) {
  5.             // 滑动停止时加载图片
  6.             imageLoader.resumeLoading()
  7.         } else {
  8.             // 滑动时暂停图片加载
  9.             imageLoader.pauseLoading()
  10.         }
  11.     }
  12. })
复制代码
四、实战案例

4.1 多范例列表实现

  1. class MultiTypeAdapter(private val items: List<Any>) :
  2.     RecyclerView.Adapter<RecyclerView.ViewHolder>() {
  3.    
  4.     companion object {
  5.         const val TYPE_HEADER = 0
  6.         const val TYPE_NORMAL = 1
  7.     }
  8.    
  9.     override fun getItemViewType(position: Int): Int {
  10.         return if (position == 0) TYPE_HEADER else TYPE_NORMAL
  11.     }
  12.    
  13.     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
  14.         return when (viewType) {
  15.             TYPE_HEADER -> {
  16.                 val view = LayoutInflater.from(parent.context)
  17.                     .inflate(R.layout.item_header, parent, false)
  18.                 HeaderViewHolder(view)
  19.             }
  20.             else -> {
  21.                 val view = LayoutInflater.from(parent.context)
  22.                     .inflate(R.layout.item_normal, parent, false)
  23.                 NormalViewHolder(view)
  24.             }
  25.         }
  26.     }
  27.    
  28.     override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
  29.         when (holder) {
  30.             is HeaderViewHolder -> holder.bind(items[position] as Header)
  31.             is NormalViewHolder -> holder.bind(items[position] as NormalItem)
  32.         }
  33.     }
  34.    
  35.     override fun getItemCount() = items.size
  36.    
  37.     class HeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
  38.         fun bind(header: Header) {
  39.             // 绑定头部数据
  40.         }
  41.     }
  42.    
  43.     class NormalViewHolder(view: View) : RecyclerView.ViewHolder(view) {
  44.         fun bind(item: NormalItem) {
  45.             // 绑定普通项数据
  46.         }
  47.     }
  48. }
复制代码
4.2 网格结构与瀑布流实现

  1. // 网格布局
  2. val gridLayoutManager = GridLayoutManager(this, 3) // 3列网格
  3. recyclerView.layoutManager = gridLayoutManager
  4. // 设置跨列显示的项
  5. gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
  6.     override fun getSpanSize(position: Int): Int {
  7.         return if (adapter.getItemViewType(position) == MultiTypeAdapter.TYPE_HEADER) {
  8.             3 // 头部占据3列
  9.         } else {
  10.             1 // 普通项占据1列
  11.         }
  12.     }
  13. }
  14. // 瀑布流布局
  15. val staggeredGridLayoutManager =
  16.     StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) // 2列瀑布流
  17. recyclerView.layoutManager = staggeredGridLayoutManager
复制代码
五、性能监测与调优

5.1 性能监测工具


  • Systrace

    • 分析UI渲染性能
    • 检测帧率和卡顿题目

  • Android Profiler

    • 监控监控内存利用情况
    • 分析CPU利用率

  • 自界说性能监测
  1. class PerformanceMonitorAdapter<VH : RecyclerView.ViewHolder>(private val wrappedAdapter: RecyclerView.Adapter<VH>) :
  2.     RecyclerView.Adapter<VH>() {
  3.    
  4.     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
  5.         val startTime = System.nanoTime()
  6.         val holder = wrappedAdapter.onCreateViewHolder(parent, viewType)
  7.         val endTime = System.nanoTime()
  8.         Log.d("Performance", "onCreateViewHolder took ${(endTime - startTime) / 1000000}ms")
  9.         return holder
  10.     }
  11.    
  12.     override fun onBindViewHolder(holder: VH, position: Int) {
  13.         val startTime = System.nanoTime()
  14.         wrappedAdapter.onBindViewHolder(holder, position)
  15.         val endTime = System.nanoTime()
  16.         Log.d("Performance", "onBindViewHolder for position $position took ${(endTime - startTime) / 1000000}ms")
  17.     }
  18.    
  19.     override fun getItemCount() = wrappedAdapter.itemCount
  20. }
  21. // 使用方式
  22. recyclerView.adapter = PerformanceMonitorAdapter(myAdapter)
复制代码
5.2 常见性能题目及办理方案


  • 结构层级过深

    • 利用Hierarchy Viewer分析结构层级
    • 利用ConstraintLayout镌汰嵌套

  • 频仍创建对象

    • 利用对象池复用临时对象
    • 制止在onBindViewHolder中创建新对象

  • 主线程壅闭

    • 耗时利用放到背景线程
    • 利用协程或RxJava处置处罚异步任务

六、口试题分析

Q1:RecyclerView相比ListView有哪些上风?

答:RecyclerView相比ListView的重要上风:

  • 欺凌利用ViewHolder模式,进步性能
  • 通过LayoutManager支持多种结构方式(线性、网格、瀑布流)
  • 提供了ItemAnimator实现丰富的动画结果
  • 通过ItemDecoration自界说分割线和装饰
  • 支持局部革新,镌汰不须要的重绘
  • 提供了更好的变乱处置处罚机制
  • 与DiffUtil联合,高效处置处罚数据厘革
Q2:怎样优化RecyclerView的性能?

答:优化RecyclerView性能的重要方法:

  • 利用DiffUtil举行高效的数据更新
  • 对于固定巨细的列表,利用setHasFixedSize(true)
  • 复杂结构利用多级缓存和RecycledViewPool
  • 制止在onBindViewHolder中举行耗时利用
  • 利用异步加载和图片缓存
  • 实现分页加载,制止一次加载大量数据
  • 滑动时停息加载图片等耗时利用
  • 优化item结构,镌汰嵌套和太过绘制
Q3:ListView和RecyclerView的缓存机制有什么区别?

答:ListView和RecyclerView的缓存机制区别:

  • ListView的缓存机制:

    • 一级缓存:mActiveViews,屏幕内可见的View
    • 二级缓存:mScrapViews,脱离屏幕的View

  • RecyclerView的缓存机制:

    • 一级缓存:mAttachedScrap,屏幕内可见的ViewHolder
    • 二级缓存:mCachedViews,脱离屏幕的ViewHolder,默认巨细为2
    • 三级缓存:ViewCacheExtension,开发者自界说缓存
    • 四级缓存:RecycledViewPool,缓存差别范例的ViewHolder,可以在多个RecyclerView间共享

  • 重要区别:

    • RecyclerView缓存粒度更细,缓存条理更多
    • RecyclerView支持多个RecyclerView共享缓存池
    • RecyclerView缓存ViewHolder而不但是View
    • RecyclerView支持自界说缓存战略

七、开源项目实战

7.1 利用Epoxy实现复杂列表

Airbnb的Epoxy是一个用于构建复杂RecyclerView的库,它利用了类似于React的组件化头脑:
  1. // 定义Model
  2. @EpoxyModelClass
  3. abstract class HeaderModel : EpoxyModelWithHolder<HeaderHolder>() {
  4.     @EpoxyAttribute lateinit var title: String
  5.    
  6.     override fun getDefaultLayout() = R.layout.item_header
  7.    
  8.     override fun bind(holder: HeaderHolder) {
  9.         holder.titleView.text = title
  10.     }
  11. }
  12. // 定义Controller
  13. class MyEpoxyController : TypedEpoxyController<List<Item>>() {
  14.     override fun buildModels(data: List<Item>) {
  15.         // 添加头部
  16.         header {
  17.             id("header")
  18.             title("我的列表")
  19.         }
  20.         
  21.         // 添加列表项
  22.         data.forEach { item ->
  23.             itemModel {
  24.                 id(item.id)
  25.                 title(item.title)
  26.                 description(item.description)
  27.                 clickListener { _ -> onItemClicked(item) }
  28.             }
  29.         }
  30.     }
  31.    
  32.     private fun onItemClicked(item: Item) {
  33.         // 处理点击事件
  34.     }
  35. }
  36. // 在Activity中使用
  37. val controller = MyEpoxyController()
  38. val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
  39. recyclerView.adapter = controller.adapter
  40. // 更新数据
  41. controller.setData(items)
复制代码
项目所在:Epoxy
7.2 利用Paging3实现无穷滚动列表

Jetpack Paging3库提供了一种更当代化的分页加载方案:
  1. // 定义PagingSource
  2. class MyPagingSource(
  3.     private val api: MyApi
  4. ) : PagingSource<Int, Item>() {
  5.    
  6.     override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> {
  7.         return try {
  8.             val page = params.key ?: 1
  9.             val response = api.getItems(page, params.loadSize)
  10.             
  11.             LoadResult.Page(
  12.                 data = response.items,
  13.                 prevKey = if (page == 1) null else page - 1,
  14.                 nextKey = if (response.items.isEmpty()) null else page + 1
  15.             )
  16.         } catch (e: Exception) {
  17.             LoadResult.Error(e)
  18.         }
  19.     }
  20.    
  21.     override fun getRefreshKey(state: PagingState<Int, Item>): Int? {
  22.         return state.anchorPosition?.let { anchorPosition ->
  23.             state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
  24.                 ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
  25.         }
  26.     }
  27. }
  28. // 在ViewModel中使用
  29. class MyViewModel : ViewModel() {
  30.     val items = Pager(
  31.         config = PagingConfig(
  32.             pageSize = 20,
  33.             enablePlaceholders = false,
  34.             maxSize = 100
  35.         ),
  36.         pagingSourceFactory = { MyPagingSource(api) }
  37.     ).flow.cachedIn(viewModelScope)
  38. }
  39. // 在Activity中使用
  40. class MyActivity : AppCompatActivity() {
  41.     private val viewModel: MyViewModel by viewModels()
  42.     private val adapter = MyPagingAdapter()
  43.    
  44.     override fun onCreate(savedInstanceState: Bundle?) {
  45.         super.onCreate(savedInstanceState)
  46.         setContentView(R.layout.activity_my)
  47.         
  48.         val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
  49.         recyclerView.adapter = adapter
  50.         
  51.         lifecycleScope.launch {
  52.             viewModel.items.collectLatest { pagingData ->
  53.                 adapter.submitData(pagingData)
  54.             }
  55.         }
  56.     }
  57. }
复制代码
项目所在:Paging
总结

本文具体先容了ListView和RecyclerView的优化本领,从根本概念到高级应用,体系地解说了怎样进步列表性能和用户体验。RecyclerView作为Android当代列表控件,提供了更多的机动性和性能上风,是开发高性能列表界面的首选。
在实际开发中,应根据具体需求选择符合的优化战略,如利用DiffUtil举行高效更新、实现分页加载、优化结构结构等。同时,联合Epoxy、Paging等良好的开源库,可以更轻松地构建复杂且高性能的列表界面。
下一篇文章将先容Fragment的生命周期与利用,敬请等待!
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表