Coder Social home page Coder Social logo

facebook / litho Goto Github PK

View Code? Open in Web Editor NEW
7.6K 183.0 752.0 725.9 MB

A declarative framework for building efficient UIs on Android.

Home Page: https://fblitho.com

License: Apache License 2.0

Java 49.76% C 0.20% C++ 4.86% Shell 0.07% CMake 0.04% Kotlin 42.86% Starlark 1.95% JavaScript 0.23% CSS 0.04%

litho's Introduction

Litho GithubCI Bintray Join the chat at https://gitter.im/facebook/litho License

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.
  • View flattening: Litho uses Yoga for layout and automatically reduces the number of ViewGroups that your UI contains.
  • Fine-grained recycling: Any component such as a text or image can be recycled and reused anywhere in the UI.

To get started, check out these links:

Installation

Litho can be integrated either in Gradle or Buck projects. Read our Getting Started guide for installation instructions.

Quick start

1. Initialize SoLoader in your Application class.

public class SampleApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, false);
  }
}

2. Create and display a component in your Activity

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

    final ComponentContext c = new ComponentContext(this);

    final Component component = Text.create(c)
        .text("Hello World")
        .textSizeDip(50)
        .build();

    setContentView(LithoView.create(c, component));
}

Run sample

You can find more examples in our sample app.

To build and run (on an attached device/emulator) the sample app, execute

$ buck fetch sample
$ buck install -r sample

or, if you prefer Gradle,

$ ./gradlew :sample:installDebug

Contributing

Before contributing to Litho, please first read the Code of Conduct that we expect project participants to adhere to.

For pull requests, please see our CONTRIBUTING guide.

See our issues page for ideas on how to contribute or to let us know of any problems.

Please also read our Coding Style and Code of Conduct before you contribute.

Getting Help

  • Post on StackOverflow using the #litho tag.
  • Chat with us on Gitter.
  • Join our Facebook Group to stay up-to-date with announcements.
  • Please open GitHub issues only if you suspect a bug in the framework or have a feature request and not for general questions.

License

Litho is licensed under the Apache 2.0 License.

litho's People

Contributors

adityasharat avatar andrewpmsmith avatar apowolny avatar astreet avatar colriot avatar davidaurelio avatar ianchilds avatar jaegs avatar jsendros avatar kevinstrider avatar kingsleyadio avatar lucasr avatar marco-cova avatar mhorowitz avatar mihaelao avatar mkarpio avatar muraziz avatar nickgerleman avatar nicous avatar pakoito avatar pasqualeanatriello avatar passy avatar priteshrnandgaonkar avatar rratmansky avatar sergeynv avatar sidharthguglani-zz avatar strulovich avatar usikder avatar vavom avatar zielinskimz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

litho's Issues

Create specific container for single-child usecase

Row and Column are great APIs as they make the direction of the container explicit. However this causes some confusion when wanting a container with only a single child as both Row and Column will work. Therefor we would like to introduce some Container.Builder type which only accepts one child (possibly only 1 relative positioned child and however many absolute children).

I can think of two possible APIs:

SingleChildContainer.create(c)
   .child(...)
   .child(...) // throws exception
   ...;

or

MyComponent.create(c)
   .prop1(...)
   .withContainer()
   ....;

Point of contact: @emilsjolander

litho-core contains widget resources

The widget_attrs.xml is currently part of litho-core but should obviously live in litho-widget.

It's not as easy as moving it to the other package and updating the references to com.facebook.litho.widget.R as we create another cycle since litho-testing depends on some of the styleable attributes defined in there.

We need to find a way to untangle this. Another stand-alone resource package sounds like something we would want to avoid. Get creative!

don't do anything If a state is "updated", but it hasn't actually changed

Don't trigger state updates if the old value and new value of the state is the same.

Two ideas for this:

  1. When we have all the stateUpdates on the ComponentTree, we could call shouldUpdate on each Component and if it is false for all of them, then we should return without calculating a new layout.
  2. When we update the value for the state, we could compare it to the old one and if it is the same, then don't do anything (seems simpler, not sure if there are issues here).

We could have a StateContainer comparison and don't enqueue incoming StateUpdates if the state value doesn't change.

Code pointer

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentTree.java#L697

Point of contact: @mihaelao

Image bounds rendered incorrectly

Issues and Steps to Reproduce

Use the following code inside @OnCreateLayout:

Column.create(c).widthDip(72).heightDip(72).child(
    Image.create(c).drawableRes(R.drawable.ic_launcher).scaleType(ImageView.ScaleType.CENTER_CROP)
).backgroundColor(Color.RED)
.build();

FYI: Adding alignItems(YogaAlign.STRETCH) and/or alignContent(YogaAlign.STRETCH) does not change the result in any way.

The following image is what is rendered on device:

When scaleType is set to CENTER Litho generates the following:

Expected Behavior

