Android ListView列表優化的方法詳解

前言

列表 ListView 是應用中最為常見的組件,而列表往往也會承載很多元素,當元素多,尤其是那種圖片文件比較大的場合,就可能會導致列表卡頓,嚴重的時候可能導致應用崩潰。本篇來介紹如何優化列表。

優化點1:使用 builder構建列表

當你的列表元素是動態增長的時候(比如上拉加載更多),請不要直接用children 的方式,一直往children 的數組增加組件,那樣會很糟糕。

//糟糕的用法
ListView(
  children: [
    item1,
    item2,
    item3,
    ...
  ],
)

//正確的用法
ListView.builder(
  itemBuilder: (context, index) => ListItem(),
  itemCount: itemCount,
)

對於 ListView.builder 是按需構建列表元素,也就是隻有那些可見的元素才會調用itemBuilder 構建元素,這樣對於大列表而言性能開銷自然會小很多。

Creates a scrollable, linear array of widgets that are created on demand. This constructor is appropriate for list views with a large (or infinite) number of children because the builder is called only for those children that are actually visible.

優化點2:禁用 addAutomaticKeepAlives 和 addRepaintBoundaries 特性

這兩個屬性都是為瞭優化滾動過程中的用戶體驗的。addAutomaticKeepAlives 特性默認是 true,意思是在列表元素不可見後可以保持元素的狀態,從而在再次出現在屏幕的時候能夠快速構建。這其實是一個拿空間換時間的方法,會造成一定程度的內存開銷。可以設置為 false 關閉這一特性。缺點是滑動過快的時候可能會出現短暫的白屏(實際會很少發生)。

addRepaintBoundaries 是將列表元素使用一個重繪邊界(Repaint Boundary)包裹,從而使得滾動的時候可以避免重繪。而如果列表很容易繪制(列表元素佈局比較簡單的情況下)的時候,可以關閉這個特性來提高滾動的流暢度。

addAutomaticKeepAlives: false,
addRepaintBoundaries: false,

優化點3:盡可能將列表元素中不變的組件使用 const 修飾

使用 const 相當於將元素緩存起來實現共用,若列表元素某些部分一直保持不變,那麼可以使用 const 修飾。

return Padding(
  child: Row(
    children: [
      const ListImage(),
      const SizedBox(
        width: 5.0,
      ),
      Text('第$index 個元素'),
    ],
  ),
  padding: EdgeInsets.all(10.0),
);

優化點4:使用 itemExtent 確定列表元素滾動方向的尺寸

對於很多列表,我們在滾動方向上的尺寸是提前可以根據 UI設計稿知道的,如果能夠知道的話,那麼使用 itemExtent 屬性制定列表元素在滾動方向的尺寸,可以提升性能。這是因為,如果不指定的話,在滾動過程中,會需要推算每個元素在滾動方向的尺寸從而消耗計算資源。

itemExtent: 120,

優化實例

下面是一開始未改造的列表,嗯,可以認為是垃圾代碼

class LargeListView extends StatefulWidget {
  const LargeListView({Key? key}) : super(key: key);

  @override
  _LargeListViewState createState() => _LargeListViewState();
}

class _LargeListViewState extends State<LargeListView> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('大列表'),
        brightness: Brightness.dark,
      ),
      body: ListView(
        children: List.generate(
          1000,
          (index) => Padding(
            padding: EdgeInsets.all(10.0),
            child: Row(
              children: [
                Image.network(
                  'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7869eac08a7d4177b600dc7d64998204~tplv-k3u1fbpfcp-watermark.jpeg',
                  width: 200,
                ),
                const SizedBox(
                  width: 5.0,
                ),
                Text('第$index 個元素'),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

當然,實際不會是用 List.generate 來生成列表元素,但是也不要用一個 List<Widget> 列表對象一直往裡面加列表元素,然後把這個列表作為 ListView 的 children!改造後的代碼如下所示,因為將列表元素拆分得更細,代碼量是多一些,但是性能上會好很多。

import 'package:flutter/material.dart';

class LargeListView extends StatefulWidget {
  const LargeListView({Key? key}) : super(key: key);

  @override
  _LargeListViewState createState() => _LargeListViewState();
}

class _LargeListViewState extends State<LargeListView> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('大列表'),
        brightness: Brightness.dark,
      ),
      body: ListView.builder(
        itemBuilder: (context, index) => ListItem(
          index: index,
        ),
        itemCount: 1000,
        addAutomaticKeepAlives: false,
        addRepaintBoundaries: false,
        itemExtent: 120.0,
      ),
    );
  }
}

class ListItem extends StatelessWidget {
  final int index;
  ListItem({Key? key, required this.index}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      child: Row(
        children: [
          const ListImage(),
          const SizedBox(
            width: 5.0,
          ),
          Text('第$index 個元素'),
        ],
      ),
      padding: EdgeInsets.all(10.0),
    );
  }
}

class ListImage extends StatelessWidget {
  const ListImage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Image.network(
      'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7869eac08a7d4177b600dc7d64998204~tplv-k3u1fbpfcp-watermark.jpeg',
      width: 200,
    );
  }
}

總結

本篇介紹瞭 Flutter ListView 的4個優化要點,非常實用哦!實際上,這些要點都可以從官網的文檔裡找出對應的說明。因此,如果遇到瞭性能問題,除瞭搜索引擎外,也建議多看看官方的文檔。另外一個,對於列表圖片,有時候也需要前後端配合,比如目前的手機都是號稱1億像素的,如果上傳的時候直接上傳原圖,那麼加載如此大的圖片肯定是非常消耗資源的。對於這種情況,建議是生成列表縮略圖(可能需要針對不同屏幕尺寸生成不同的縮略圖)。

以上就是Android ListView列表優化的方法詳解的詳細內容,更多關於Android ListView列表優化的資料請關註WalkonNet其它相關文章!

推薦閱讀: