最佳的UI更新套路

以前有段時間寫Windows桌面程序悟出的道理。java

是這樣的,有時候,咱們須要建立一個符合業務的View,或者稱爲UI對象,好比,即時通信軟件的好友列表裏面的每一個item,那麼這個item要有頭像,名字,簡短描述三個數據項。那麼,咱們這個View對象,就得有三個對應的方法來設置這三個屬性,而後View顯示的時候,就顯示出咱們最新的數據就行了。網絡

最最最開始的時候,我都是耿直的寫成這樣。spa

// 如下是僞代碼的形式,並不嚴謹
class UserItemView extends View {
    ImageView mAvatar;
    TextView mName;
    TextView mDesc;
    
    public void setAvatar(Bitmap avatarImage) {
        mAvatar.setImage(avatarImage);
    }
    
    public void setName(String name) {
        mName.setText(name);
    }
    
    public void setDesc(String desc) {
        mDesc.setText(desc);
    }
}

在最開始的時候,這樣是OK的,看起來很是完美,沒有任何問題。若是某個用戶的數據變了,就更新某個數據就行了。設計

好,如今問題了,這樣寫的問題在於,當狀態/數據之間相互依賴對UI的顯示產生影響時,那麼就會出問題,代碼會混亂。code

咱們如今提個這樣的需求,若是描述(desc)是空的,就顯示出頭像,若是不是空的,就不顯示頭像
需求確實很奇怪,可是實際的工做中必定會遇到相似的狀況。對象

那麼,首先咱們想固然的改動一下代碼吧。事件

class UserItemView extends View {
    ImageView mAvatar;
    TextView mName;
    TextView mDesc;
    
    String mDescData;
    
    public void setAvatar(Bitmap avatarImage) {
        mAvatar.setImage(avatarImage);
    }
    
    public void setName(String name) {
        mName.setText(name);
    }
    
    public void setDesc(String desc) {
        mDescData = desc;
        if (mDescData == null || mDescData .equals("")) {
            mAvatar.setVisible(true);
        } else {
            mAvatar.setVisible(false);
        }
        mDesc.setText(mDescData );
    }
}

你看,如今設置描述的方法裏要去管頭像的顯示狀況,這就很噁心了。若是有更多的數據項和狀態,更多的UI控件,他們之間有很是多的依賴關係,若是按這樣的寫法,你的每一個方法裏面的邏輯都會變得很是噁心,複雜。甚至,設置某個UI的狀態還須要依賴其餘的數據項,你沒辦法,只能把無關的數據當參數傳入,就會變成這樣。圖片

// !!!爆炸!!!爲何頭像要關注其餘的數據!!!
public void setAvatar(Bitmap avatarImage, String desc) {
    mAvatar.setImage(avatarImage);
    if (desc== null || desc.equals("")) {
        mAvatar.setVisible(true);
    } else {
        mAvatar.setVisible(false);
    }
}

當時,我那個UI已經變得很是噁心了,在這樣的狀況下,我終於意識到,對UI對象的更新,不能就地去作,UI對象的更新,應該用一個統一的方法來作,而會改變UI顯示狀況的那些setXXXX方法,只作兩件事,一是把數據設到這個對象的成員屬性上,另外一件事就是調用統一的方法來更新UI。it

其實一個標準的設計一直在眼前,直到那一刻,我才意識到和真正的理解。那就是Android中的View。Android中的每一個View的子類,都有超級多的set方法,好比TextView,就有setTextsetTextColor等等。它就是每一個set方法,其實是給這個對象作一個數據上的變化,而後就無論了。等到系統來調用OnDraw方法的時候,在OnDraw方法中統一的來更新UI。class

接下來就簡單了。咱們的代碼改爲這樣:

class UserItemView extends View {
    ImageView mAvatar;
    TextView mName;
    TextView mDesc;
    
    String mNameData;
    String mDescData;
    Bitmap mAvatarData;
    
    public void updateView() {
        mAvatar.setImage(mAvatarData);
        if (mDescData== null || mDescData.equals("")) {
            mAvatar.setVisible(true);
        } else {
            mAvatar.setVisible(false);
        }
        
        mDesc.setText(mDescData);
        
        mName.setText(mNameData);
    }
    
    public void setAvatar(Bitmap avatarImage) {
        mAvatarData = avatarImage;
        updateView();
    }
    
    public void setName(String name) {
        mNameData = name;
        updateView();
    }
    
    public void setDesc(String desc) {
        mDescData = desc;
        updateView();
    }
}

注意到,添加了一個updateView方法,這個方法專門用來將數據更新到UI上,這樣寫,其餘set方法一概只作把數據存進來的事情,updateView方法專門根據當前的數據狀態更新UI,這樣set方法就乾淨整潔。而邏輯再複雜的顯示邏輯,都不用怕,在updateView裏面搞就好了。

我曾經而且一直在維護的一個Activity,它大概有15+個View,20-30個數據和狀態,這些數據和狀態會謎通常的印象着這些view的顯示。當時年輕不懂事,就耿直的在數據變化的後面(下一行),立馬就更新UI的狀態。有以下這些位置吧:

  • 點擊事件

  • 系統回調

  • 網絡請求回調

  • 定時器,handler

這麼多地方都在更新數據,而且改變UI,維護起來簡直要屎。

後來,在我領悟到上面這個技巧後,我重構了一波,裏面有個超級大的updateView方法,而後對每一個View進行更新。

這裏有另外一個小技巧,就是你有1w個View,和1w個狀態,你按狀態分類寫,仍是按View分類寫。這個意思就是:按正常人思惟吧,好比你的頁面有兩種模式,通常人就會寫

if (mode == A) {
    viewA.xxxxxx
    viewB.xxxxxx
    ...
} else if (mode == B) {
    viewA.xxxxxx
    viewB.xxxxxx
    ...
}

這樣寫又出問題了,少年,你覺得你的狀態只有兩種嗎?你覺得每一個UI對象,就只依賴一個狀態嗎?會這麼優雅的恰好分佈在if和else裏面嗎?你錯了,需求這個東西啊,是很是噁心的,徹底不符合邏輯的。

這樣寫呢,當你狀態不少不少的時候,你很難去安排這個view的更新放哪裏,若是有新加的狀態又影響到它。爲了避免影響到以前的邏輯,你每每就是再後面再加上一行,這樣多了也是難以維護的。

那樣怎麼作呢?最簡單,按View來分類寫。你有1w個View吧,那就挨個挨個來,先把第一個View處理好了,第一個View受那些影響呢?所有寫出來,if-else也好,什麼都好,反正最開始這幾行代碼,先把View1搞定。

而後再一次寫View2,View3的邏輯,這樣惟一的缺點就是,你的判斷得重複寫。看上去很累贅,view1和view2,極可能他們的顯示邏輯是相同的,你很是想把他們寫在一個if-else裏,可是我建議你不要這麼作,爲了最高的變通性,請將他們分開寫。

當你往後維護的時候,哪一個View出問題了,你只要找到那一坨就行了,別的都不須要管。

祝各位工做愉快!

圖片描述

相關文章
相關標籤/搜索