The image should be spread accross the parent Column as marked by the red region (scaleType CENTER_CROP):

The image should be centered inside the view (scaleType CENTER):

Link to Code

See above minimal code sample to reproduce.

Add support for borderRadius

We already have support for specifying border width and border color on a component. Now we would like to have an ability to specify the border radius which will draw curved edges on the component. The API might look like this:

Row.create(c)
        .child(
            Row.create(c)
                ....)
        .child(
            Text.create(c)
                .text("Sample with border radius")
                .textSizeDip(14))
        .child(
            Row.create(c)
                ....)
        .borderColor(Color.BLUE)
        .borderWidthDip(YogaEdge.BOTTOM, 33)
        .borderRadiusDip(TOPLEFT, 2)
        .borderRadiusDip(TOPRIGHT, 2)
        .build();

Or you can specify one value for all edges something like:

Row.create(c)
       .....
        .borderRadiusDip(ALL, 2)
        .build();

Besides specifying radius in Dip, you should be able to specify it in pixel value, resource attribute, dimension, similar to how borderWidth is defined.

Point of contact: @muraziz

StickyHeader not visible after scrolling back

Issues and Steps to Reproduce

Issue:StickyHeader is not visible after scrolling back.
Steps to reproduce:Create a RecyclerBinder and add components that are sticky.

Expected Behavior

Expect the sticky header to be visible

Link to Code

Code Available here: Recycler Spec

Gif of the issue

Create TextSpec API for highlighting text with longclick like in Android EditText

Create a new API in TextSpec to give callback about the text that's highlighted and change highlight text background. This should be similar to how Android EditText handles text selection.

To achieve that first we need to get (x, y) coordinates of a touch event, and then get the start, end offset of the text that's under (x,y).
For the first part we can do something similar as in TextView's getOffsetForPosition() API (https://developer.android.com/reference/android/widget/TextView.html#getOffsetForPosition(float, float)). When we have start, end offset of the text, it shouldn't be hard to highlight corresponding portion of the text.

For the API side we might create a new event, something like TextHighlightEvent and provide corresponding prop to TextSpec.

Point of contact: @muraziz

Fix lint warnings and reenable abortOnError

Lint doesn't have the highest opinion of us at the moment and we had to disable abortOnError as a consequence. Some of the issues are just noise, others are signal.

We should suppress those that we're okay with in code and fix the ones we're not.

This is a good task for new contributors.

Sample crashes on startup

Issues and Steps to Reproduce

Install and run sample on Nexus 6 with Android 5.0. The application crashes on startup.

