A full explanation of the MVC framework can be foundhere. This document is intended to go over the logistics of the most basic implementation of the framework in Chrome’s codebase.
For this example, we’ll be implementing a simple progress bar; a rectangle that changes length based on the loading state of the underlying webpage.
The file structure of our component will be the following:
SimpleProgressCoordinator.java
SimpleProgressMediator.java
SimpleProgressViewBinder.java
SimpleProgressProperties.java
SimpleProgressView.java
(assuming it requires interesting logic)The class responsible for setting up the component. This should be the only public class in the component's package and is the only class with direct access to the mediator.
publicclassSimpleProgressCoordinator{privatefinalSimpleProgressMediator mMediator;privatefinalView mView;publicSimpleProgressCoordinator(Tab tabProgressBarIsFor,Context context){PropertyModel model=newPropertyModel.Builder(SimpleProgressProperties.ALL_KEYS).with(SimpleProgressProperties.PROGRESS_FRACTION,0f).with(SimpleProgressProperties.FOREGROUND_COLOR,Color.RED).build(); mView=LayoutInflater.from(context).inflate(R.layout.my_simple_progress_bar);PropertyModelChangeProcessor.create(model, mView,SimpleProgressViewBinder::bind); mMediator=newSimpleProgressMediator(model, tabProgressBarIsFor);}publicvoid destroy(){ mMediator.destroy();}publicView getView(){return mView;}}
Note that there are several ways to acquire the view. If this MVC component owns its own layout file, then it should inflate the view, as shown above. #getView() allows the parent component‘s coordinator to add this component’s view to the hierarchy. However, if the desired view for this MVC component is part of some other parent component's layout file, then the parent component should be responsible for calling findViewById() and passing the right view into this coordinator. Seethis email thread for related discussion.
The class that handles all of the signals coming from the outside world. External classes should never interact with this class directly.
classSimpleProgressMediatorextendsEmptyTabObserver{privatefinalPropertyModel mModel;privatefinalTab mObservedTab;publicSimpleProgressMediator(PropertyModel model,Tab tabProgressBarIsFor){ mModel= model; mObservedTab= tabProgressBarIsFor; mObservedTab.addObserver(this);}@Overridepublicvoid onLoadProgressChanged(Tab tab,int progress){ mModel.set(SimpleProgressProperties.PROGRESS_FRACTION, progress/100f);}@Overridepublicvoid onDidChangeThemeColor(Tab tab,int color){ mModel.set(SimpleProgressProperties.FOREGROUND_COLOR, color);}void destroy(){// Be sure to clean up anything that needs to be (in this case, detach the tab// observer). mObservedTab.removeObserver(this);}}
The class responsible for applying a model to a specific view. In general there is a 1:1 relationship between a type of view and its binder. Multiple binders can know how to take a single type of model and apply it to a view. The binder's method should be stateless; this is implied by the ‘static’ identifier.
classSimpleProgressViewBinder{publicstaticvoid bind(PropertyModel model,View view,PropertyKey propertyKey){if(SimpleProgressProperties.PROGRESS_FRACTION== propertyKey){// Apply width modification to 'view' here.}elseif(SimpleProgressProperties.FOREGROUND_COLOR== propertyKey){// Apply color modification to 'view' here.}}}
These are properties associated with the view the model will be applied to.
classSimpleProgressProperties{publicstaticfinalWritableFloatPropertyKey PROGRESS_FRACTION=newWritableFloatPropertyKey();publicstaticfinalWritableIntPropertyKey FOREGROUND_COLOR=newWritableIntPropertyKey();publicstaticfinalPropertyKey[] ALL_KEYS={PROGRESS_FRACTION, FOREGROUND_COLOR};}