[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-4175":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":17,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":16,"starSnapshotCount":16,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},4175,"epoxy","airbnb\u002Fepoxy","airbnb","Epoxy is an Android library for building complex screens in a RecyclerView","https:\u002F\u002Fgoo.gl\u002FeIK82p",null,"Java",8557,732,151,299,0,1,39.6,"Apache License 2.0",false,"master",true,[],"2026-06-12 02:00:59","[![Build Status](https:\u002F\u002Ftravis-ci.com\u002Fairbnb\u002Fepoxy.svg?branch=master)](https:\u002F\u002Ftravis-ci.com\u002Fgithub\u002Fairbnb\u002Fepoxy)\n[![Maven Central](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fcom.airbnb.android\u002Fepoxy\u002Fbadge.svg)](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fcom.airbnb.android\u002Fepoxy)\n[![GitHub license](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Flicense\u002Fairbnb\u002Fepoxy)](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fblob\u002Fmaster\u002FLICENSE)\n![GitHub contributors](https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fcontributors\u002Fairbnb\u002Fepoxy)\n\n# Epoxy\n\nEpoxy is an Android library for building complex screens in a RecyclerView. Models are automatically generated from custom views or databinding layouts via annotation processing. These models are then used in an EpoxyController to declare what items to show in the RecyclerView.\n\nThis 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.\n\n[We developed Epoxy at Airbnb](https:\u002F\u002Fmedium.com\u002Fairbnb-engineering\u002Fepoxy-airbnbs-view-architecture-on-android-c3e1af150394#.xv4ymrtmk) 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.\n\n* [Installation](#installation)\n* [Basic Usage](#basic-usage)\n* [Documentation](#documentation)\n* [Min SDK](#min-sdk)\n* [Contributing](#contributing)\n* [Sample App](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FSample-App)\n\n## Installation\n\nGradle is the only supported build configuration, so just add the dependency to your project `build.gradle` file:\n\n```groovy\ndependencies {\n  implementation \"com.airbnb.android:epoxy:$epoxyVersion\"\n  \u002F\u002F Add the annotation processor if you are using Epoxy's annotations (recommended)\n  annotationProcessor \"com.airbnb.android:epoxy-processor:$epoxyVersion\"\n}\n```\n\nReplace the variable `$epoxyVersion` with the latest version : [![Maven Central](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fcom.airbnb.android\u002Fepoxy\u002Fbadge.svg)](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fcom.airbnb.android\u002Fepoxy)\n\nSee the [releases page](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Freleases) for up to date release versions and details\n\n#### Kotlin with KAPT\nIf you are using Kotlin with KAPT you should also add\n```groovy\napply plugin: 'kotlin-kapt'\n\nkapt {\n    correctErrorTypes = true\n}\n```\n\nso that `AutoModel` annotations work properly. More information [here](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FEpoxy-Controller#usage-with-kotlin)\n\nAlso, make sure to use `kapt` instead of `annotationProcessor` in your dependencies in the `build.gradle` file.\n\n#### Kotlin with KSP (Recommended)\nKSP (Kotlin Symbol Processing) is recommended over KAPT as it is significantly faster.\n\nAdd the KSP plugin to your root `build.gradle`:\n```groovy\nplugins {\n    id 'com.google.devtools.ksp' version \"$KSP_VERSION\" apply false\n}\n```\n\nThen apply it in your module's `build.gradle`:\n```groovy\nplugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n    id 'com.google.devtools.ksp'\n}\n\ndependencies {\n    implementation \"com.airbnb.android:epoxy:$epoxyVersion\"\n    ksp \"com.airbnb.android:epoxy-processor:$epoxyVersion\"\n}\n```\n\nYou can configure KSP processor options:\n```groovy\nksp {\n    \u002F\u002F Validation and debugging\n    arg(\"validateEpoxyModelUsage\", \"true\")                \u002F\u002F Validate model usage at runtime (default: true)\n    arg(\"logEpoxyTimings\", \"false\")                       \u002F\u002F Log annotation processing timings (default: false)\n\n    \u002F\u002F Code generation options\n    arg(\"epoxyDisableGenerateReset\", \"false\")             \u002F\u002F Disable reset() method generation (default: false)\n    arg(\"epoxyDisableGenerateGetters\", \"false\")           \u002F\u002F Disable getter generation (default: false)\n    arg(\"epoxyDisableGenerateOverloads\", \"false\")         \u002F\u002F Disable builder overload generation (default: false)\n    arg(\"disableEpoxyKotlinExtensionGeneration\", \"false\") \u002F\u002F Disable Kotlin extension generation (default: false)\n    arg(\"epoxyDisableDslMarker\", \"false\")                 \u002F\u002F Disable DSL marker annotation (default: false)\n\n    \u002F\u002F Model requirements\n    arg(\"requireHashCodeInEpoxyModels\", \"false\")          \u002F\u002F Require hashCode\u002Fequals in models (default: false)\n    arg(\"requireAbstractEpoxyModels\", \"false\")            \u002F\u002F Require abstract model classes (default: false)\n    arg(\"implicitlyAddAutoModels\", \"false\")               \u002F\u002F Auto-add models to controllers (default: false)\n}\n```\n\n**Important:** DataBinding models are **not supported** with KSP, as Android's DataBinding library itself uses KAPT. If you need DataBinding support, you must continue using KAPT. For custom views with `@ModelView` or ViewHolder models, KSP works perfectly.\n\nSee the [epoxy-kspsample](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Ftree\u002Fmaster\u002Fepoxy-kspsample) module for a complete working example.\n\n## Library Projects\nIf you are using layout resources in Epoxy annotations then for library projects add [Butterknife's gradle plugin](https:\u002F\u002Fgithub.com\u002FJakeWharton\u002Fbutterknife#library-projects) to your `buildscript`.\n\n```groovy\nbuildscript {\n  repositories {\n    mavenCentral()\n  }\n  dependencies {\n    classpath 'com.jakewharton:butterknife-gradle-plugin:10.1.0'\n  }\n}\n```\n\nand then apply it in your module:\n```groovy\napply plugin: 'com.android.library'\napply plugin: 'com.jakewharton.butterknife'\n```\n\nNow make sure you use R2 instead of R inside all Epoxy annotations.\n```java\n@ModelView(defaultLayout = R2.layout.view_holder_header)\npublic class HeaderView extends LinearLayout {\n   ....\n}\n```\n\nThis is not necessary if you don't use resources as annotation parameters, such as with [custom view models](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FGenerating-Models-from-View-Annotations).\n\n## Basic Usage\nThere are two main components of Epoxy:\n\n1. The `EpoxyModel`s that describe how your views should be displayed in the RecyclerView.\n2. The `EpoxyController` where the models are used to describe what items to show and with what data.\n\n### Creating Models\nEpoxy generates models for you based on your view or layout. Generated model classes are suffixed with an underscore (`_`) are used directly in your EpoxyController classes.\n\n#### From Custom Views\nAdd 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.\n\n```java\n@ModelView(autoLayout = Size.MATCH_WIDTH_WRAP_HEIGHT)\npublic class HeaderView extends LinearLayout {\n\n  ... \u002F\u002F Initialization omitted\n\n  @TextProp\n  public void setTitle(CharSequence text) {\n    titleView.setText(text);\n  }\n}\n```\n\nA `HeaderViewModel_` is then generated in the same package.\n\n[More Details](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FGenerating-Models-from-View-Annotations)\n\n#### From DataBinding\n\nIf you use Android DataBinding you can simply set up your xml layouts like normal:\n\n```xml\n\u003Clayout xmlns:android=\"http:\u002F\u002Fschemas.android.com\u002Fapk\u002Fres\u002Fandroid\">\n    \u003Cdata>\n        \u003Cvariable name=\"title\" type=\"String\" \u002F>\n    \u003C\u002Fdata>\n\n    \u003CTextView\n        android:layout_width=\"120dp\"\n        android:layout_height=\"40dp\"\n        android:text=\"@{title}\" \u002F>\n\u003C\u002Flayout>\n```\n\nThen, create an interface or class in any package and add an `EpoxyDataBindingLayouts` annotation to declare your databinding layouts.\n\n```java\npackage com.airbnb.epoxy.sample;\n\nimport com.airbnb.epoxy.EpoxyDataBindingLayouts;\n\n@EpoxyDataBindingLayouts({R.layout.header_view, ... \u002F\u002F other layouts })\ninterface EpoxyConfig {}\n```\n\nFrom this layout name Epoxy generates a `HeaderViewBindingModel_`.\n\n[More Details](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FData-Binding-Support)\n\n#### From ViewHolders\nIf you use xml layouts without databinding you can create a model class to do the  binding.\n\n```java\n@EpoxyModelClass(layout = R.layout.header_view)\npublic abstract class HeaderModel extends EpoxyModelWithHolder\u003CHolder> {\n  @EpoxyAttribute String title;\n\n  @Override\n  public void bind(Holder holder) {\n    holder.header.setText(title);\n  }\n\n  static class Holder extends BaseEpoxyHolder {\n    @BindView(R.id.text) TextView header;\n  }\n}\n```\n\nA `HeaderModel_` class is generated that subclasses HeaderModel and implements the model details.\n\n[More Details](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FViewHolder-Models)\n\n### Using your models in a controller\n\nA controller defines what items should be shown in the RecyclerView, by adding the corresponding models in the desired order.\n\n 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.\n\nAs 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.\n\n```java\npublic class PhotoController extends Typed2EpoxyController\u003CList\u003CPhoto>, Boolean> {\n    @AutoModel HeaderModel_ headerModel;\n    @AutoModel LoaderModel_ loaderModel;\n\n    @Override\n    protected void buildModels(List\u003CPhoto> photos, Boolean loadingMore) {\n      headerModel\n          .title(\"My Photos\")\n          .description(\"My album description!\")\n          .addTo(this);\n\n      for (Photo photo : photos) {\n        new PhotoModel()\n           .id(photo.id())\n           .url(photo.url())\n           .addTo(this);\n      }\n\n      loaderModel\n          .addIf(loadingMore, this);\n    }\n  }\n```\n\n#### Or with Kotlin\nAn extension function is generated for each model so we can write this:\n```kotlin\nclass PhotoController : Typed2EpoxyController\u003CList\u003CPhoto>, Boolean>() {\n\n    override fun buildModels(photos: List\u003CPhoto>, loadingMore: Boolean) {\n        header {\n            id(\"header\")\n            title(\"My Photos\")\n            description(\"My album description!\")\n        }\n\n        photos.forEach {\n            photoView {\n                id(it.id())\n                url(it.url())\n            }\n        }\n\n        if (loadingMore) loaderView { id(\"loader\") }\n    }\n}\n```\n\n### Integrating with RecyclerView\n\nGet the backing adapter off the EpoxyController to set up your RecyclerView:\n```java\nMyController controller = new MyController();\nrecyclerView.setAdapter(controller.getAdapter());\n\n\u002F\u002F Request a model build whenever your data changes\ncontroller.requestModelBuild();\n\n\u002F\u002F Or if you are using a TypedEpoxyController\ncontroller.setData(myData);\n```\n\nIf you are using the [EpoxyRecyclerView](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FEpoxyRecyclerView) integration is easier.\n\n```java\nepoxyRecyclerView.setControllerAndBuildModels(new MyController());\n\n\u002F\u002F Request a model build on the recyclerview when data changes\nepoxyRecyclerView.requestModelBuild();\n```\n\n#### Kotlin\nOr use [Kotlin Extensions](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki\u002FEpoxyRecyclerView#kotlin-extensions) to simplify further and remove the need for a controller class.\n```kotlin\nepoxyRecyclerView.withModels {\n        header {\n            id(\"header\")\n            title(\"My Photos\")\n            description(\"My album description!\")\n        }\n\n        photos.forEach {\n            photoView {\n                id(it.id())\n                url(it.url())\n            }\n        }\n\n        if (loadingMore) loaderView { id(\"loader\") }\n    }\n}\n```\n\n\n### More Reading\nAnd 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\n\nEpoxy handles much more than these basics, and is highly configurable. See [the wiki](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki) for in depth documentation.\n\n## Documentation\nSee examples and browse complete documentation at the [Epoxy Wiki](https:\u002F\u002Fgithub.com\u002Fairbnb\u002Fepoxy\u002Fwiki)\n\nIf you still have questions, feel free to create a new issue.\n\n## Min SDK\nWe 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.\n\n## Contributing\nPull 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.\n\n## License\n\n```\nCopyright 2016 Airbnb, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","Epoxy是一个用于在Android的RecyclerView中构建复杂屏幕的库。它通过注解处理自动生成模型，这些模型可以是自定义视图或数据绑定布局，并且可以在EpoxyController中声明要显示的项目。该库简化了ViewHolder、项目差异计算与绑定更新、项目类型和ID管理等常见任务，同时支持保存视图状态及自动检测项目更改。Epoxy非常适合需要在一个列表或网格布局中展示多种不同类型的UI元素的应用场景，比如社交应用中的动态流或是新闻客户端的文章列表。通过减少模板代码，Epoxy让开发者能够更专注于业务逻辑而非底层实现细节。",2,"2026-06-11 02:58:52","top_language"]