04-19 13:11:26.200 893-2754/? I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.facebook.samples.litho/.DemoListActivity bnds=[781,953][998,1170] (has extras)} from uid 10095 on display 0
04-19 13:11:26.286 893-1411/? I/ActivityManager: Start proc com.facebook.samples.litho for activity com.facebook.samples.litho/.DemoListActivity: pid=31261 uid=10106 gids={50106, 9997, 3003, 1028, 1015} abi=armeabi-v7a
04-19 13:11:26.293 31261-31261/? I/art: Late-enabling -Xcheck:jni
04-19 13:11:26.408 31261-31261/? V/fb-UnpackingSoSource: locked dso store /data/data/com.facebook.samples.litho/lib-main
04-19 13:11:26.409 31261-31261/? V/fb-UnpackingSoSource: deps mismatch on deps store: regenerating
04-19 13:11:26.409 31261-31261/? V/fb-UnpackingSoSource: so store dirty: regenerating
04-19 13:11:26.437 31261-31261/? D/ApkSoSource: not allowing consideration of lib/armeabi-v7a/libfb_jpegturbo.so: deferring to libdir
04-19 13:11:26.437 31261-31261/? D/ApkSoSource: not allowing consideration of lib/armeabi-v7a/libgnustl_shared.so: deferring to libdir
04-19 13:11:26.437 31261-31261/? D/ApkSoSource: not allowing consideration of lib/armeabi-v7a/libimagepipeline.so: deferring to libdir
04-19 13:11:26.437 31261-31261/? D/ApkSoSource: not allowing consideration of lib/armeabi-v7a/liblib_fb_fbjni.so: deferring to libdir
04-19 13:11:26.437 31261-31261/? D/ApkSoSource: not allowing consideration of lib/armeabi-v7a/libyoga.so: deferring to libdir
04-19 13:11:26.437 31261-31261/? D/ApkSoSource: not allowing consideration of lib/armeabi-v7a/libyogacore.so: deferring to libdir
04-19 13:11:26.438 31261-31261/? V/fb-UnpackingSoSource: regenerating DSO store com.facebook.soloader.ApkSoSource
04-19 13:11:26.439 31261-31261/? V/fb-UnpackingSoSource: starting syncer worker
04-19 13:11:26.447 31261-31261/? V/fb-UnpackingSoSource: releasing dso store lock for /data/data/com.facebook.samples.litho/lib-main (from syncer thread)
04-19 13:11:26.447 31261-31261/? V/fb-UnpackingSoSource: not releasing dso store lock for /data/data/com.facebook.samples.litho/lib-main (syncer thread started)
04-19 13:11:26.456 31261-31279/? I/stetho: Listening on @stetho_com.facebook.samples.litho_devtools_remote
04-19 13:11:26.485 31261-31261/? W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
04-19 13:11:26.674 31261-31281/? D/OpenGLRenderer: Render dirty regions requested: true
04-19 13:11:26.680 31261-31261/? D/Atlas: Validating map...
04-19 13:11:26.734 31261-31261/? E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
04-19 13:11:26.734 31261-31261/? E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
04-19 13:11:26.734 31261-31261/? E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
04-19 13:11:26.735 31261-31261/? A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 31261 (k.samples.litho)
04-19 13:11:26.839 12429-12429/? I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
04-19 13:11:26.839 12429-12429/? I/DEBUG: Build fingerprint: 'google/shamu/shamu:5.0/LRX21O/1570415:user/release-keys'
04-19 13:11:26.839 12429-12429/? I/DEBUG: Revision: '33696'
04-19 13:11:26.839 12429-12429/? I/DEBUG: ABI: 'arm'
04-19 13:11:26.840 12429-12429/? I/DEBUG: pid: 31261, tid: 31261, name: k.samples.litho  >>> com.facebook.samples.litho <<<
04-19 13:11:26.840 12429-12429/? I/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
04-19 13:11:26.851 12429-12429/? I/DEBUG:     r0 00000000  r1 00000020  r2 ffffffff  r3 b6fd432c
04-19 13:11:26.851 12429-12429/? I/DEBUG:     r4 00000000  r5 ab746794  r6 bebf0b24  r7 ab746780
04-19 13:11:26.851 12429-12429/? I/DEBUG:     r8 b678f628  r9 b6fd5df4  sl 00000055  fp 00000055
04-19 13:11:26.851 12429-12429/? I/DEBUG:     ip b6fd19b8  sp bebf0b18  lr b676782d  pc b6fb905c  cpsr 000f0030
04-19 13:11:26.851 12429-12429/? I/DEBUG: backtrace:
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #00 pc 0005005c  /system/lib/libc.so (__strchr_chk+11)
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #01 pc 00021829  /system/lib/libhwui.so (android::uirenderer::Extensions::findExtensions(char const*, android::SortedVector<android::String8>&) const+12)
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #02 pc 000218a9  /system/lib/libhwui.so (android::uirenderer::Extensions::Extensions()+64)
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #03 pc 00022463  /system/lib/libhwui.so
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #04 pc 0001b1a3  /system/lib/libhwui.so (android::uirenderer::Caches::Caches()+50)
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #05 pc 000189df  /system/lib/libhwui.so (android::Singleton<android::uirenderer::Caches>::getInstance()+34)
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #06 pc 0001fc23  /system/lib/libhwui.so (android::uirenderer::DisplayListRenderer::DisplayListRenderer()+98)
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #07 pc 0007a027  /system/lib/libandroid_runtime.so
04-19 13:11:26.851 12429-12429/? I/DEBUG:     #08 pc 00019f09  /data/dalvik-cache/arm/system@[email protected]

Expected Behavior

The application doesn't crash.

Link to Code

No special code is needed. Just ./gradlew :sample:installDebug on a cloned project.

Bug with headers in stock sample/demo

Issues and Steps to Reproduce

  1. Star the sample app.
  2. Scroll down until the article on Gaston Marichal (under 1830) is half-way off screen.
  3. Scroll back up a little bit, so the date changes to 1810.
  4. Observe the 1830 header is not detaching from the fixed header and is not becoming part of the document.

Expected Behavior

The 1830 header should pop back into the scrollable surface. Currently the header does not pop into the document, which results in a strange/glitchy header.

Link to Code

Standard litho sample/demo as of today. Video: https://www.youtube.com/watch?v=KEbN1-iriSk&feature=youtu.be

Adding layout attributes to an already measured component doesn't work

When calling Layout.create(c, component) after the component has been measured, it causes the subsequent attribute (including the click handler) to be lost.

What happens is that we end up with two internal nodes for the same components and it doesn't work out well. The problem relates to returning early when we call LayoutState.collectResults() and InternalNode.isNestedTreeHolder() returns true, which is the case when we already have a cached layout for the component associated with the node.

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/LayoutState.java#L447

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/InternalNode.java#L369

Point of contact: @marco-cova

Create api to acquire Views for measuring

We need an API for acquiring Views for measuring. This api would allow us to do things like:

@OnMeasure
protected static void onMeasure(
        ComponentContext c, 
        ComponentLayout layout, 
        int widthSpec, 
        int heightSpec, 
        Size size) {
    View toMeasure = c.acquireMeasurableView();
    toMeasure.measure(...)
    ...
    c.releaseMeasurableView(toMeasure);
}

