1. 程式人生 > >Android 列表(ListView、RecyclerView)不斷重新整理最佳實踐

Android 列表(ListView、RecyclerView)不斷重新整理最佳實踐

本文微信公眾號「AndroidTraveler」首發。

背景

在 Android 列表開發過程中,有時候我們的 Item 會有一些元件,比如倒計時。這類元件要求不斷重新整理,這個時候由於列表複用的機制,因此會有一些坑。那麼我們本篇文章就給大家講兩個主題。

第一個是列表複用是否一定有問題。
第二個是出現問題有哪些解決方案可供我們選擇。

小 Demo

由於我們的主題重點是為了解決不斷重新整理問題,因此關於 RecyclerView 的基本使用就不再贅述,不清楚的小夥伴可以看下我之前的文章:
RecyclerView基本使用

首先我們看下效果圖:

很簡單,就是一個 RecyclerView 列表,列表項有兩個元件。分別代表第幾項和剩餘秒數。

這裡就是通過倒計時來演示重新整理可能存在的問題。

重點程式碼是 Adapter 裡面的顯示邏輯,初始為:

@Override
public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {
    holder.mTvNum.setText(String.valueOf(position + 1));
    updateTime(holder, itemList.get(position));
}

private void updateTime(final RecyclerViewViewHolder holder, final long time) {
    String content;
    long remainTime = time - System.currentTimeMillis();
    remainTime /= 1000;
    if (remainTime <= 0) {
        content = "Time up";
        holder.mTxtTitle.setText(content);
        return;
    }

    content = "剩下"+remainTime+"秒";
    holder.mTxtTitle.setText(content);
}

全部程式碼見:https://github.com/nesger/RecyclerView/tree/feature/refresh

接下來我們增加重新整理方法,有很多種,我們一一說明。

1. 使用 handler 來實現倒計時重新整理

修改顯示程式碼,如下:

@Override
public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {
    holder.mTvNum.setText(String.valueOf(position + 1));
    updateTime(holder, itemList.get(position));
}

private void updateTime(final RecyclerViewViewHolder holder, final long time) {
    String content;
    long remainTime = time - System.currentTimeMillis();
    remainTime /= 1000;
    if (remainTime <= 0) {
        content = "Time up";
        holder.mTxtTitle.setText(content);
        return;
    }

    content = "剩下"+remainTime+"秒";
    holder.mTxtTitle.setText(content);
    holder.mTxtTitle.postDelayed(new Runnable() {
        @Override
        public void run() {
            updateTime(holder, time);
        }
    }, 1000);
}

可以看到通過 handler 延時一秒,然後每次更新時間也是減少一秒。

我們看下效果圖:

可以看到沒滾動之前還好,滾動之後會發現,倒計時都亂了。

當然有時候可能不會暴露出來,比如滾動數目少,或者只有部分元件有倒計時,不像我們這個例子,所有專案都有倒計時,但是這也間接留下了可能的坑。

出現這個問題的原因在於元件的複用,如果你用 ListView 演示,並且不用複用,那麼是不會錯亂的。

當然列表不復用這個肯定是不推薦的。

因此,該方式不推薦。

全部程式碼見:https://github.com/nesger/RecyclerView/tree/feature/refresh_1

2. 使用 Timer 來實現倒計時重新整理

@Override
public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {
    holder.mTvNum.setText(String.valueOf(position + 1));
    updateTime(holder, itemList.get(position));
}

private void updateTime(final RecyclerViewViewHolder holder, final long time) {
    String content;
    long remainTime = time - System.currentTimeMillis();
    remainTime /= 1000;
    if (remainTime <= 0) {
        content = "Time up";
        holder.mTxtTitle.setText(content);
        return;
    }

    content = "剩下"+remainTime+"秒";
    holder.mTxtTitle.setText(content);
}

一樣不行,不推薦。

全部程式碼見:https://github.com/nesger/RecyclerView/tree/feature/refresh_2

3. 使用 Timer + View 集合

其實我們簡單分析一下就知道,出現上面錯亂情況的原因大致是兩個:一個是複用,一個是程式碼多次呼叫。
所以如果能夠解決這兩個問題,那麼這個問題就解決了。

因為我們這裡的業務是倒計時監聽,所有 View 都是一樣的,就是一秒更新一次。

所以我們的定時器不需要 N 個,只需要一個,在建構函式初始化即可。

另外為了避免複用和程式碼多次呼叫問題,我們將 View 通過一個集合儲存起來。

