Skip to content

Commit

Permalink
Add tutorial for simple MVC lists
Browse files Browse the repository at this point in the history
Change-Id: I2a049b3c8ad7ed50b478d6ba2f8aded22544a307
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1628989
Reviewed-by: Theresa <twellington@chromium.org>
Commit-Queue: Matthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664796}
  • Loading branch information
iotitan authored and Commit Bot committed May 30, 2019
1 parent 99b15fe commit b514f98
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/ui/android/mvc_architecture_tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ A full explanation of the MVC framework can be found [here](https://docs.google.
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.

#### Additional Resources
[Simple MVC lists](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ui/android/mvc_simple_list_tutorial.md)
[Testing MVC primer doc](https://docs.google.com/document/d/1Mel7f4lE_osFjnttkxu1wcUf_k9CmIzPv6oxwCw9tx4/edit#)

#### File Structure
Expand Down
160 changes: 160 additions & 0 deletions docs/ui/android/mvc_simple_list_tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Simple Lists in MVC Land

### Overview
This tutorial is intended to go over a basic implementation of lists in the Chrome on Android MVC
framework. If you're not sure what MVC is, see [Additional Resources](#Additional-Resources).

In this example we'll be creating a simple menu list where each list item consists of an icon and
label.

### Additional Resources
[Introductory MVC tutorial][mvc_tutorial_link]

#### Todo
[Update this doc to use data providers](https://crbug.com/967054)

### File Structure
The file structure of our component will be the following:
* ./chrome/android/java/src/org/chromium/chrome/browser/simple_menu/
* [`SimpleMenuCoordinator.java`](#SimpleMenuCoordinator)
* [`SimpleMenuItemViewBinder.java`](#SimpleMenuItemViewBinder)
* [`SimpleMenuProperties.java`](#SimpleMenuProperties)
* ./chrome/android/java/res/layout/
* [`simple_menu_item.xml`](#simple_menu_item_xml)

### SimpleMenuCoordinator
This class will own the ListAdapter that knows how to show PropertyModels. In this example we'll be
combining the responsibilities of what would otherwise be the coordinator and mediator for
simplicity.

```java
public class SimpleMenuCoordinator {

public SimpleMenuCoordinator(Context context, ListView listView) {
ModelListAdapter adapter = new ModelListAdapter(context);

final LayoutInflater layoutInflater = context.getSystemService(LAYOUT_INFLATER_SERVICE);

// If this is a heterogeneous list, register more than one type.
adapter.registerType(
ListItemType.DEFAULT,
() -> layoutInflater.inflate(R.layout.simple_menu_item, null),
SimpleMenuItemViewBinder::bind);

listView.setAdapter(adapter);

List<Pair<Integer, PropertyModel>> items = new ArrayList<>();
PropertyModel listModel1 = generateListItem(
ApiCompatibilityUtils.getDrawable(context.getResources(), R.drawable.icon),
context.getResources().getString(R.string.label));
// The list adapter needs to be told what kind of view should render the data; hence the
// pair object.
list.add(new Pair(ListItemType.DEFAULT, listModel1));

// ... add other list items as needed. Typically this work is done in the mediator piece of
// the component.

adapter.updateModels(items);
}

public PropertyModel generateListItem(Drawable icon, String text) {
return PropertyModel.Builder(SimpleMenuProperties.ALL_KEYS)
.with(SimpleMenuProperties.ICON, icon)
.with(SimpleMenuProperties.LABEL, text)
.with(SimpleMenuProperties.CLICK_LISTENER, (view) -> handleClick(view))
.build();
// Click handling can be done as above or the listener can be passed in.
}

private void handleClick(View view) {
// Do some click logic here. This would typically be done in the mediator.
}
}
```

### SimpleMenuProperties
These are the types of data that we want to apply to each list item in our menu.
```java
class SimpleMenuProperties {
@IntDef({ListItemType.DEFAULT})
@Retention(RetentionPolicy.SOURCE)
/**
* This can be one or more items depending on if the list is homogeneous. If homogeneous,
* this definition can be skipped and 0 can be used in place of that parameter.
*/
public @interface ListItemType {
int DEFAULT = 0;
}

/** The icon for the list item. */
public static final WritableObjectPropertyKey<Drawable> ICON =
new WritableObjectPropertyKey<>();

/** The text shown next to the icon. */
public static final WritableObjectPropertyKey<String> LABEL =
new WritableObjectPropertyKey<>();

/** The action that occurs when the list item is tapped. */
public static final WritableObjectPropertyKey<OnClickListener> CLICK_LISTENER =
new WritableObjectPropertyKey<>();

public static final PropertyKey[] ALL_KEYS = {ICON, LABEL, CLICK_LISTENER};
}
```

### SimpleMenuItemViewBinder
As per the MVC architecture, this class is responsible for taking a model and applying that
information in to to a provided view.
```java
class SimpleMenuItemViewBinder {

// This can optionally be in the coordinator file depending on the complexity.
public static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
if (SimpleMenuProperties.ICON == propertyKey) {
((ImageView) view.findViewById(R.id.simple_menu_icon)).setImageDrawable(
model.get(SimpleMenuProperties.ICON));

} else if (SimpleMenuProperties.LABEL == propertyKey) {
((TextView) view.findViewById(R.id.simple_menu_label)).setText(
model.get(SimpleMenuProperties.LABEL));

} else if (SimpleMenuProperties.CLICK_LISTENER == propertyKey) {
view.setOnClickListener(model.get(SimpleMenuProperties.CLICK_LISTENER));
}
}

}
```

### simple_menu_item.xml
```xml
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@color/modern_primary_color">

<org.chromium.ui.widget.ChromeImageView
android:id="@+id/simple_menu_icon"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center_vertical"
android:scaleType="centerInside"/>

<TextView
android:id="@+id/simple_menu_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.BlackBody"/>

</LinearLayout>
```

[mvc_tutorial_link]:https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ui/android/mvc_architecture_tutorial.md

0 comments on commit b514f98

Please sign in to comment.