Epoxy


Source link: https://github.com/airbnb/epoxy

Epoxy

Epoxy is an Android library for building complex screens in a RecyclerView. Models are automatically generated from custom views, databinding layouts, or Litho components via annotation processing. These models are then used in an EpoxyController to declare what items to show in the RecyclerView.

This abstracts the boilerplate of view holders, diffing items and binding payload changes, item types, item ids, span counts, and more, in order to simplify building screens with multiple view types. Additionally, Epoxy adds support for saving view state and automatic diffing of item changes.

We developed Epoxy at Airbnb to simplify the process of working with RecyclerViews, and to add the missing functionality we needed. We now use Epoxy for most of the main screens in our app and it has improved our developer experience greatly.

Installation

Gradle is the only supported build configuration, so just add the dependency to your project build.gradle file:

dependencies {

compile 'com.airbnb.android:epoxy:2.7.2'
// Add the annotation processor if you are using Epoxy's annotations (recommended)
annotationProcessor 'com.airbnb.android:epoxy-processor:2.7.2' 
}

Kotlin

If you are using Kotlin you should also add

apply plugin: 'kotlin-kapt'  kapt {

  correctErrorTypes = true 
}
 

so that AutoModel annotations work properly. More information here

Also, make sure to use kapt instead of annotationProcessor in your dependencies in the build.gradle file.

Library Projects

To use Epoxy in a library or module add Butterknife's gradle plugin to your buildscript.

buildscript {

repositories {

  mavenCentral()
 
}

dependencies {

  classpath 'com.jakewharton:butterknife-gradle-plugin:8.7.0'

}
 
}

and then apply it in your module:

apply plugin: 'com.android.library' apply plugin: 'com.jakewharton.butterknife'

Now make sure you use R2 instead of R inside all Epoxy annotations.

@ModelView(defaultLayout = R2.layout.view_holder_header) public class HeaderView extends LinearLayout {

 .... 
}

Basic Usage

There are two main components of Epoxy:

  1. The EpoxyModels that describe how your views should be displayed in the RecyclerView.
  2. The EpoxyController where the models are used to describe what items to show and with what data.

Creating Models

Epoxy generates models for you based on your view or layout. Generated model classes are suffixed with an underscore ( _) are are used directly in your EpoxyController classes.

From Custom Views

Add the @ModelView annotation on a view class. Then, add a "prop" annotation on each setter method to mark it as a property for the model.

@ModelView(autoLayout = Size.MATCH_WIDTH_WRAP_HEIGHT) public class HeaderView extends LinearLayout {

 ... // Initialization omitted
 @TextProp
public void setTitle(CharSequence text) {

  titleView.setText(text);

}
 
}

A HeaderViewModel_ is then generated in the same package.

More Details

From DataBinding

If you use Android DataBinding you can simply set up your xml layouts like normal:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
  <data>

<variable name="title" type="String" />
  </data>

<TextView

android:layout_width="120dp"

android:layout_height="40dp"

android:text="@{
title
}
" /> </layout>

Then, create a package-info.java file in any package and add an EpoxyDataBindingLayouts annotation to declare your databinding layouts.

@EpoxyDataBindingLayouts({
R.layout.header_view, ... // other layouts 
}
) package com.airbnb.epoxy.sample;  import com.airbnb.epoxy.EpoxyDataBindingLayouts; import com.airbnb.epoxy.R;

From this layout name Epoxy generates a HeaderViewBindingModel_.

More Details

From ViewHolders

If you use xml layouts without databinding you can create a model class to do the binding.

@EpoxyModelClass(layout = R.layout.header_view) public abstract class HeaderModel extends EpoxyModelWithHolder<Holder> {

@EpoxyAttribute String title;
 @Override
public void bind(Holder holder) {

  holder.header.setText(title);

}

 static class Holder extends BaseEpoxyHolder {

  @BindView(R.id.text) TextView header;

}
 
}

A HeaderModel_ class is generated that subclasses HeaderModel and implements the model details.

More Details

Using your models in a controller

A controller defines what items should be shown in the RecyclerView, by adding the corresponding models in the desired order.

The controller's buildModels method declares which items to show. You are responsible for calling requestModelBuild whenever your data changes, which triggers buildModels to run again. Epoxy tracks changes in the models and automatically binds and updates views.

As an example, our PhotoController shows a header, a list of photos, and a loader (if more photos are being loaded). The controller's setData(photos, loadingMore) method is called whenever photos are loaded, which triggers a call to buildModels so models representing the state of the new data can be built.

public class PhotoController extends Typed2EpoxyController<List<Photo>, Boolean> {

  @AutoModel HeaderModel_ headerModel;
  @AutoModel LoaderModel_ loaderModel;

@Override
  protected void buildModels(List<Photo> photos, Boolean loadingMore) {

 headerModel

  .title("My Photos")

  .description("My album description!")

  .addTo(this);

  for (Photo photo : photos) {

new PhotoModel()

.id(photo.id())

.url(photo.url())

.addTo(this);

 
}

  loaderModel

  .addIf(loadingMore, this);

  
}

}

Or with Kotlin

An extension function is generated for each model so we can write this:

class PhotoController : Typed2EpoxyController<List<Photo>, Boolean>() {

override fun buildModels(photos: List<Photo>, loadingMore: Boolean) {

header {

 id("header")

 title("My Photos")

 description("My album description!")

}

 photos.forEach {

 photoView {

  id(it.id())

  url(it.url())

 
}

}

 if (loadingMore) loaderView {
 id("loader") 
}

  
}
 
}

Integrating with RecyclerView

Get the backing adapter off the EpoxyController to set up your RecyclerView:

