Better UI update routine

Better UI update routine

[[174789]]

This is a lesson I learned while writing Windows desktop programs.

Sometimes, we need to create a business-compliant View, or UI object. For example, each item in the friend list of an instant messaging software must have three data items: avatar, name, and brief description. Then, our View object must have three corresponding methods to set these three properties, and then when the View is displayed, it will display our required data.

At the very beginning, I wrote like this honestly.

  1. // The following is a pseudo-code, not rigorous
  2. class UserItemView extends View {
  3. ImageView mAvatar;
  4. TextView mName;
  5. TextView mDesc;
  6.      
  7. public void setAvatar(Bitmap avatarImage) {
  8. mAvatar.setImage(avatarImage);
  9. }
  10.      
  11. public void setName(String name ) {
  12. mName.setText( name );
  13. }
  14.      
  15. public void setDesc(String desc ) {
  16. mDesc.setText( desc );
  17. }
  18. }

At the beginning, this is OK, it looks very safe, and there is no problem. If a user's data changes, just update the data.

Okay, now here comes the problem. The problem with writing like this is that when the states/data depend on each other and affect the display of the UI, problems will arise and the code will be messy.

We now propose a requirement that if the description (desc) is empty, the avatar will be displayed, and if it is not empty, the avatar will not be displayed.

The requirement is indeed strange, but similar situations will definitely be encountered in actual work.

Well, first of all, let's change the code as a matter of course.

  1. class UserItemView extends View {
  2. ImageView mAvatar;
  3. TextView mName;
  4. TextView mDesc;
  5.      
  6. String mDescData;
  7.      
  8. public void setAvatar(Bitmap avatarImage) {
  9. mAvatar.setImage(avatarImage);
  10. }
  11.      
  12. public void setName(String name ) {
  13. mName.setText( name );
  14. }
  15.      
  16. public void setDesc(String desc ) {
  17. mDescData = desc ;
  18. if (mDescData == null || mDescData .equals( "" )) {
  19. mAvatar.setVisible( true );
  20. } else {
  21. mAvatar.setVisible( false );
  22. }
  23. mDesc.setText(mDescData);
  24. }
  25. }

You see, now the description setting method has to take care of the avatar display, which is disgusting. If there are more data items and states, more UI controls, and there are many dependencies between them, if you write it this way, the logic in each of your methods will become disgusting and complicated. Even setting the state of a certain UI needs to depend on other data items. You have no choice but to pass in irrelevant data as parameters, and it will become like this.

  1. // !!! Explosion!!! Why should the avatar pay attention to other data!!!
  2. public void setAvatar(Bitmap avatarImage, String desc ) {
  3. mAvatar.setImage(avatarImage);
  4. if ( desc == null || desc .equals( "" )) {
  5. mAvatar.setVisible( true );
  6. } else {
  7. mAvatar.setVisible( false );
  8. }
  9. }

At that time, my UI had become very disgusting. Under such circumstances, I finally realized that the update of UI objects cannot be done on the spot. The update of UI objects should be done in a unified way. The setXXXX methods that will change the UI display only do two things. One is to set the data to the member properties of this object, and the other is to call a unified method to update the UI.

In fact, a standard design has always been in front of me, but I didn't realize and truly understand it until that moment. That is the View in Android. Each subclass of View in Android has a lot of set methods. For example, TextView has setText, setTextColor, etc. It is that each set method actually makes a data change to this object and then ignores it. When the system calls the OnDraw method, the UI is updated uniformly in the OnDraw method.

The next step is simple. Our code becomes like this:

  1. class UserItemView extends View {
  2. ImageView mAvatar;
  3. TextView mName;
  4. TextView mDesc;
  5.      
  6. String mNameData;
  7. String mDescData;
  8. Bitmap mAvatarData;
  9.      
  10. public void updateView() {
  11. mAvatar.setImage(mAvatarData);
  12. if (mDescData== null || mDescData.equals( "" )) {
  13. mAvatar.setVisible( true );
  14. } else {
  15. mAvatar.setVisible( false );
  16. }
  17.          
  18. mDesc.setText(mDescData);
  19.          
  20. mName.setText(mNameData);
  21. }
  22.      
  23. public void setAvatar(Bitmap avatarImage) {
  24. mAvatarData = avatarImage;
  25. updateView();
  26. }
  27.      
  28. public void setName(String name ) {
  29. mNameData = name ;
  30. updateView();
  31. }
  32.      
  33. public void setDesc(String desc ) {
  34. mDescData = desc ;
  35. updateView();
  36. }
  37. }

Notice that an updateView method is added, which is used to update the data to the UI. In this way, other set methods only store data, and the updateView method updates the UI according to the current data status, so the set method is clean and tidy. No matter how complex the display logic is, you don't have to worry about it, just do it in updateView.

I have been maintaining an Activity that has about 15+ Views, 20-30 data and states. These data and states mysteriously affect the display of these views. I was young and ignorant at the time, so I just updated the UI status immediately after the data changed (the next line). There are the following locations:

  • Click Event
  • System callback
  • Network request callback
  • Timer, handler

With so many places updating data and changing the UI, it's a pain to maintain.

Later, after I realized the above technique, I refactored it and had a super large updateView method in it, and then updated each View.

Here is another little trick, that is, if you have 10,000 views and 10,000 states, do you write by state or by view? This means: think like a normal person, for example, if your page has two modes, most people will write

  1. if (mode == A) {
  2. viewA.xxxxxx
  3. viewB.xxxxxx
  4. ...
  5. } else if (mode == B) {
  6. viewA.xxxxxx
  7. viewB.xxxxxx
  8. ...
  9. }

There is a problem with writing like this, young man, do you think you only have two states? Do you think each UI object only depends on one state? Can it be so elegantly distributed in if and else? You are wrong. Requirements are very disgusting and completely illogical.

If you write it this way, when you have a lot of states, it is difficult to arrange where to update the view, and if a new state is added, it will affect it. In order not to affect the previous logic, you often add another line at the end, which is difficult to maintain.

How to do it? The easiest way is to write by View. If you have 10,000 Views, then deal with them one by one. How does the first View affect you? Write them all out, whether it's if-else or anything else. Anyway, the first few lines of code should deal with View1 first.

Then write the logic of View2 and View3 again. The only disadvantage is that you have to repeat your judgment. It seems cumbersome. View1 and View2 may have the same display logic. You really want to write them in one if-else, but I suggest you not to do so. For the best flexibility, please write them separately.

When you perform maintenance in the future, if there is a problem with a View, you only need to find that one and don't need to worry about anything else.

<<:  Using macaca for mobile hybrid automation testing (Part 4)

>>:  An in-depth review of the top five WeChat app development IDEs

Recommend

How to solve the problem of low conversion? 6 key points for high conversion!

In an era of traffic panic, even if you have mill...

How to analyze retention data and reduce user churn?

As we enter the second half of the Internet era, ...

Besides VR, what else can leverage the Internet of Things in 2016?

In 2016, the Internet of Things was written into ...

Android source code download: Android-like Tiantian Dongting player

Functional classification: Entertainment Supporte...