This way we might cache the views by context and thread and make this thread safe and consistent with theming. The acquire being a Component level api could simply call ComponentLifecycle.createMountContent under the hood (and create appropriate LayoutParams).

Point of contact: @IanChilds

Better testing APIs for state

Add more fluent and straight-forward APIs for testing changes to components and sub-components after state updates. The current way to test stateful APIs is unnecessarily clunky. StateUpdatesTestHelper is the current way to trigger state updates and make assertions about the state after an update. The test helper Gets Things Done, but it's unpleasant to use. You need to use reflection to set it up and if something goes wrong, it's unclear why.

It would be nice to unify this under our ComponentAsserts API and allow for a fluent style like assertThat(component).afterStateUpdate(...).hasSubComponent(...).

Point of contact: @passy

I use Huawei mobile phones,Android 4.2.2and 7.0 are crash

I use Huawei mobile phones,Android 4.2.2and 7.0 are crash

build.gradle
// Litho compile 'com.facebook.litho:litho-core:0.2.0' compile 'com.facebook.litho:litho-widget:0.2.0' provided 'com.facebook.litho:litho-annotations:0.2.0' annotationProcessor 'com.facebook.litho:litho-processor:0.2.0' // SoLoader compile 'com.facebook.soloader:soloader:0.2.0' // Optional // For debugging debugCompile 'com.facebook.litho:litho-stetho:0.2.0' // For integration with Fresco compile 'com.facebook.litho:litho-fresco:0.2.0' // For testing testCompile 'com.facebook.litho:litho-testing:0.2.0'

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

Application:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); SoLoader.init(this, false); } }

AndroidManifest.xml:
application android:name=".MyApplication"

I use Meizu Mx4 pro is normal ,Android version 5.1

Kotlin LayoutSpec files not found by the Processor

Issues and Steps to Reproduce

I have a simple LayoutSpec that returns a single Text item. Defining it in Kotlin:

@LayoutSpec
class CurrentWeatherTopSpec {

    companion object {
        @JvmStatic
        @OnCreateLayout
        fun onCreateLayout(c: ComponentContext): ComponentLayout {
            return Column.create(c)
                    .child(Row.create(c)
                            .child(
                                    Text.create(c)
                                            .text("Current Weather")
                                            .build()
                            ).build())
                    .build()
        }
    }
}

Nothing gets generated. No CurrentWeatherTopLayout file created. If I create the same code in Java, it gets generated:

@LayoutSpec
class CurrentWeatherTopSpec {

    @OnCreateLayout
    static ComponentLayout onCreateLayout(ComponentContext context) {
        return Column.create(c)
                    .child(Row.create(c)
                            .child(
                                    Text.create(c)
                                            .text("Current Weather")
                                            .build()
                            ).build())
                    .build();
    }
}

Expected Behavior

I'd expect the corresponding generated component class.

Is this a bug or something I am doing wrong here?
I guess my real question here is what does Litho use to determine if the class is a proper @LayoutSpec. Also FYI im using kapt and using it for java files works fine, but when in Kotlin the processor does not work.

Support prop defaults being resource identifiers

Currently there is no way to specify the default value of a ResType prop using resource ids.

Say you declare a prop like the following

@Prop(optional = true, resType = ResType.DIMEN_SIZE) int textSize

You would like to be able to declare its default value as being a dimension resource.

@PropDefault protected static final int textSize = R.dimen.text_size_medium;

However that currently would not work as it would parse the resource id as an actual size value and not grab the size associated with the id.

This would most likely be solved by adding resType to the @PropDefault annotation.

@PropDefault(resType = ResType.DIMEN_SIZE) protected static final int textSize = R.dimen.text_size_medium;

Code pointers

https://github.com/facebook/litho/blob/master/litho-annotations/src/main/java/com/facebook/litho/annotations/Prop.java

https://github.com/facebook/litho/blob/master/litho-annotations/src/main/java/com/facebook/litho/annotations/PropDefault.java

https://github.com/facebook/litho/blob/master/litho-processor/src/main/java/com/facebook/litho/specmodels/generator/ComponentImplGenerator.java#L158

Point of contact: @emilsjolander

Fix touch dispatching to Text when it has View MountSpec sibling that's covering its parent fully

Consider the following layout:

Row.create(c)
    .child(FullBleedPhotoComponent.create(c)
        ....)
    .child(Text.create(c)
        .text(textWithClickableSpan)
        .withLayout()
        .positionType(ABSOLUTE)
        ...)
    .build();

Let's consider that FullBleedPhotoComponent is a mount spec defined in FullBleedPhotoComponentSpec that mounts a view stretching fully to its parent bounds and has click listener. And let's say we need to draw text that has ClickableSpan on top of FullBleedPhotoComponent. As we can see from the layout definition above the Text component is laid out in absolute position meaning the Text will be displayed on top of FullBleedPhotoComponent.