最後修改的程式碼如下:

private Timer mTimer;
private Set<RecyclerViewViewHolder> mHolders;

public RecyclerViewAdapter(Activity activity, List<Long> itemList) {
    if (activity == null || itemList == null) {
        throw new IllegalArgumentException("params can't be null");
    }
    this.activity = activity;
    this.itemList = itemList;
    mHolders = new HashSet<>();
    mTimer = new Timer();
    mTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            for (RecyclerViewViewHolder holder : mHolders) {
                updateTime(holder, holder.getTime());
            }
        }
    }, 0, 1000);
}

@Override
public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {
    holder.setTime(itemList.get(position));
    mHolders.add(holder);
    holder.mTvNum.setText(String.valueOf(position + 1));
    updateTime(holder, itemList.get(position));
}

效果圖如下:

可以看到沒問題了。

當然這裡有些優化還沒處理,因為本篇主要是思路分析,這裡就不添加了。

待優化點:定時器的啟動和關閉跟生命週期關聯,無資料來源不啟用定時器等。

全部程式碼見:https://github.com/nesger/RecyclerView/tree/feature/refresh_3

該方法來自與一名朋友的分享。

4. 使用 ScheduledExecutorService + View 集合

這邊 AndroidStudio 有安裝阿里巴巴提供的一個程式碼檢測外掛,連結為:https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines

在 AndroidStudio 輸入外掛名字 Alibaba Java Coding Guidelines 查詢安裝即可。

在方法 3 使用 Timer 時提示下面資訊:

Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions. 
            
//org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
    new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
executorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        //do something
    }
},initialDelay,period, TimeUnit.HOURS);

所以我們這裡修改 Timer 為 ScheduledExecutorService:

private ScheduledExecutorService mExecutorService;

public RecyclerViewAdapter(Activity activity, List<Long> itemList) {
    if (activity == null || itemList == null) {
        throw new IllegalArgumentException("params can't be null");
    }
    this.activity = activity;
    this.itemList = itemList;
    mHolders = new HashSet<>();
    mExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
        @Override
        public Thread newThread(@NonNull Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("countdown");
            return thread;
        }
    });
    mExecutorService.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            for (RecyclerViewViewHolder holder : mHolders) {
                updateTime(holder, holder.getTime());
            }
        }
    }, 0, 1000, TimeUnit.MILLISECONDS);
}

全部程式碼見:https://github.com/nesger/RecyclerView/tree/feature/refresh_4

有更多方法歡迎到上面的 GitHub 連結提 PR,可以基於 feature/refresh 分支新建分支。

有另外一位朋友提出了自定義 View 的處理方式,將倒計時的功能放到 View 裡面去處理,這個感興趣的小夥伴可以實現然後提 PR 哈,這裡提供額外一種思路。

相關推薦

Android 列表(ListViewRecyclerView)不斷重新整理最佳實踐

本文微信公眾號「AndroidTraveler」首發。 背景 在 Android 列表開發過程中,有時候我們的 Item 會有一些元件,比如倒計時。這類元件要求不斷重新整理,這個時候由於列表複用的機制,因此會有一些坑。那麼我們本篇文章就給大家講兩個主題。 第一個是列表複用是否一定有問題。 第二個是出現問題有哪

AndroidListViewRecyclerViewScrollView裡巢狀ListView 相對優雅的解決方案:NestFullListView

一 背景概述: ScrollView裡巢狀ListView,一直是Android開發者(反正至少是我們組)最討厭的設計之一,完美打破ListView(RecyclerView)的複用機制,成功的將Native頁面變成一個又臭又長的H5網頁效果,但由於這種設計需

Recyclerview實現列表分組下拉重新整理以及上拉載入--原始碼

這裡放上之前整理的一篇文章的完整原始碼,因為是從專案中抽取出來的,也沒有單獨再寫一個demo,希望需要的小夥伴能結合之前寫的一篇文章,理解實現原理,而不要簡單的想要直接copy拿來用,這裡是博文地址: import android.content.Con

Recyclerview實現列表分組下拉重新整理以及上拉載入更多

為什麼要使用Recyclerview替代ListView等傳統元件 目前的專案中已經逐步拋棄了ListView、GridView以及各種自定義的瀑布流效果的第三方庫了,統一用強大的替代者Recyclerview來實現。一個Recyclerview就解決了上面三

