Skip to content

Commit

Permalink
More fixes
Browse files Browse the repository at this point in the history
Summary: a bunch of nits and fixes as described in the attached tasks

Reviewed By: IanChilds

Differential Revision: D4905979

fbshipit-source-id: e1a25ee174c893add7d7eaf66a1d26169481678b
  • Loading branch information
mihaelao authored and facebook-github-bot committed Apr 18, 2017
1 parent 12c9bd1 commit 7acc85c
Show file tree
Hide file tree
Showing 13 changed files with 93 additions and 81 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<img src="docs/static/logo.png" width=150 align=left>

Litho is a powerful Android UI framework that enables developers to write efficient UI code through a simple annotation-based Java API.
Litho is a declarative framework for building efficient UIs on Android.

* **Declarative:** Litho uses a declarative API to define UI components. You simply describe the layout for your UI based on a set of immutable inputs and the framework takes care of the rest.
* **Asynchronous layout:** Litho can measure and layout your UI ahead of time without blocking the UI thread.
Expand Down
1 change: 1 addition & 0 deletions docs/_data/nav_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- id: custom-layout
- id: tree-props
- id: inc-mount
- id: component-tree
- title: Architecture
items:
- id: codegen
Expand Down
8 changes: 4 additions & 4 deletions docs/_docs/asynchronous-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ Immutability in Java usually comes at the cost of having to do many more allocat

Litho offers both synchronous and asynchronous APIs for layout computation. Both APIs are thread safe and can be invoked from any thread. The final layout will always represent the Component that was set last with calls to `setRoot()` or `setRootAsync()`.

Synchronous layout calculation ensures that immediately after calling `setRoot()` on a ComponentTree, the result of the layout calculation is available to be mounted on a `LithoView`. The main disadvantage of this is that the computation happens on the caller thread, therefore calling `setRoot()` from the main thread is not advisable. On the other hand there are situations in which waiting for a background tread to compute a layout before showing something on screen is not acceptable. In these cases calling `setRoot()` synchronously is the best route.
Aaving synchronous operations also makes it very easy to integrate Litho with pre-existing threading models. If your application already has a complex and structured thread design you might want to fit the layout calculation into it without relying on Litho built in threads.
Synchronous layout calculation ensures that immediately after calling `setRoot()` on a [ComponentTree](/javadoc/com/facebook/litho/ComponentTree), the result of the layout calculation is available to be mounted on a [LithoView](/javadoc/com/facebook/litho/LithoView).
The main disadvantage of this is that the computation happens on the caller thread, therefore calling `setRoot()` from the main thread is not advisable. On the other hand there are situations in which you cannot wait for a background thread to compute a layout before showing something on screen, for example when the item you want to display is already in the viewport. In these cases calling `setRoot()` synchronously is the best route.
Having synchronous operations also makes it very easy to integrate Litho with pre-existing threading models. If your application already has a complex and structured thread design you might want to fit the layout calculation into it without relying on Litho's built-in threads.

Asynchronous layout calculation will use Litho's LayoutThread to compute the Component layout. This means that the work gets enqueued immediately on a separate thread and the layout result will not be visible immediately on the calling thread.
Asynchronous layout operations are used widely for example from the [RecyclerBinder](/docs/recycler-component).
Asynchronous layout calculation will use Litho's Layout Thread to compute the Component layout. This means that the work gets enqueued immediately on a separate thread and the layout result will not be visible immediately on the calling thread. Asynchronous layout operations are used widely for example from the [RecyclerBinder](/docs/recycler-component).
29 changes: 29 additions & 0 deletions docs/_docs/component-tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
docid: component-tree
title: Creating a ComponentTree
layout: docs
permalink: /docs/component-tree
---

In the [Using Components](/docs/using-components) guide, we saw how you can create a root component and pass it to a `LithoView`, which will take care of creating a [ComponentTree](/javadoc/com/facebook/litho/ComponentTree) with the given root. ComponentTree manages your component's lifecycle in a thread-safe way. You can create and make calls to it from any thread.
You shouldn't typically need to do this, but there are situations where you might want to create and manage your own `ComponentTree`.
This is how you can create a `ComponentTree`, pass it a component root and attach it to a 'LithoView'. The `ComponentTree`'s `create()` method returns a [Builder](/javadoc/com/facebook/litho/ComponentTree.Builder) which exposes configuration methods for the ComponentTree.

