blob: 397032b4bd7b2afc1c99d67b69501ab792a31f4a [file] [log] [blame] [view]
Matthew Jones871569f12019-04-03 16:25:481# So, you want to do MVC...
2
3### Overview
Ted Choca21201e42022-04-15 03:23:094A full explanation of the MVC framework can be found [here](mvc_overview.md). This document is intended to go over the logistics of the most basic implementation of the framework in Chromes codebase.
Matthew Jones871569f12019-04-03 16:25:485
6For this example, well be implementing a simple progress bar; a rectangle that changes length based on the loading state of the underlying webpage.
7
8#### Additional Resources
Ted Choca21201e42022-04-15 03:23:099* [Simple MVC lists](mvc_simple_list_tutorial.md)
10* [Testing MVC primer doc](mvc_testing.md)
Matthew Jones871569f12019-04-03 16:25:4811
12#### File Structure
13The file structure of our component will be the following:
14* ./org/chromium/chrome/browser/simple_progress/
15 * [`SimpleProgressCoordinator.java`](#SimpleProgressCoordinator)
16 * [`SimpleProgressMediator.java`](#SimpleProgressMediator)
17 * [`SimpleProgressViewBinder.java`](#SimpleProgressViewBinder)
18 * [`SimpleProgressProperties.java`](#SimpleProgressProperties)
19 * `SimpleProgressView.java` (assuming it requires interesting logic)
20
21#### SimpleProgressCoordinator
22The 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.
23
24```java
25public class SimpleProgressCoordinator {
26
Sky Malice7892f112020-11-09 18:06:2327 private final SimpleProgressMediator mMediator;
28 private final View mView;
Matthew Jones871569f12019-04-03 16:25:4829
Sky Malice7892f112020-11-09 18:06:2330 public SimpleProgressCoordinator (Tab tabProgressBarIsFor, Context context) {
Matthew Jones871569f12019-04-03 16:25:4831
32 PropertyModel model = new PropertyModel.Builder(SimpleProgressProperties.ALL_KEYS)
33 .with(SimpleProgressProperties.PROGRESS_FRACTION, 0f)
34 .with(SimpleProgressProperties.FOREGROUND_COLOR, Color.RED)
35 .build();
36
Sky Malice7892f112020-11-09 18:06:2337 mView = LayoutInflater.from(context).inflate(R.layout.my_simple_progress_bar);
Matthew Jones871569f12019-04-03 16:25:4838
Sky Malice7892f112020-11-09 18:06:2339 PropertyModelChangeProcessor.create(model, mView, SimpleProgressViewBinder::bind);
Matthew Jones871569f12019-04-03 16:25:4840
41 mMediator = new SimpleProgressMediator(model, tabProgressBarIsFor);
42 }
43
44 public void destroy() {
45 mMediator.destroy();
46 }
Sky Malice7892f112020-11-09 18:06:2347
48 public View getView() {
49 return mView;
50 }
Matthew Jones871569f12019-04-03 16:25:4851}
52```
Sky Malice7892f112020-11-09 18:06:2353Note 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. See [this email thread](http://g/clank-frontend/u8x2PBa5EfI) for related discussion.
Matthew Jones871569f12019-04-03 16:25:4854
55#### SimpleProgressMediator
56The class that handles all of the signals coming from the outside world. External classes should never interact with this class directly.
57
58```java
59class SimpleProgressMediator extends EmptyTabObserver {
60
Sky Malice7892f112020-11-09 18:06:2361 private final PropertyModel mModel;
62 private final Tab mObservedTab;
Matthew Jones871569f12019-04-03 16:25:4863
64 public SimpleProgressMediator(PropertyModel model, Tab tabProgressBarIsFor) {
65 mModel = model;
66 mObservedTab = tabProgressBarIsFor;
67 mObservedTab.addObserver(this);
68 }
69
70 @Override
71 public void onLoadProgressChanged(Tab tab, int progress) {
72 mModel.set(SimpleProgressProperties.PROGRESS_FRACTION, progress / 100f);
73 }
74
75 @Override
76 public void onDidChangeThemeColor(Tab tab, int color) {
77 mModel.set(SimpleProgressProperties.FOREGROUND_COLOR, color);
78 }
79
80 void destroy() {
81 // Be sure to clean up anything that needs to be (in this case, detach the tab
82 // observer).
83 mObservedTab.removeObserver(this);
84 }
85}
86```
87
88#### SimpleProgressViewBinder
89The 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.
90
91```java
92class SimpleProgressViewBinder {
93
94 public static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
95 if (SimpleProgressProperties.PROGRESS_FRACTION == propertyKey) {
96
97 // Apply width modification to 'view' here.
98
99 } else if (SimpleProgressProperties.FOREGROUND_COLOR == propertyKey) {
100
101 // Apply color modification to 'view' here.
102
103 }
104 }
105}
106```
107
108#### SimpleProgressProperties
109These are properties associated with the view the model will be applied to.
110
111```java
112class SimpleProgressProperties {
113
114 public static final WritableFloatPropertyKey PROGRESS_FRACTION =
115 new WritableFloatPropertyKey();
116
117 public static final WritableIntPropertyKey FOREGROUND_COLOR =
118 new WritableIntPropertyKey();
119
120 public static final PropertyKey[] ALL_KEYS = {PROGRESS_FRACTION, FOREGROUND_COLOR};
121}
122```