Problem

The issue with this scenario is that if we click Text the touch goes first to the view mounted by FullBleedPhotoComponent and its click listener is fired instead of Text's ClickableSpan.

Cause

As we can see from the layout above Text component is mounting its content (TextDrawable) directly to its parent host which is ComponentView. Correspondingly, when we add ClickableSpan text to Text component its click handler is registered with ComponentView and in the view hierarchy FullBleedPhotoComponent's view comes before its parent ComponentView, therefore when we click Text touch event goes first to FullBleedPhotoComponent's view where it is consumed.

Current workaround

We can add .wrapInView() to Text component so that its content lives in a separate view (ComponentHost) instead of living in its parent (ComponentView). In such case in the View hierarchy the wrapped view of Text component will be the first to get touch event which will result in desired behavior.

Solution

We should be able to detect this case and wrap the Text internally (short-term) or handle touch events by ourselves which is super complicated (long term).

Point of contact: @muraziz

Proposal to change component DI approach

The annotation processor currently has hidden support for dependency injection through DependencyInjectionHelper.

Because of this implementation detail, the generated component classes instantiate the specs object in the generated code, e.g.:

private ListItemSpec mSpec = new ListItemSpec();

This is quite unexpected. I expect that ListItemSpec never gets instantiated and the generated components will just call the static methods on the spec class directly.

I discovered this because I like to add private constructors to my utility classes, e.g.:

@LayoutSpec
final class ListItemSpec {

  ...

  private ListItemSpec() {
    throw new AssertionError("No instances.");
  }
}

This limitation also prevents Spec classes from being kotlin objects, e.g.:

Currently you must write:

@LayoutSpec
class ListItemSpec {

  companion object {
    @JvmStatic
    @OnCreateLayout
    fun onCreateLayout(
        c: ComponentContext,
        ...): ComponentLayout {
      ...
    }
  }
}

vs. what you could write (slightly more succinct):

@LayoutSpec
object ListItemSpec {

  @JvmStatic
  @OnCreateLayout
  fun onCreateLayout(
      c: ComponentContext,
      ...): ComponentLayout {
    ...
  }
}

I'd like to suggest an alternative approach for DI:

Keep the static methods in the specs and delete DependencyInjectionHelper (and all related code). If your spec needs dependencies, for whatever reason, you could pass an "Injector" through the Context as a system service. This is a relatively common pattern; you can see an example in the u2020 app.

Example usage would look like this:

@OnCreateLayout
static ComponentLayout onCreateLayout(
    ComponentContext c,
    ...) {
  Injector injector = Injector.obtain(c);
  Dep1 dep1 = injector.getDep1(); 
  Dep2 dep2 = injector.getDep2();
  // etc
  ...
}

Feel free to close if you have alternative plans for this or don't agree with the proposal. Also let me know if you want help with the implementation 👍

Expose API for virtual views to LayoutSpecs

We expose AccessibilityDelegate method callbacks in LayoutSpecs as event handlers. However, we're missing getAccessibilityNodeProvider. Proper implementations of node providers can be very stateful since they need to handle hover callbacks. In order to make this as stateless as possible and play nice with the framework, there are two possibilities:

  1. Do not expose the creation of the AccessibilityNodeInfoProviderCompat object directly, instead expose its API as event handlers.
  2. Leverage the fact that ComponentAccessibilityDelegate extends ExploreByTouchHelper and expose its virtual view related abstract methods as event handlers. This would also require that we make sure that the dispatchHoverEvent callback is extended to arbitrary views and not just ComponentHost

Regardless of which idea we choose, there are a couple of things to consider:

  1. We should default to returning null in getAccessibilityNodeProvider. Returning a non-null value changes how/whether certain accessibility events are routed to the right callbacks
  2. MountSpecs that implement virtual view hierarchies, require we return a special node provider. If no event handler for node provider is set on the LayoutSpec, we should respect the MountSpec's original behavior, otherwise, we should override it. See ComponentAccessibilityDelegate#onInitializeAccessibilityNodeInfo for an example.

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentAccessibilityDelegate.java#L62

Point of contact: @muraziz

Make clipbounds behavior consistent

We want to align with web behavior here and not clip bounds of any child. Right now since we might have ComponentHosts wrapping MountItems we might end clipping bounds in some cases.
In cases where there a drawable is not wrapped in a ComponentHost on the other hand we don't clip.

Let's get rid of this inconsistency by never clipping bounds within a LithoView.
We might also want to expose an API to re-enable bounds clipping in some cases.

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentHost.java#L98

Point of Contact: @pasqualeanatriello

Post sync state updates instead of immediately starting

