当前位置: 首页 > 科技观察

一个更好的UI更新套路

时间:2023-03-12 04:53:08 科技观察

前段时间写Windows桌面程序的时候领悟到这个道理。是这样的。有时候,我们需要创建一个适合业务的View,或者一个UI对象。例如,对于即时通讯软件的好友列表中的每个项目,该项目必须具有三个数据:头像、名称和简短描述项目。那么,我们的View对象就必须要有对应的三个方法来设置这三个属性,然后View在显示的时候,才会显示我们最新的数据。一开始,我总是这样写的。//下面是伪代码的形式,不严谨{mDesc.setText(desc);}}一开始,这样就OK了,看起来很好,没有问题。如果一个用户的数据发生变化,只更新某个数据。那么,现在是问题所在。这样写的问题在于,当state/data之间的相互依赖关系影响到UI的显示时,那么就会出现问题,代码就会混乱。现在我们有这样一个需求,如果描述(desc)为空,则显示头像,不为空则不显示头像。需求确实很奇怪,但是在实际工作中肯定会遇到类似的情况。所以,首先,让我们想当然地改代码。classUserItemViewextendsView{ImageViewmAvatar;TextViewmName;TextViewmDesc;StringmDescData;publicvoidsetAvatar(BitmapavatarImage){mAvatar.setImage(avatarImage);}publicvoidsetName(Stringname){mName.setText(name);}publicvoidsetDesc(Stringdesc){mDescData=desc;if(mDescData==null||mDescData.equals("")){mAvatar.setVisible(true);}else{mAvatar.setVisible(false);}mDesc.setText(mDescData);}}看,现在我们需要设置描述方法不管头像的显示,这都恶心。如果数据项和状态多了,UI控件多了,它们之间的依赖关系也多了,这样写的话,你每一个方法里的逻辑都会变得很恶心很复杂。即使设置某个UI的状态也需要依赖于其他数据项。你没办法,只好把不相关的数据作为参数传入,就会变成这样。//!!!爆炸!!!为什么分身会关注其他数据!!!publicvoidsetAvatar(BitmapavatarImage,Stringdesc){mAvatar.setImage(avatarImage);if(desc==null||desc.equals("")){mAvatar.setVisible(true);}else{mAvatar.setVisible(false);}那时,我的UI已经变得很恶心了。在这样的情况下,我终于意识到,UI对象的更新是不能就地进行的。UI对象的更新应该统一进行,而那些会改变UI显示的setXXXX方法只做了两件事,一是给这个对象的成员属性设置数据,二是调用更新UI的统一方法。其实一个标准的设计一直就在我眼前,直到那一刻我才意识到并真正理解它。那就是Android中的View。Android中每个View的子类都有超多的set方法,比如TextView就有setText、setTextColor等。就是每一个set方法,其实就是对这个对象做了一个数据的改变,然后忽略掉。当系统调用OnDraw方法时,UI统一在OnDraw方法中更新。然后就很容易了。我们的代码更改为:.equals("")){mAvatar.setVisible(true);}else{mAvatar.setVisible(false);}mDesc.setText(mDescData);mName.setText(mNameData);}publicvoidsetAvatar(BitmapavatarImage){mAvatarData=avatarImage;updateView();}publicvoidsetName(Stringname){mNameData=name;updateView();}publicvoidsetDesc(Stringdesc){mDescData=desc;updateView();}}注意新增了一个updateView方法,专门用于更新数据到UI,这样写,其他set方法始终只保存数据,updateView方法根据当前数据状态更新UI,这样set方法干净整洁。而且再复杂的逻辑也不要怕显示逻辑,在updateView里做就好了。我曾经和一直在维护的一个Activity大约有15+个View,20-30个数据和状态,而这些数据和状态会神秘地打动这些视图的展示。那时年少无知,只是跟着数据变化(下一行),立即更新UI的状态。有以下几个位置:点击事件系统回调网络请求回调定时器,handler,这么多地方都在更新数据,改UI,维护简直狗屁。后来我实现了上面的技巧后,重构了一波,里面有超大的updateView方法,然后更新各个View。这里还有一个小技巧,就是如果你有1w个视图和1w个状态,你可以按状态写,也可以按视图写。意思是:按照正常人的思维,比如你的页面有两种模式,大部分人会写if(mode==A){viewA.xxxxxxviewB.xxxxxx...}elseif(mode==B){viewA.xxxxxxviewB.xxxxxx...}这么写又有问题了,小子,你以为你只有两种状态吗?你认为每个UI对象只依赖于一个状态吗?会不会优雅到恰好分布在if和else之间?你错了,要求这个东西是非常恶心和完全不合逻辑的。这样写,当你有很多状态的时候,你很难安排在哪里更新视图,如果有新的状态,就会影响它。为了不影响之前的逻辑,往往后面就加一行,多了不好维护。怎么做?最简单的方法就是按照View分类写。你有10,000个视图,所以让我们一个一个地处理它们,首先处理第一个视图。第一个视图如何受到影响?都写出来吧,不管是if-else什么的,反正最好在这几行代码的开头,先获取View1。然后再写View2和View3的逻辑。这样做唯一的缺点就是你的判断要重复写。看起来很繁琐,view1和view2的显示逻辑可能是一样的,你很想把它们写在一个if-else中,但我建议你不要这样做,为了最好的灵活性,请把它们分开写。以后维护的时候,如果是哪个View有问题,只要找到那个疙瘩就行了,别的什么都不用管。