```java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

final LithoView lithoView = new LithoView(this);
final ComponentContext context = new ComponentContext(this);

final Component text = Text.create(context)
.text("Hello World")
.textSizeDip(50)
.build();
final ComponentTree componentTree = ComponentTree.create(context, text).build();

lithoView.setComponentTree(componentTree);
setContentView(lithoView);
}
```
14 changes: 7 additions & 7 deletions docs/_docs/events-touch-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ And the callback within MyComponentSpec would look like this:
@LayoutSpec
class MyComponentSpec {
...
@OnEvent(ClickEvent.class)
static void onClick(
ComponentContext c,
@FromEvent View view,
@Prop String someProp) {
// Handle click here.
}
@OnEvent(ClickEvent.class)
static void onClick(
ComponentContext c,
@FromEvent View view,
@Prop String someProp) {
// Handle click here.
}
}
```

Expand Down
4 changes: 3 additions & 1 deletion docs/_docs/getting-started/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

final ComponentContext c = new ComponentContext(this);

final LithoView lithoView = LithoView.create(
this /* context */,
Text.create(new ComponentContext(this))
Text.create(c)
.text("Hello, World!")
.build());

Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ layout: docs
permalink: /docs/intro
---

Litho is a declarative framework to build efficient user interfaces (UI) on
Litho is a declarative framework for building efficient user interfaces (UI) on
Android. It allows you to write highly-optimized Android views through a simple
functional API based on Java annotations. It was [primarily built](/docs/uses)
to implement complex scrollable UIs based on RecyclerView.
Expand Down
4 changes: 0 additions & 4 deletions docs/_docs/mount-specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,9 @@ public class ColorComponentSpec {
```

- The mount operation has an API very similar to Android's [RecyclerView Adapters](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html). It has a `onCreateMountContent` method to create and initialize the `View`/`Drawable` content if the recycling pool is empty, and an `onMount` method to update the recycled content with the current information.

- The return type from `onCreateMountContent` should always match the type of the second argument of `onMount`. They are required to be a `View` or a `Drawable` subclass. This is validated by the annotation processor at build time.

- Mounting always happens in the main thread as it might have to deal with Android Views (which are bound to the main thread).

- `onCreateMountContent` cannot take a `@Prop` or any other annotated parameter.

- Given that the `@OnMount` method always runs in the UI thread, expensive operations should not be performed in it.