Sync state updates should be posted on the main thread instead of executed immediately.
If a sync state update is triggered while we're executing a layout calculation, then the state update would trigger another layout calculation. We should instead post any sync state updates on the main thread and wait for the current layout calculation to be finished before starting a new one.

Code pointer

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentTree.java#L677

Point of contact: @mihaelao

Add ability to send imperative commands with new Trigger API

The Event api gives us the ability for child components to give callback to their parents. However in some cases we have the need for components to trigger actions on their children. The typical example is to issue a scrollToTop command on a Recycler component.

Let's call this Triggers, i.e. the ability perform imperative commands on mounted views/drawables in a type-safe way. This API would look very similar to the Event API but act in the inverse direction.

Point of contact: @muraziz

Border width is not respected in leaf nodes

When we set border color and border width on container components (such as Row/Column or any layout spec), it is drawn correctly respecting the boundaries of child items. However, when we set border on a leaf node (such as TextSpec or any mount spec) the border is overlapping with the actual content of that leaf node. Let's see these two cases:

Case 1: This works fine, i.e. the text content is not overlapped with border:

Column.create(c)
             .child(Text.create(c)
                        .text("Lorem ipsum")
                        .textSizeDip(18))
             .borderColor(Color.GREEN)
             .borderWidthDip(YogaEdge.LEFT, 6)
             .borderWidthDip(YogaEdge.TOP, 6)

Case 2: Here the border is overlapping with the content:

Text.create(c)
             .text("Lorem ipsum")
             .textSizeDip(18)
             .withLayout()
             .borderColor(Color.GREEN)
             .borderWidthDip(YogaEdge.LEFT, 6)
             .borderWidthDip(YogaEdge.TOP, 6)