Android 仿QQ側滑刪除—一個滿足ListViewRecyclerView以及其他View通用的側滑刪除

對於側滑刪除已經是見慣不慣的了,我也一直有寫類似QQ那樣的側滑刪除控制元件的想法,雖然研究一段時間的自定義View,然對自定義ViewGroup實戰還是較少,並且側滑刪除還要考慮大量的事件分發機制,比如如何處理子控制元件與父控制元件之間的滑動衝突以及一系列的do

android_NestedScrollView與listViewrecyclerView滑動衝突

效果圖 與recyclerView gradle: compile 'com.android.support:design:26.0.0-alpha1'//材料設計語言 compile 'com.jakewharton:butterknife:8.5.1'//butterknife

AndroidListViewGridView的通用適配封裝簡化程式碼

轉載請註明出處:http://blog.csdn.net/u013038616/article/details/50733935 ListView和GridView是我們平時經常用來展示集合資料,每次都要為每種列表建一個專門的適配,雖然建立介面卡灰常簡單,但是每次都會有很多類似的程式碼,作

android技術-----ListViewRecyclerView

一、RecyclerView在很多方面能取代ListView,Google為什麼沒把ListView劃上一條過時的橫線? ListView採用的是RecyclerBin的回收機制在一些輕量級的List顯示時效率更高。 RecyclerView 與 ListView 的主要區別: 1、佈局效果對比

ScrollView巢狀ListViewRecyclerView,使其高度自適應

1、針對ScrollView巢狀ListView時只顯示第一個item高度的bug,可在activity裡動態修改ListView的高度(即計算每個item和分割線的高度後進行相加得到總高度),在setAdapter之後呼叫下面這個函式即可。 值得注意的是,此時ListVi

Android PullToRefresh ListView GridView 下拉重新整理 使用詳解

                轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/38238749,本文出自:【張鴻洋的部落格】群裡一哥們今天聊天偶然提到這個git hub上的控制元件:pull-to-refresh ,有興趣的看下,例子中的功

Android實現CoordinatorLayoutRecyclerView返回頂部效果

一、CoordinatorLayout返回頂部: CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.L

ListViewRecyclerView自動跳到頁面頂部或者中間的問題

scrollview下的第一個子控制元件里加上如下兩句即可解決此問題。  android:focusable="true"  android:focusableInTouchMode="true"  更新: 最近發現很多機型,使用上面的程式碼不生效。後來發現用 and

ListviewRecyclerView中的item中包含可以點選的控制元件,點選衝突解決辦法

在item的根佈局中新增: android:descendantFocusability="blocksDescendants" 可點選的子item新增 android:focusable="f

AndroidListViewRecyclerView的基本用法

在Android 5.0 版本之前,為了方便的顯示多行資料,形如QQ聊天資訊主介面,最常用的選擇無非是ListView控制元件,但是ListView控制元件本身就有很大的侷限性和效率問題(相對於RecyclerView控制元件)來說,比如說ListView只能豎

Android列表ListView的分割線新增邊距

ListView列表我們新增分割線一般如下: <ListView android:id="@+id/taskList1" android:layout_width="match_parent" android:layou

Android車輛運動軌跡大資料採集最佳實踐

github原始碼下載地址:https://github.com/geduo83/TrackDataCollect csdn原始碼下載地址:https://download.csdn.net/download/geduo_83/10841480 前言:   &nb

Android 6.0 執行時許可權管理最佳實踐

這是一篇遲來的部落格,Android M已經發布一年多了(6.0的變化),在Android M中許可權系統被重新設計,發生了顛覆性的變化,很多人把握不好這個變化,一是對這個許可權策略和套路還沒有摸透,二是沒有一個很好的實踐來支撐,在我的技術開發群裡很多人問我

androidListView實現資料列表展示

基於上一篇第八節的資料庫操作為基礎,對資料庫中的內容在android介面上進行列表展示 1、工程結構: 列表顯示示意圖: 列表顯示效果圖: 2、介面的列表展示配置檔案 item.xml: <?xml version="1.0" encoding="utf-8"

androidlistview分頁載入上拉重新整理更新listview

==========系統方法實現上啦重新整理=========public class GreatToolsActivity extends Activity { private TextView tv; private ListView listtest;

android 自定義ListView實現下拉重新整理分頁載入點選事件——自定義控制元件學習(七)

package com.example.administrator.customerpulldownrefreshandpageload; import android.content.Context; import android.os.Handler; import android.os.Message