MyController controller = new MyController();
 recyclerView.setAdapter(controller);
  // Request a model build whenever your data changes controller.requestModelBuild();
  // Or if you are using a TypedEpoxyController controller.setData(myData);

If you are using the EpoxyRecyclerView integration is easier.

epoxyRecyclerView.setControllerAndBuildModels(new MyController());
  // Request a model build on the recyclerview when data changes epoxyRecyclerView.requestModelBuild();

Kotlin

Or use Kotlin Extensions to simplify further and remove the need for a controller class.

epoxyRecyclerView.withModels {

header {

 id("header")

 title("My Photos")

 description("My album description!")

}

 photos.forEach {

 photoView {

  id(it.id())

  url(it.url())

 
}

}

 if (loadingMore) loaderView {
 id("loader") 
}

  
}
 
}

More Reading

And that's it! The controller's declarative style makes it very easy to visualize what the RecyclerView will look like, even when many different view types or items are used. Epoxy handles everything else. If a view only partially changes, such as the description, only that new value is set on the view, so the system is very efficient

Epoxy handles much more than these basics, and is highly configurable. See the wiki for in depth documentation.

Documentation

See examples and browse complete documentation at the Epoxy Wiki

If you still have questions, feel free to create a new issue.

Min SDK

We support a minimum SDK of 14. However, Epoxy is based on the v7 support libraries so it should work with lower versions if you care to override the min sdk level in the manifest.

If you are using the optional Litho integration then the min SDK is 15 due to Litho's SDK requirement.

Contributing

Pull requests are welcome! We'd love help improving this library. Feel free to browse through open issues to look for things that need work. If you have a feature request or bug, please open a new issue so we can track it.

License

Copyright 2016 Airbnb, Inc.  Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License. You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 

Resources

Drop-down menu for Android which allows to filter the items using multiple condition.

Animated CircleMenu for wear devices.

RestorableSQLiteDatabase is a wrapper to replicate android's SQLiteDatabase class with restoring capability. This wrapper makes it possible to undo changes made after execution of SQL queries.

Implementation of SeekBarPreference with material design.

Mugen is a microlibrary for implementing infinite scroll on Android.

This little tool generates wrappers for your SharedPreferences, so you can benefit from compile time verification and code completion in your IDE. You also get nice singletons for free.

Topics


2D Engines   3D Engines   9-Patch   Action Bars   Activities   ADB   Advertisements   Analytics   Animations   ANR   AOP   API   APK   APT   Architecture   Audio   Autocomplete   Background Processing   Backward Compatibility   Badges   Bar Codes   Benchmarking   Bitmaps   Bluetooth   Blur Effects   Bread Crumbs   BRMS   Browser Extensions   Build Systems   Bundles   Buttons   Caching   Camera   Canvas   Cards   Carousels   Changelog   Checkboxes   Cloud Storages   Color Analysis   Color Pickers   Colors   Comet/Push   Compass Sensors   Conferences   Content Providers   Continuous Integration   Crash Reports   Credit Cards   Credits   CSV   Curl/Flip   Data Binding   Data Generators   Data Structures   Database   Database Browsers   Date &   Debugging   Decompilers   Deep Links   Dependency Injections   Design   Design Patterns   Dex   Dialogs   Distributed Computing   Distribution Platforms   Download Managers   Drawables   Emoji   Emulators   EPUB   Equalizers &   Event Buses   Exception Handling   Face Recognition   Feedback &   File System   File/Directory   Fingerprint   Floating Action   Fonts   Forms   Fragments   FRP   FSM   Functional Programming   Gamepads   Games   Geocaching   Gestures   GIF   Glow Pad   Gradle Plugins   Graphics   Grid Views   Highlighting   HTML   HTTP Mocking   Icons   IDE   IDE Plugins   Image Croppers   Image Loaders   Image Pickers   Image Processing   Image Views   Instrumentation   Intents   Job Schedulers   JSON   Keyboard   Kotlin   Layouts   Library Demos   List View   List Views   Localization   Location   Lock Patterns   Logcat   Logging   Mails   Maps   Markdown   Mathematics   Maven Plugins   MBaaS   Media   Menus   Messaging   MIME   Mobile Web   Native Image   Navigation   NDK   Networking   NFC   NoSQL   Number Pickers   OAuth   Object Mocking   OCR Engines   OpenGL   ORM   Other Pickers   Parallax List   Parcelables   Particle Systems   Password Inputs   PDF   Permissions   Physics Engines   Platforms   Plugin Frameworks   Preferences   Progress Indicators   ProGuard   Properties   Protocol Buffer   Pull To   Purchases   Push/Pull   QR Codes   Quick Return   Radio Buttons   Range Bars   Ratings   Recycler Views   Resources   REST   Ripple Effects   RSS   Screenshots   Scripting   Scroll Views   SDK   Search Inputs   Security   Sensors   Services   Showcase Views   Signatures   Sliding Panels   Snackbars   SOAP   Social Networks   Spannable   Spinners   Splash Screens   SSH   Static Analysis   Status Bars   Styling   SVG   System   Tags   Task Managers   TDD &   Template Engines   Testing   Testing Tools   Text Formatting   Text Views   Text Watchers   Text-to   Toasts   Toolkits For   Tools   Tooltips   Trainings   TV   Twitter   Updaters   USB   User Stories   Utils   Validation   Video   View Adapters   View Pagers   Views   Watch Face   Wearable Data   Wearables   Weather   Web Tools   Web Views   WebRTC   WebSockets   Wheel Widgets   Wi-Fi   Widgets   Windows   Wizards   XML   XMPP   YAML   ZIP Codes