Possible solution could be to offset the LayoutOutput's bounds when it's calculated (https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/LayoutState.java#L215) in the same way as we do with padding.

Point of contact: @muraziz

File title in sample code

At the moment we have the file name in square brackets as the first line of the sample code in "Getting Started". Can we get it as some kind of title, separate from code?

Support layout diffing on nested trees with double measurements

Right now we only cache one measure in DiffNode. This means that if a node is measured twice we still end up measuring as the first of the two measurements won't match the content of DiffNode. We need to store all the measurements a node went through during a layout pass and check them in order.

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentLifecycle.java#L144

Point of contact: @pasqualeanatriello

AccessibilityNodeInfo objects created by Components do not set className

Debugging accessibility hierarchies for Litho is specially difficult since unless it's explicitly overridden, we'll never set a class name in onInitializeAccessibilityNodeInfo, which results in the node being labelled with a generic View class name. We should instead figure out a way to report the name of the spec that's being rendered or at the very least default to label the node as ComponentHost so it's clear that said node represents a Component.

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentAccessibilityDelegate.java#L62

Point of contact: @muraziz

Support setting themable styles on Components

Right now we only apply styles if the user explicitly sets one in the component's create() method. However, we want to be able to set this in the Component itself so that you can make components automatically gather styles from the theme.

The work here is to automatically get the theme off the context and set the appropriate props on mount items (Text, etc) if they are not otherwise overridden.

Point of contact @emilsjolander

Pass style res to @OnCreateMountContent

There are Views in Android that only accept some attributes when the view is created and can't be changed after that (e.g. android:textCursorDrawable in EditText). We need a way to pass down these attribute sets into @OnCreateMountContent to allow android widgets to be constructed with them.

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentLifecycle.java#L414

https://github.com/facebook/litho/blob/master/litho-processor/src/main/java/com/facebook/litho/specmodels/processor/MountSpecModelFactory.java#L104

Point of contact: @emilsjolander

Buck fetch fails for support libraries

Issues and Steps to Reproduce

Buck fetch is unable to download android support libraries.

To repro, run buck fetch sample

Expected Behavior

Buck should fetch binaries.

Actual Behavior

Buck exits error code 1, with errors

Not using buckd because watchman isn't installed.
Unable to download: mvn:com.android.support:support-core-utils:aar:25.3.1
Unable to download: mvn:com.android.support:support-core-ui:aar:25.3.1
Unable to download: mvn:com.android.support:support-annotations:jar:25.3.1
Unable to download: mvn:com.android.support:support-fragment:aar:25.3.1
Unable to download: mvn:com.android.support:support-v4:aar:25.3.1
Unable to download: mvn:com.android.support:recyclerview-v7:aar:25.3.1
Unable to download: mvn:com.android.support:appcompat-v7:aar:25.3.1
Unable to download: mvn:com.android.support:support-vector-drawable:aar:25.3.1
Unable to download: mvn:com.android.support:support-compat:aar:25.3.1
BUILD FAILED: //lib/android-support:android-support-compat.aar failed with exit code -1:
curl
[-] PROCESSING BUCK FILES...FINISHED 1.0s [100%] 🐳  New buck daemon
[-] DOWNLOADING... (0.00 B/S AVG, TOTAL: 0.00 B, 0 Artifacts)
[-] BUILDING...FINISHED 11.2s [100%] (23/23 JOBS, 0 UPDATED, 0 [0.0%] CACHE MISS)

Buck doctor output: http://filebin.ca/3JauRmg0MBd4/defect_report2771786254819049122.zip
Buck version buck version 22a78d57a01d6d3b7a107fff010721f4a756037d
OS: Ubuntu 14.04

UnsatisfiedLinkError: dlopen failed

Issues and Steps to Reproduce

java.lang.UnsatisfiedLinkError: dlopen failed: "/data/data/com.nullcognition.litho/lib-main/libgnustl_shared.so" is 32-bit instead of 64-bit
Create a project, import all gradle dependencies(uncomment testing dep.), add App application class, change manifest application name to App class, init SoLoader in App, add Testing your Installation code, run.

Expected Behavior

Now, when you run the app you should see “Hello World!” displayed on the screen.

Link to Code

https://github.com/ersin-ertan/android-ui/tree/master/litho

Related Info

SoLoader issue for 32 and 64-bit @rspencer01
Device: Galaxy Tab S2

Sample app crashes when browsing the view hierarchy in Stetho

Issues and Steps to Reproduce

Using an Nexus 6P on Android 7.1.2:

  1. ./gradlew :sample:installDebug
  2. Open the sample app, navigate to the Lithography list
  3. Connect to Stetho
  4. Click the search button in Stetho view hierachy
  5. Tap on the first image in the list

Expected Behavior

No crash

Link to Code

04-19 15:26:47.672 30649-30649/com.facebook.samples.litho E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.facebook.samples.litho, PID: 30649
    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/facebook/litho/annotations/Prop;
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyles(ComponentStethoNodeDescriptor.java:193)
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyles(ComponentStethoNodeDescriptor.java:26)
        at com.facebook.stetho.inspector.elements.AbstractChainedDescriptor.getStyles(AbstractChainedDescriptor.java:154)
        at com.facebook.stetho.inspector.elements.Document.getElementStyles(Document.java:153)
        at com.facebook.stetho.inspector.protocol.module.CSS$2$1.store(CSS.java:144)
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyleRuleNames(ComponentStethoNodeDescriptor.java:152)
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyleRuleNames(ComponentStethoNodeDescriptor.java:26)
        at com.facebook.stetho.inspector.elements.AbstractChainedDescriptor.getStyleRuleNames(AbstractChainedDescriptor.java:145)
        at com.facebook.stetho.inspector.elements.Document.getElementStyleRuleNames(Document.java:147)
        at com.facebook.stetho.inspector.protocol.module.CSS$2.run(CSS.java:118)
        at com.facebook.stetho.common.android.HandlerUtil$2.onRun(HandlerUtil.java:90)
        at com.facebook.stetho.common.android.HandlerUtil$2.onRun(HandlerUtil.java:87)
        at com.facebook.stetho.common.android.HandlerUtil$WaitableRunnable.run(HandlerUtil.java:109)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6121)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "com.facebook.litho.annotations.Prop" on path: DexPathList[[zip file "/data/app/com.facebook.samples.litho-1/base.apk"],nativeLibraryDirectories=[/data/app/com.facebook.samples.litho-1/lib/arm64, /system/fake-libs64, /data/app/com.facebook.samples.litho-1/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyles(ComponentStethoNodeDescriptor.java:193) 
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyles(ComponentStethoNodeDescriptor.java:26) 
        at com.facebook.stetho.inspector.elements.AbstractChainedDescriptor.getStyles(AbstractChainedDescriptor.java:154) 
        at com.facebook.stetho.inspector.elements.Document.getElementStyles(Document.java:153) 
        at com.facebook.stetho.inspector.protocol.module.CSS$2$1.store(CSS.java:144) 
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyleRuleNames(ComponentStethoNodeDescriptor.java:152) 
        at com.facebook.litho.ComponentStethoNodeDescriptor.onGetStyleRuleNames(ComponentStethoNodeDescriptor.java:26) 
        at com.facebook.stetho.inspector.elements.AbstractChainedDescriptor.getStyleRuleNames(AbstractChainedDescriptor.java:145) 
        at com.facebook.stetho.inspector.elements.Document.getElementStyleRuleNames(Document.java:147) 
        at com.facebook.stetho.inspector.protocol.module.CSS$2.run(CSS.java:118) 
        at com.facebook.stetho.common.android.HandlerUtil$2.onRun(HandlerUtil.java:90) 
        at com.facebook.stetho.common.android.HandlerUtil$2.onRun(HandlerUtil.java:87) 
        at com.facebook.stetho.common.android.HandlerUtil$WaitableRunnable.run(HandlerUtil.java:109) 
        at android.os.Handler.handleCallback(Handler.java:751) 
        at android.os.Handler.dispatchMessage(Handler.java:95) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6121) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779) 