## Inter-stage inputs and outputs
Expand Down
46 changes: 26 additions & 20 deletions docs/_docs/recycler-component.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
docid: recycler-component
title: RecyclerComponent
title: Recycler
layout: docs
permalink: /docs/recycler-component
---
Expand All @@ -18,40 +18,39 @@ static ComponentLayout onCreateLayout(
final ComponentContext c,
@Prop RecyclerBinder recyclerBinder) {

return Container.create(c)
.child(Recycler.create(c)
.binder(recyclerBinder))
.build();
return Recycler.create(c)
.binder(recyclerBinder)
.buildWithLayout();
}
```
This code will render a Recycler component that will display the content of recyclerBinder.
This code will render a `Recycler` component that will display the content of `recyclerBinder`.

### RecyclerBinder
[RecyclerBinder](/javadoc/com/facebook/litho/widget/RecyclerBinder) is the entry point to manipulate list-like UIs with Components.
[RecyclerBinder](/javadoc/com/facebook/litho/widget/RecyclerBinder) is the entry point to manipulate list-like UIs with components.
It keeps a list of all the components contained in the list and as the user scrolls through the list it computes layouts for items that are about to become visible.

`RecyclerBinder` is the part of Litho that:

- Serves as an Adapter for a RecyclerView
- Defines the layout to use in the RecyclerView (ex Linear, Grid)
- Serves as an `Adapter` for a `RecyclerView`
- Defines the layout to use in the `RecyclerView` (ex Linear, Grid)
- Manages all the complexity of computing layouts ahead of time in a background thread.

![Layout range in action](/static/images/range_small.gif "Layout range in action")

Let's start creating a RecyclerBinder:
Let's start creating a `RecyclerBinder`:

``` java
final RecyclerBinder recyclerBinder = new RecyclerBinder(c);
```
This will create the simplest possible RecyclerBinder that will layout the content of the Recycler as a vertical list.
This will create the simplest possible `RecyclerBinder` that will layout the content of the `Recycler` as a vertical list.

To have Recycler use a grid layout we can use this constructor instead:
To have `Recycler` use a grid layout we can use this constructor instead:

``` java
final RecyclerBinder recyclerBinder = new RecyclerBinder(c, new GridLayoutInfo(c, spanCount);
```

RecyclerBinder exposes a set of APIs to manipulate the items that will be displayed in the Recycler.
`RecyclerBinder` exposes a set of APIs to manipulate the items that will be displayed in the `Recycler`.

The most commonly used are:

Expand All @@ -62,28 +61,35 @@ recyclerBinder.removeItemAt(position);
recyclerBinder.moveItem(fromPosition, toPosition);
```

RecyclerBinder's API works directly with Components, since a Component is only a collection of Props, we can build any component ahead of time and leave the layout management to the RecyclerBinder.
`RecyclerBinder`'s API works directly with components. Since a component is only a collection of **props**, we can build any component ahead of time and leave the layout management to the `RecyclerBinder`.
RecyclerBinder also supports receiving extra information about the way a Component should be laid out. These extra information can be passed in through a ComponentInfo. Here's what the code looks like:
`RecyclerBinder` also supports receiving extra information about the way a component should be laid out. These extra information can be passed in through a `ComponentInfo`. Here's what the code looks like:

``` java
recyclerBinder.insertItemAt(
position,
ComponentInfo.create().component(component).isSticky().build());
ComponentInfo.create()
.component(component)
.isSticky()
.build());
```

#### Using RecyclerBinder with DiffUtil

RecyclerBinder exposes by default bindings to be used in conjunction with [DiffUtil](https://developer.android.com/reference/android/support/v7/util/DiffUtil.html).
Litho defines in its API a [RecyclerBinderUpdateCallback](/javadoc/com/facebook/litho/widget/RecyclerBinderUpdateCallback.html) that implements ListUpdateCallback and therefore can be used to dispatch the DiffResult to a RecyclerBinder.
Litho defines in its API a [RecyclerBinderUpdateCallback](/javadoc/com/facebook/litho/widget/RecyclerBinderUpdateCallback.html) that implements `ListUpdateCallback` and therefore can be used to dispatch the `DiffResult` to a `RecyclerBinder`.

Here's an example of how DiffUtil can be used with Litho:
Here's an example of how `DiffUtil` can be used with Litho:
``` java
private final ComponentRenderer<Data> mComponentRenderer = new ComponentRenderer<> {
ComponentInfo render(Data data, int idx) {
return ComponentInfo.create().component(DataComponent.create(mComponentContext).data(data)).build();
return ComponentInfo.create()
.component(
DataComponent.create(mComponentContext)
.data(data))
.build();
}
}
Expand All @@ -101,4 +107,4 @@ Here's an example of how DiffUtil can be used with Litho:
}
```
The ComponentRenderer will be invoked whenever a new Component needs to be created for a new or updated model in the list.
The `ComponentRenderer` will be invoked whenever a new component needs to be created for a new or updated model in the list.
26 changes: 14 additions & 12 deletions docs/_docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ How do you render this component? In your activity, simply change the `Component
final Component text = ListItem.create(context).build();
```

**Note:** That's `ListItem` you're using, which is the generated implementation of [ComponentLifecycle](/javadoc/com/facebook/litho/ComponentLifecycle), not `ListItemSpec`.
**Note:** That's `ListItem` you're using, not `ListItemSpec`.

Where did `ListItem` come from? Where are `create` and `build` defined? This is the magic of Litho _Specs_.

Expand Down Expand Up @@ -138,30 +138,32 @@ Next, in your activity, modify the `Component` definition as follows:
```java
final RecyclerBinder recyclerBinder = new RecyclerBinder(
context,
4.0f, /* range ratio */
new LinearLayoutInfo(this, OrientationHelper.VERTICAL, false));

final Component component = Recycler.create(context)
.binder(recyclerBinder)
.build();
```

This code constructs a `RecyclerBinder` and attaches it to a `Recycler`. A new `RecyclerBinder` takes as constructor parameters a component context, a range ratio, and layout info. In your example, this sets the range ratio to 4 and a `LinearLayoutInfo` for the layout info.
This code constructs a `RecyclerBinder` and attaches it to a `Recycler`. A new `RecyclerBinder` takes as constructor parameters a component context and layout info.

You then create and pass in the `Recycler` component to the `LithoView`.

Now turn your focus to populating the binder with list items. Define a helper function in your activity to do this:

```java
private void addContent(
RecyclerBinder recyclerBinder,
ComponentContext context) {
for (int i = 0; i < 32; i++) {
ComponentInfo.Builder componentInfoBuilder = ComponentInfo.create();
componentInfoBuilder.component(ListItem.create(context).build());
recyclerBinder.insertItemAt(i, componentInfoBuilder.build());
}
}
private static void addContent(RecyclerBinder recyclerBinder, ComponentContext context) {
for (int i = 0; i < 32; i++) {
recyclerBinder.insertItemAt(
i,
ComponentInfo.create()
.component(
ListItem.create(context)
.color(i % 2 == 0 ? Color.WHITE : Color.LTGRAY)
.message("Hello, world!")
.build())
.build());
}
```

In the code, a [ComponentInfo](/javadoc/com/facebook/litho/ComponentInfo) is created that describes the components to be rendered by a `Recycler`. In this example, a `ListItem` is the component to be rendered.
Expand Down
22 changes: 0 additions & 22 deletions docs/_docs/using-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,4 @@ In this example, `MyComponent` will be laid out by the hosting `LithoView`, whic
> IMPORTANT: The LithoView from this example, if directly used in your view hierarchy as is, will perform layout synchronously on the main thread.
For more information about performing layout off the main thread, see [Async Layout](/docs/architecture#async-layout).

## Extra
We saw how you can create a root component and pass it to a `LithoView`, which will take care of creating a [ComponentTree](/javadoc/com/facebook/litho/ComponentTree) with the given root. ComponentTree manages your component's lifecycle in a thread-safe way. You can create and make calls to it from any thread.
You shouldn't typically need to do this, but there are situations where you might want to create and manage your own `ComponentTree`, such as turning off [incremental mount](/docs/intro#incremental-mount).
This is how you can create a `ComponentTree`, pass it a component root and attach it to a 'LithoView'. The `ComponentTree`'s `create()` method returns a [Builder](/javadoc/com/facebook/litho/ComponentTree.Builder) which exposes configuration methods for the ComponentTree.

```java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LithoView lithoView = new LithoView(this);
final ComponentContext context = new ComponentContext(this);
final Component text = Text.create(context)
.text("Hello World")
.textSizeDip(50)
.build();
final ComponentTree componentTree = ComponentTree.create(context, text).build();
lithoView.setComponentTree(componentTree);
setContentView(lithoView);
}
```
3 changes: 0 additions & 3 deletions docs/_docs/visibility_handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,8 @@ permalink: /docs/visibility-handling
The framework currently supports four types of Visibility Event:

- [Visible Event](/javadoc/com/facebook/litho/VisibleEvent): this event is triggered when at least one pixel of the Component is visible.

- [Invisible Event](/javadoc/com/facebook/litho/InvisibleEvent): this event is triggered when the Component no longer has any pixels on the screen.

- [Focused Visible Event](/javadoc/com/facebook/litho/FocusedVisibleEvent): this event is triggered when either the Component occupies at least half of the viewport, or, if the Component is smaller than half the viewport, when it is fully visible.

- [Full Impression Visible Event](/javadoc/com/facebook/litho/FullImpressionVisibleEvent): this event is triggered when the entire Component has passed through the viewport at some point.

### Usage ###
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public void onCreate(Bundle savedInstanceState) {

final RecyclerBinder recyclerBinder = new RecyclerBinder(
context,
4.0f,
new LinearLayoutInfo(this, OrientationHelper.VERTICAL, false));

final Component component = Recycler.create(context)
Expand All @@ -44,13 +43,15 @@ public void onCreate(Bundle savedInstanceState) {

private static void addContent(RecyclerBinder recyclerBinder, ComponentContext context) {
for (int i = 0; i < 32; i++) {
ComponentInfo.Builder componentInfoBuilder = ComponentInfo.create();
componentInfoBuilder.component(
ListItem.create(context)
recyclerBinder.insertItemAt(
i,
ComponentInfo.create()
.component(
ListItem.create(context)
.color(i % 2 == 0 ? Color.WHITE : Color.LTGRAY)
.message("Hello, world!")
.build());
recyclerBinder.insertItemAt(i, componentInfoBuilder.build());
.build())
.build());
}
}
}

0 comments on commit 7acc85c

Please sign in to comment.