In the previous post we explicitly register each observer to the subject. it is more useful to have the observers register and unregister themselves once the views are attached and detached from the screen respectively. This means we never need to worry about adding extra code to deal with unregistering a particular observer during lifecycle events. Here is an improved UIConfigAwareTextView that does that
public class UIConfigAwareTextView extends TextView implements UIConfigObserver { public UIConfigAwareTextView(Context context) { this(context, null); } public UIConfigAwareTextView(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.textViewStyle); } public UIConfigAwareTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if(getContext() instanceof UIConfigGetter){ ((UIConfigGetter)getContext()).getUIConfig().registerObserver(this); } } @Override protected void onDetachedFromWindow() { if(getContext() instanceof UIConfigGetter){ ((UIConfigGetter)getContext()).getUIConfig().unregisterObserver(this); } super.onDetachedFromWindow(); } @Override public void update(UIConfig uiConfig) { this.setTextColor(uiConfig.getFontColor()); } }
Also here is a more complete UIConfig model that uses a Handler and synhronizes the observers list. This version will also notify observers once they are added to the observer list so as to ensure they always recieve the latest font color as soon as the view is attached. In order to achieve notifying single observers we can add another method to the UIConfigSubject interface (scroll down for this)
public class UIConfig implements UIConfigSubject { private static final String TAG = UIConfig.class.getSimpleName(); private ArrayList<UIConfigObserver> mObservers = new ArrayList<UIConfigObserver>(); private Object mObserversLock = new Object(); private Handler mHandler = null; private boolean mPostPending = false; /** UIConfig properties */ private int mFontColor; public UIConfig() { // } public void setHandler(Handler handler) { mHandler = handler; if(mPostPending){ mPostPending = false; notifyObservers(); } } @Override public void registerObserver(UIConfigObserver observer) { synchronized (mObserversLock){ mObservers.add(observer); notifyObservers(); } } @Override public void unregisterObserver(UIConfigObserver observer) { synchronized (mObserversLock){ mObservers.remove(observer); } } @Override public void notifyObservers() { synchronized (mObserversLock){ if(mHandler != null){ mHandler.post(new NotifyAllRunnable()); } else{ mPostPending = true; } } } @Override public void notifyObserver(UIConfigObserver observer) { mHandler.post(new NotifySingleRunnable(observer)); } private class NotifySingleRunnable implements Runnable { private UIConfigObserver mNewestObserver; public NotifySingleRunnable(UIConfigObserver observer) { mNewestObserver = observer; } @Override public void run() { Log.w(TAG, "Notifying Single observer."); mNewestObserver.update(UIConfig.this); } } private class NotifyAllRunnable implements Runnable { public NotifyAllRunnable() { // } @Override public void run() { Log.w(TAG, "Notifying " + mObservers.size() + " observers."); for(UIConfigObserver ob : mObservers){ ob.update(UIConfig.this); } } } public void copyProperties(UIConfig uiConfig) { setFontColor(uiConfig.getFontColor()); } public void setFontColor(int fontColor) { this.mFontColor = fontColor; notifyObservers(); } public int getFontColor() { return mFontColor; } }
Here is the revised interface
public interface UIConfigSubject { public void registerObserver(UIConfigObserver observer); public void unregisterObserver(UIConfigObserver observer); public void notifyObservers(); //This allows us to notify observers independently public void notifyObserver(UIConfigObserver observer); }