Recycle StateUpdate objects

We should have a pool of StateUpdate objects instead of allocating a new one for each state update creation. The state update objects don't live past a layout calculation, so we can recycle them then. Each generated lifecycle would need a pool instance types to their specific StateUpdate subclass and instead of calling new StateUpdate() we would need to acquire / release these objects from / to the pool. This is very similar to how we currently generate pools for the specific builder objects.

Code pointers

https://github.com/facebook/litho/blob/master/litho-processor/src/main/java/com/facebook/litho/specmodels/generator/ComponentImplGenerator.java#L320

https://github.com/facebook/litho/blob/master/litho-processor/src/main/java/com/facebook/litho/specmodels/generator/BuilderGenerator.java#L93

Point of contact: @mihaelao

Use a ComponentContext pool for scoping

Each Component is scoped to a ComponentContext. This helps us identify, given a ComponentContext, which component we are trying to apply a state update to.
Every time a component gets scoped to a component context, a new component context instance is created and associated with that component.
We should use a ComponentContext pool instead of allocating a new ComponentContext each time we want to have a ComponentContext -> Component association

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/Component.java#L303

Point of contact: @mihaelao

Don't re-render subtrees who's size don't change on state update

If we know that the size of a subtree is not changing when we update its state, then we definitely don't need to relayout the whole tree, which should save a lot of work.

We could check for the prefix of the global key of a root and if we don't have any pending state updates for it then just copy everything from the old tree.

Code pointers

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/ComponentTree.java

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/Component.java#L298

https://github.com/facebook/litho/blob/master/litho-core/src/main/java/com/facebook/litho/LayoutState.java

Point of contact: @mihaelao

Error trying to run sample

I'm following the instructions in the README to run the sample

$ buck fetch sample
Error: cannot locate a .buckversion file in the current directory or any parent directories.
Are you sure you are inside a valid repository?
caabernathy-mbp:litho caabernathy$ buck fetch sample
Using watchman.
[-] PROCESSING BUCK FILES...FINISHED 0.0s [100%] 🐳  New buck daemon
[+] DOWNLOADING... (0.00 B/S, TOTAL: 0.00 B, 0 Artifacts)
[+] BUILDING...2.2s
BUILD FAILED: Couldn't get dependency '//sample/src/main/java/com/facebook/samples/litho:litho' of target '//sample:sample':
Parse error for build file /Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/sample/src/main/java/com/facebook/samples/litho/BUCK:
TypeError: android_library() got an unexpected keyword argument 'plugins'
Call stack:
  File "/Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/.buckd/tmp/buck_run.qTVfjj/buck_python_program8423449095434172792/python_bundle.zip/buck_parser/buck.py", line 1271, in process_with_diagnostics
    diagnostics=diagnostics)
  File "/Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/.buckd/tmp/buck_run.qTVfjj/buck_python_program8423449095434172792/python_bundle.zip/buck_parser/buck.py", line 1161, in process
    implicit_includes=self._implicit_includes)
  File "/Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/.buckd/tmp/buck_run.qTVfjj/buck_python_program8423449095434172792/python_bundle.zip/buck_parser/buck.py", line 1094, in _process_build_file
    implicit_includes=implicit_includes)
  File "/Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/.buckd/tmp/buck_run.qTVfjj/buck_python_program8423449095434172792/python_bundle.zip/buck_parser/buck.py", line 1016, in _process
    exec(code, module.__dict__)
  File "/Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/sample/src/main/java/com/facebook/samples/litho/BUCK", line 28
    COMPONENTS_YOGAPREBUILT_TARGET,
  File "/Users/caabernathy/Facebook/OpenSource/Litho/Code/litho/.buckd/tmp/buck_run.qTVfjj/buck_python_program8423449095434172792/python_bundle.zip/buck_parser/buck.py", line 149, in invoke
    return self.func(*args, **updated_kwargs)

Note, I'm using the open source version of Buck:

$ buck --version
buck version v2016.11.11.01

NoClassDefFoundError: com/facebook/jni/Countable when load liblib_fb_fbjni.so in Andorid 4.1

Issues and Steps to Reproduce

  1. Create new android project.
  2. Add dependencies to gradle:
    compile 'com.facebook.litho:litho-core:0.2.0'
    compile 'com.facebook.litho:litho-widget:0.2.0'
    provided 'com.facebook.litho:litho-annotations:0.2.0'

    annotationProcessor 'com.facebook.litho:litho-processor:0.2.0'

    // SoLoader
    compile 'com.facebook.soloader:soloader:0.2.0'
  1. Create simple text component in MainActivity:
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final ComponentContext context = new ComponentContext(this);

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

    setContentView(LithoView.create(context, component));
}
  1. Create custom Application class:
public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, false);
    }
}
  1. Run app

Expected Behavior

App should display hello world

Link to Code

Stacktrace: https://pastebin.com/yjSNTe6T

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.