[
  {
    "path": ".gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n.classpath\n.project\n.settings/\n\n# Proguard folder generated by Eclipse\nproguard/\n\n#Log Files\n*.log\n\n# OS X\n.DS_Store\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.war\n*.ear\n*.iml\n\n# IDEA Files\n.idea/\nout/\n\n# MAVEN COMPILE Files\ntarget/\nlint.xml\n\ndeploy.gradle\n#jcenterDeploy.gradle\n#jcenterInstall.gradle"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guide\n\nThank you for your attention to this project. Any bug, doc, examples and suggestion is appreciated. Here are some suggestions for you to create Pull Requests or open Issues.\n\n## Branch Management\n\n```\nmaster\n ↑\ndevelop         <--- PR(bugfix/typo/3rd-PR)\n ↑ PR\n{type}/{description}\n```  \nBranches\n\n* `master` branch\n    * `master` is the latest (pre-)release branch.\n* `develop` branch\n    * `develop` is the stable developing branch. [Github Release](https://help.github.com/articles/creating-releases/) is used to publish a (pre-)release version to `master` branch.\n    * ***It's RECOMMENDED to commit bugfix or feature PR to `develop`***.\n* `{action}/{description}` branch\n    * The branch for a developing or bugfix\n    *. **DO NOT commit any PR to such a branch**.\n\n## Branch Name\n\n```\n{action}/{description}\n```\n\n* `{action}`:\n\t* `feature`: used for developing a new feature.\n\t* `bugfix`: used for fixing bugs.\n* for example: `feature/add_flex_layouthelper`\n\n## Commit Log\n\n\n```\n{action} {description}\n```\n\n* `{action}`\n    * `add`\n    * `update` or `bugfix`\n    * `remove`\n    * ...\n* `{description}`\n    * It's ***RECOMMENDED*** to close issue with syntax `#123`, see [the doc](https://help.github.com/articles/closing-issues-via-commit-messages/) for more detail. It's useful for responding issues and release flow.\n\nfor example:\n\n* `add new layout helper`\n* `fix #123, make compatible to recyclervew 25.2.0`\n* `remove abc`\n\n## Issue\n\n* Please apply a proper label to an issue.\n* Suggested to use English.\n* Provide sufficient instructions to be able to reproduce the issue and make the issues clear. Such as phone model, system version, sdk version, crash logs and screen captures. \n\n## Pull Request And Contributor License Agreement\n\n\n[Create Pull Requests](https://github.com/alibaba/vlayout/compare) here.\n\nIn order to contribute code to vlayout, you (or the legal entity you represent) must sign the Contributor License Agreement (CLA).\n\nYou can read and sign the [Alibaba CLA](https://cla-assistant.io/alibaba/vlayout) online.\n\nFor CLA assistant service works properly, please make sure you have added email address that your commits linked to GitHub account.\n\n## Code Style Guide\n\n### Java & Android \n\n* Use [Google Java Style](https://google.github.io/styleguide/javaguide.html) as basic guidelines of java code.\n* Follow [AOSP Code Style](https://source.android.com/source/code-style.html) for rest of android related code style.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2016 Alibaba Group\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README-ch.md",
    "content": "# 注意，该项目停止维护！！！\n\n# vlayout\n\n[English Document](README.md)\n\n## Tangram 相关开源库\n\n### Android\n\n+ [Tangram-Android](https://github.com/alibaba/Tangram-Android)\n+ [Virtualview-Android](https://github.com/alibaba/Virtualview-Android)\n+ [vlayout](https://github.com/alibaba/vlayout)\n+ [UltraViewPager](https://github.com/alibaba/UltraViewPager)\n\n### iOS\n\n+ [Tangram-iOS](https://github.com/alibaba/Tangram-iOS)\n+ [Virtualview-iOS](https://github.com/alibaba/VirtualView-iOS)\n+ [LazyScrollView](https://github.com/alibaba/lazyscrollview)\n\nVirtualLayout是一个针对RecyclerView的LayoutManager扩展, 主要提供一整套布局方案和布局间的组件复用的问题。\n\n## 设计思路\n\n通过定制化的LayoutManager，接管整个RecyclerView的布局逻辑；LayoutManager管理了一系列LayoutHelper，LayoutHelper负责具体布局逻辑实现的地方；每一个LayoutHelper负责页面某一个范围内的组件布局；不同的LayoutHelper可以做不同的布局逻辑，因此可以在一个RecyclerView页面里提供异构的布局结构，这就能比系统自带的LinearLayoutManager、GridLayoutManager等提供更加丰富的能力。同时支持扩展LayoutHelper来提供更多的布局能力。\n\n## 主要功能\n\n * 默认通用布局实现，解耦所有的View和布局之间的关系: Linear, Grid, 吸顶, 浮动, 固定位置等。\n\t* LinearLayoutHelper: 线性布局\n\t* GridLayoutHelper:  Grid布局， 支持横向的colspan\n\t* FixLayoutHelper: 固定布局，始终在屏幕固定位置显示\n\t* ScrollFixLayoutHelper: 固定布局，但之后当页面滑动到该图片区域才显示, 可以用来做返回顶部或其他书签等\n\t* FloatLayoutHelper: 浮动布局，可以固定显示在屏幕上，但用户可以拖拽其位置\n\t* ColumnLayoutHelper: 栏格布局，都布局在一排，可以配置不同列之间的宽度比值\n\t* SingleLayoutHelper: 通栏布局，只会显示一个组件View\n\t* OnePlusNLayoutHelper: 一拖N布局，可以配置1-5个子元素\n\t* StickyLayoutHelper: stikcy布局， 可以配置吸顶或者吸底\n\t* StaggeredGridLayoutHelper: 瀑布流布局，可配置间隔高度/宽度\n * 上述默认实现里可以大致分为两类：一是非fix类型布局，像线性、Grid、栏格等，它们的特点是布局在整个页面流里，随页面滚动而滚动；另一类就是fix类型的布局，它们的子节点往往不随页面滚动而滚动。\n * 所有除布局外的组件复用，VirtualLayout将用来管理大的模块布局组合，扩展了RecyclerView，使得同一RecyclerView内的组件可以复用，减少View的创建和销毁过程。\n\n\n## 使用\n\n**虽然 vlayout 布局灵活，然而 API 相对原始，手工维护数据及 LayoutHelper 比较麻烦，强烈建议大家使用 [Tangram-Android](https://github.com/alibaba/Tangram-Android) 来间接使用 vlayout，Tangram 具备 vlayout 里所有的功能，且隐藏了细节，通过数据配置即可搭建页面，能避免绝大多数 Issue 里提到的问题，而且重大更新维护主要基于 Tangram，包括局部刷新、响应式接口等。**\n\n版本请参考 [release 说明](https://github.com/alibaba/vlayout/releases)里的最新版本，最新的 aar 都会发布到 jcenter 和 MavenCentral 上，确保配置了这两个仓库源，然后引入aar依赖：\n\n``` gradle \ncompile ('com.alibaba.android:vlayout:1.2.8@aar') {\n\ttransitive = true\n}\n```\n\n或者maven:  \npom.xml\n``` xml\n<dependency>\n  <groupId>com.alibaba.android</groupId>\n  <artifactId>vlayout</artifactId>\n  <version>1.2.8</version>\n  <type>aar</type>\n</dependency>\n```\n\n\n初始化```LayoutManager```\n\n``` java\nfinal RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);\nfinal VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);\n\nrecyclerView.setLayoutManager(layoutManager);\n```\n\n设置回收复用池大小，（如果一屏内相同类型的 View 个数比较多，需要设置一个合适的大小，防止来回滚动时重新创建 View）：\n\n``` java\nRecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();\nrecyclerView.setRecycledViewPool(viewPool);\nviewPool.setMaxRecycledViews(0, 10);\n\n```\n\n**注意：上述示例代码里只针对type=0的item设置了复用池的大小，如果你的页面有多种type，需要为每一种类型的分别调整复用池大小参数。**\n\n加载数据时有两种方式:\n\n* 一种是使用 ```DelegateAdapter```, 可以像平常一样写继承自```DelegateAdapter.Adapter```的Adapter, 只比之前的Adapter需要多重载```onCreateLayoutHelper```方法。\n其他的和默认Adapter一样。\n\n``` java\nDelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, hasConsistItemType);\nrecycler.setAdapter(delegateAdapter);\n\n// 之后可以通过 setAdapters 或 addAdapter方法添加DelegateAdapter.Adapter\n\ndelegateAdapter.setAdapters(adapters);\n\n// or\nCustomAdapter adapter = new CustomAdapter(data, new GridLayoutHelper());\ndelegateAdapter.addAdapter(adapter);\n\n// 如果数据有变化，调用自定义 adapter 的 notifyDataSetChanged()\nadapter.notifyDataSetChanged();\n```\n**注意：当hasConsistItemType=true的时候，不论是不是属于同一个子adapter，相同类型的item都能复用。表示它们共享一个类型。\n当hasConsistItemType=false的时候，不同子adapter之间的类型不共享**\n\n* 另一种是当业务有自定义的复杂需求的时候, 可以继承自```VirtualLayoutAdapter```, 实现自己的Adapter\n\n``` java\npublic class MyAdapter extends VirtualLayoutAdapter {\n   ......\n}\n\nMyAdapter myAdapter = new MyAdapter(layoutManager);\n\n//构造 layoutHelper 列表\nList<LayoutHelper> helpers = new LinkedList<>();\nGridLayoutHelper gridLayoutHelper = new GridLayoutHelper(4);\ngridLayoutHelper.setItemCount(25);\nhelpers.add(gridLayoutHelper);\n\nGridLayoutHelper gridLayoutHelper2 = new GridLayoutHelper(2);\ngridLayoutHelper2.setItemCount(25);\nhelpers.add(gridLayoutHelper2);\n\n//将 layoutHelper 列表传递给 adapter\nmyAdapter.setLayoutHelpers(helpers);\n\n//将 adapter 设置给 recyclerView\nrecycler.setAdapter(myAdapter);\n\n```\n\n在这种情况下，需要使用者注意在当```LayoutHelpers```的结构或者数据数量等会影响到布局的元素变化时，需要主动调用```setLayoutHelpers```去更新布局模式。\n\n另外如果你的应用有混淆配置，请为vlayout添加一下防混淆配置：\n\n```\n-keepattributes InnerClasses\n-keep class com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx { *; }\n-keep class android.support.v7.widget.RecyclerView$LayoutParams { *; }\n-keep class android.support.v7.widget.RecyclerView$ViewHolder { *; }\n-keep class android.support.v7.widget.ChildHelper { *; }\n-keep class android.support.v7.widget.ChildHelper$Bucket { *; }\n-keep class android.support.v7.widget.RecyclerView$LayoutManager { *; }\n```\n\n# Demo\n\n![](http://img3.tbcdn.cn/L1/461/1/1b9bfb42009047f75cee08ae741505de2c74ac0a)\n\n[Demo工程](https://github.com/alibaba/vlayout/tree/master/examples)\n\n# FAQ\n\n使用之前或者碰到问题的时候，建议先看看其他[FAQ](docs/VLayoutFAQ.md)。\n\n# 布局属性\n\n每一种layoutHelper都有自己的布局属性来控制布局样式，详情请参考[文档](docs/ATTRIBUTES-ch.md)。\n\n# 贡献代码\n\n在提 Issue 或者 PR 之前，建议先阅读[Contributing Guide](CONTRIBUTING.md)。按照规范提建议。\n\n# 开源许可证\n\nvlayout遵循MIT开源许可证协议。\n"
  },
  {
    "path": "README.md",
    "content": "# Attention. This project is not maintained any more !!!\n\n# vlayout\n\n[中文文档](README-ch.md)\n\n## Projects of Tangram\n\n### Android\n\n+ [Tangram-Android](https://github.com/alibaba/Tangram-Android)\n+ [Virtualview-Android](https://github.com/alibaba/Virtualview-Android)\n+ [vlayout](https://github.com/alibaba/vlayout)\n+ [UltraViewPager](https://github.com/alibaba/UltraViewPager)\n\n### iOS\n\n+ [Tangram-iOS](https://github.com/alibaba/Tangram-iOS)\n+ [Virtualview-iOS](https://github.com/alibaba/VirtualView-iOS)\n+ [LazyScrollView](https://github.com/alibaba/lazyscrollview)\n\nProject `vlayout` is a powerful LayoutManager extension for RecyclerView, it provides a group of layouts for RecyclerView. Make it able to handle a complicate situation when grid, list and other layouts in the same recyclerview.\n\n## Design\n\nBy providing a custom LayoutManager to RecyclerView, VirtualLayout is able to layout child views with different style at single view elegantly. The custom LayoutManager manages a serial of layoutHelpers where each one implements the specific layout logic for a certain position range items. By the way, implementing your custom layoutHelper and provding it to the framework is also supported.\n\n## Main Feature\n* Provide default common layout implementation, decouple the View and Layout. Default layout implementations are:\n\t* LinearLayoutHelper: provide linear layout as LinearLayoutManager.\n\t* GridLayoutHelper: provide grid layout as GridLayoutManager, but with more feature.\n\t* FixLayoutHelper: fix the view at certain position of screen, the view does not scroll with whole page.\n\t* ScrollFixLayoutHelper: fix the view at certain position of screen, but the view does not show until it scrolls to it position.\n\t* FloatLayoutHelper: float the view on top of page, user can drag and drop it.\n\t* ColumnLayoutHelper: perform like GridLayoutHelper but layouts all child views in one line.\n\t* SingleLayoutHelper: contain only one child view.\n\t* OnePlusNLayoutHelper: a custom layout with one child view layouted at left and the others at right, you may not need this.\n\t* StickyLayoutHelper: scroll the view when its position is inside the screen, but fix the view at start or end when its position is outside the screen.\n\t* StaggeredGridLayoutHelper: provide waterfall like layout as StaggeredGridLayoutManager.\n* LayoutHelpers provided by default can be generally divided into two categories. One is non-fix LayoutHelper such as LinearLayoutHelper, GridLayoutHelper, etc which means the children of these LayoutHelper will be layouted in the flow of parent container and will be scrolled with the container scrolling. While the other is fix LayoutHelper which means the child of these is always fix in parent container.\n\n\n## Usage\n\n### Import Library\n\nPlease find the latest version in [release notes](https://github.com/alibaba/vlayout/releases). The newest version has been upload to jcenter and MavenCentral, make sure you have added at least one of these repositories. As follow:\n\nFor gradle:\n``` gradle\ncompile ('com.alibaba.android:vlayout:1.2.8@aar') {\n\ttransitive = true\n}\n```\n\nOr in maven:  \npom.xml\n``` xml\n<dependency>\n  <groupId>com.alibaba.android</groupId>\n  <artifactId>vlayout</artifactId>\n  <version>1.2.8</version>\n  <type>aar</type>\n</dependency>\n```\n\n### Initialize LayoutManager\n``` java\nfinal RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);\nfinal VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);\n\nrecyclerView.setLayoutManager(layoutManager);\n```\n\n### Initialize recycled pool's size\nProvide a reasonable recycled pool's size to your recyclerView, since the default value may not meet your situation and cause re-create views when scrolling.\n\n``` java\nRecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();\nrecyclerView.setRecycledViewPool(viewPool);\nviewPool.setMaxRecycledViews(0, 10);\n```\n\n**Attention: the demo code above only modify the recycle pool size of item with type = 0, it you has more than one type in your adapter, you should update recycle pool size for each type.**\n\n### Set Adapters\n\n* You can use `DelegateAdapter` for as a root adapter to make combination of your own adapters. Just make it extend ```DelegateAdapter.Adapter``` and overrides ```onCreateLayoutHelper``` method.\n\n``` java\nDelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, hasConsistItemType);\nrecycler.setAdapter(delegateAdapter);\n\n// Then you can set sub- adapters\n\ndelegateAdapter.setAdapters(adapters);\n\n// or\nCustomAdapter adapter = new CustomAdapter(data, new GridLayoutHelper());\ndelegateAdapter.addAdapter(adapter);\n\n// call notify change when data changes\nadapter.notifyDataSetChanged();\n\n```\n\n**Attention: When `hasConsistItemType = true`, items with same type value in different sub-adapters share the same type, their view would be reused during scroll. When `hasConsistItemType = false`, items with same type value in different sub-adapters do not share the same type internally.**\n\n* The other way to set adapter is extending ```VirtualLayoutAdapter``` and implementing it to make deep combination to your business code.\n\n``` java\npublic class MyAdapter extends VirtualLayoutAdapter {\n   ......\n}\n\nMyAdapter myAdapter = new MyAdapter(layoutManager);\n\n//create layoutHelper list\nList<LayoutHelper> helpers = new LinkedList<>();\nGridLayoutHelper gridLayoutHelper = new GridLayoutHelper(4);\ngridLayoutHelper.setItemCount(25);\nhelpers.add(gridLayoutHelper);\n\nGridLayoutHelper gridLayoutHelper2 = new GridLayoutHelper(2);\ngridLayoutHelper2.setItemCount(25);\nhelpers.add(gridLayoutHelper2);\n\n//set layoutHelper list to adapter\nmyAdapter.setLayoutHelpers(helpers);\n\n//set adapter to recyclerView\nrecycler.setAdapter(myAdapter);\n\n```\n\nIn this way, one thing you should note is that you should call ```setLayoutHelpers``` when the data of Adapter changes.\n\n### Config proguard\n\nAdd following configs in your proguard file if your app is released with proguard.\n\n```\n-keepattributes InnerClasses\n-keep class com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx { *; }\n-keep class android.support.v7.widget.RecyclerView$LayoutParams { *; }\n-keep class android.support.v7.widget.RecyclerView$ViewHolder { *; }\n-keep class android.support.v7.widget.ChildHelper { *; }\n-keep class android.support.v7.widget.ChildHelper$Bucket { *; }\n-keep class android.support.v7.widget.RecyclerView$LayoutManager { *; }\n```\n\n# Demo\n\n![](http://img3.tbcdn.cn/L1/461/1/1b9bfb42009047f75cee08ae741505de2c74ac0a)\n\n[Demo Project](https://github.com/alibaba/vlayout/tree/master/examples)\n\n# FAQ\n\nRead FAQ(In Chinese language only now) before submitting issue: [FAQ](docs/VLayoutFAQ.md)。\n\n# Layout Attributes\n\nEach layoutHelper has a few attributes to control its layout style. See [this](docs/ATTRIBUTES.md) to read more.\n\n# Contributing\n\nBefore you open an issue or create a pull request, please read [Contributing Guide](CONTRIBUTING.md) first.\n\n# LICENSE\n\nVlayout is available under the MIT license.\n"
  },
  {
    "path": "build.gradle",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }\n        maven { url \"http://oss.jfrog.org/oss-snapshot-local/\" }\n        mavenCentral()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.2.2'\n        classpath 'com.github.xfumihiro.view-inspector:view-inspector-plugin:0.1.1'\n        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'\n        classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.0.0'\n        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'\n    }\n}\n\nallprojects {\n    repositories {\n        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }\n        maven { url \"http://oss.jfrog.org/oss-snapshot-local/\" }\n        jcenter()\n        mavenLocal()\n    }\n}\n"
  },
  {
    "path": "docs/ATTRIBUTES-ch.md",
    "content": "为了提供丰富的布局能力，我们为`LayoutHelper`设计了一系列布局属性，用来控制布局逻辑和样式。这里介绍这些属性的概念和用法。\n\n[Englist Document](ATTRIBUTES.md)\n\n# margin, padding\n\nMargin, padding就是外边距、内边距，概念与Android系统的margin, padding一样，但也有不同的地方：\n\n+ 它不是整个`RecyclerView`页面的margin和padding，它是每一块`LayoutHelper`所负责的区域的margin和padding。\n+ 一个页面里可以有多个`LayoutHelper`，意味着不同`LayoutHelper`可以设置不同的margin和padding。\n+ `LayoutHelper`的margin和padding与页面`RecyclerView`的margin和padding可以共存。\n+ 目前主要针对非fix类型的`LayoutHelper`实现了margin和padding，fix类型`LayoutHelper`内部没有相对位置关系，不处理边距。\n\n![margin-padding](images/MarginPadding.png)\n\n### 接口\n\n对于`LayoutHelper`，调用\n\n`public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding)`\n\n`public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin)`\n\n# bgColor, bgImg\n\n背景颜色或者背景图，这其实不是布局属性，但是由于在vlayout对视图进行了直接布局，不同区域的视图的父节点都是`RecyclerView`，如果想要针对某一块区域单独绘制背景，就很难做到了。vlayout框架对此做了特殊处理，对于非fix、非float类型的`LayoutHelper`，支持配置背景色或背景图。同样目前主要针对非fix类型的`LayoutHelper`实现这个特性。\n\n![background](images/Background.png)\n\n### 接口\n\n使用背景色\n\n`public void setBgColor(int bgColor)`\n\n使用背景图\n\n首先为`LayoutManager`提供一个`ImageView`简单工厂\n\n```\nthis.mLayoutManager.setLayoutViewFactory(new LayoutViewFactory() {\n            @Override\n            public opinion generateLayoutView(@NonNull Context context) {\n                return new XXImageView(context);\n            }\n        });\n```\n\n再为`LayoutHelper`提设置图片加载的`Listener`\n\n```\nbaseHelper.setLayoutViewBindListener(new BindListener(imgUrl));\nbaseHelper.setLayoutViewUnBindListener(new UnbindListener(imgUrl));\n\n\nprivate static class BindListener implements BaseLayoutHelper.LayoutViewBindListener {\n        private String imgUrl;\n\n        public BindListener(String imgUrl) {\n            this.imgUrl = imgUrl;\n        }\n\n        @Override\n        public void onBind(View layoutView, BaseLayoutHelper baseLayoutHelper) {\n            //loading image\n        }\n    }\n\nprivate static class UnbindListener implements BaseLayoutHelper.LayoutViewUnBindListener {\n    private String imgUrl;\n\n    public UnbindListener(String imgUrl) {\n        this. imgUrl = imgUrl;\n    }\n\n    @Override\n    public void onUnbind(View layoutView, BaseLayoutHelper baseLayoutHelper) {\n            //cancel loading image\n    }\n}\n```\n\n# aspectRatio\n\n为了保证布局过程中视图的高度一致，我们设计了aspectRatio属性，它是宽与高的比例，`LayoutHelper`里有aspectRatio属性，通过vlayout添加的视图的`LayoutParams`也有aspectRatio属性，后者的优先级比前者高，但含义不一样。\n\n+ `LayoutHelper`定义的aspectRatio，指的是一行视图整体的宽度与高度之比，当然整体的宽度是减去了`RecyclerView和`对应的`LayoutHelper`的margin, padding。\n+ 视图的`LayoutParams`定义的aspectRatio，指的是在`LayoutHelper`计算出视图宽度之后，用来确定视图高度时使用的，它会覆盖通过`LayoutHelper`的aspectRatio计算出来的视图高度，因此具备更高优先级。\n\n![aspectRatio](images/AspectRatio.png)\n\n### 接口\n\n对于`LayoutHelper`，调用\n\n`public void setAspectRatio(float aspectRatio)`\n\n对于`LayoutParams`，调用\n\n`((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio`\n\n# dividerHeight\n\n`LinearLayoutHelper`的属性，`LinearLayoutHelper`是像`ListView`一样的线性布局，dividerHeight就是每个组件之间的间距。\n\n![dividerHeight](images/DividerHeight.png)\n\n### 接口\n\n对于`LinearLayoutHelper`，调用\n\n`public void setDividerHeight(int dividerHeight)`\n\n# weights\n\n`ColumnLayoutHelper`, `GridLayoutHelper`的属性，它们都是提供网格状的布局能力，**建议使用`GridLayoutHelper`**，它的能力更加强大，参考下文介绍。默认情况下，每个网格中每一列的宽度是一样的，通过weights属性，可以指定让每一列的宽度成比例分配，就像`LinearLayout`的weight属性一样。\nweights属性是一个float数组，每一项代表某一列占父容器宽度的百分比，总和建议是100，否则布局会超出容器宽度；如果布局中有4列，那么weights的长度也应该是4；长度大于4，多出的部分不参与宽度计算；如果小于4，不足的部分默认平分剩余的空间。\n\n![weights](images/Weights.png)\n\n### 接口\n\n对于`ColumnLayoutHelper`, `GridLayoutHelper`，调用\n\n`public void setWeights(float[] weights)`\n\n# vGap, hGap\n\n`GridLayoutHelper`与`StaggeredGridLayoutHelper`都有这两个属性，分别控制视图之间的垂直间距和水平间距。\n\n![vgap-hgap](images/HGapVGap.png)\n\n### 接口\n\n对于`GridLayoutHelper`, `StaggeredGridLayoutHelper`，调用\n\n`public void setHGap(int hGap)`\n\n`public void setVGap(int vGap)`\n\n# spanCount, spanSizeLookup\n\n`GridLayoutHelper`的属性，参考于系统的`GridLayoutManager`，spanCount表示网格的列数，默认情况下每一个视图都占用一个网格区域，但通过提供自定义的spanSizeLookUp，可以指定某个位置的视图占用多个网格区域。\n\n![spanCount-spanSize](images/SpanCountSpanSize.png)\n\n### 接口\n\n使用spanCount调用\n\n`public void setSpanCount(int spanCount)`\n\n使用spanSizeLookup\n\n`public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup)`\n\n# autoExpand\n\n`GridLayoutHelper`的属性，当一行里视图的个数少于spanCount值的时候，如果autoExpand为true，视图的总宽度会填满可用区域；否则会在屏幕上留空白区域。\n\n![autoExpand](images/AutoExpand.png)\n\n### 接口\n\n调用\n\n`public void setAutoExpand(boolean isAutoExpand)`\n\n# lane\n\n`StaggeredGridLayoutHelper`中有这个属性，与`GridLayoutHelper`里的spanCount类似，控制瀑布流的列数。\n\n### 接口\n\n调用\n\n`public void setLane(int lane)`\n\n# fixAreaAdjuster\n\nfix类型的`LayoutHelper`，在可能需要设置一个相对父容器四个边的偏移量，比如整个页面里有一个固定的标题栏添加在vlayout容器上，vlayout内部的fix类型视图不希望与外部的标题有所重叠，那么就可以设置一个fixAreaAdjuster来做偏移。\n\n![fixAreaAdjuster](images/FixAreaAdjuster.png)\n\n### 接口\n\n调用\n\n`public void setAdjuster(FixAreaAdjuster adjuster)`\n\n# alignType, x, y\n\n`FixLayoutHelper`, `ScrollFixLayoutHelper`, `FloatLayoutHelper`的属性，表示吸边时的基准位置，有四个取值，分别是`TOP_LEFT`, `TOP_RIGHT`, `BOTTOM_LEFT`, `BOTTOM_RIGHT`。`x`和`y`是相对这四个位置的偏移量，最终的偏移量还要受上述的fixAreaAdjuster影响。\n\n+ `TOP_LEFT`：基准位置是左上角，`x`是视图左边相对父容器的左边距偏移量，`y`是视图顶边相对父容器的上边距偏移量；\n+ `TOP_RIGHT`：基准位置是右上角，`x`是视图右边相对父容器的右边距偏移量，`y`是视图顶边相对父容器的上边距偏移量；\n+ `BOTTOM_LEFT`：基准位置是左下角，`x`是视图左边相对父容器的左边距偏移量，`y`是视图底边相对父容器的下边距偏移量；\n+ `BOTTOM_RIGHT`：基准位置是右下角，`x`是视图右边相对父容器的右边距偏移量，`y`是视图底边相对父容器的下边距偏移量；\n\n![alignTypeXY](images/AlignTypeXY.png)\n\n### 接口\n\n设置基准调用\n\n`public void setAlignType(int alignType)`\n\n设置偏移量调用\n\n`public void setX(int x)`\n\n`public void setY(int y)`\n\n# showType\n\n`ScrollFixLayoutHelper`的属性，取值有`SHOW_ALWAYS`, `SHOW_ON_ENTER`, `SHOW_ON_LEAVE`。\n\n+ `SHOW_ALWAYS`：与`FixLayoutHelper`的行为一致，固定在某个位置；\n+ `SHOW_ON_ENTER`：默认不显示视图，当页面滚动到这个视图的位置的时候，才显示；\n+ `SHOW_ON_LEAVE`：默认不显示视图，当页面滚出这个视图的位置的时候显示；\n\n![showType](images/ShowType.png)\n\n调用\n\n`public void setShowType(int showType)`\n\n# stickyStart, offset\n\n`StickyLayoutHelper`的属性，当视图的位置在屏幕范围内时，视图会随页面滚动而滚动；当视图的位置滑出屏幕时，`StickyLayoutHelper`会将视图固定在顶部（`stickyStart = true`）或者底部（`stickyStart = false`），固定的位置支持设置偏移量offset。\n\n![stickyStart](images/StickyStart.png)\n\n调用\n\n`public void setStickyStart(boolean stickyStart)`\n\n`public void setOffset(int offset)`\n"
  },
  {
    "path": "docs/ATTRIBUTES.md",
    "content": "Vlayout provides rich layout features, which are presented in the form of a serial of layout attributes technically. Here we introduce these attributes.\n\n[中文文档](ATTRIBUTES-ch.md)\n\n# margin, padding\n\nMargin, padding have the similar concepts as normal Android system's margin and padding. There are several difference you should know:\n\n+ Here we say margin and padding, specifically mean ```LayoutHelper```'s margin and padding, not the ```RecyclerView```'s.\n+ A ```RecyclerView``` could contains more than one layoutHelpers, which means each ```LayoutHelper``` is able to host its own margin and padding value.\n+ ```LayoutHelper```'s margin and padding can coexist with ```RecyclerView```'s margin and padding.\n+ Margin and padding is mainly supported by non-fix LayoutHelper.\n\n![margin-padding](images/MarginPadding.png)\n\n### API\n\nFor ```LayoutHelper```，call\n\n```public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding)```\n\n```public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin)```\n\n# bgColor, bgImg\n\nActually, background color or background image has nothing to do with layout logic. But in our framework, all child views has the same parent which is ```RecyclerView```. If you want to fill background to a certain area with a color or image, it's a hard job. Vlayout has done some effort to make it possible for non-fix ```LayoutHelper```, so it's explained here.\n\n![background](images/Background.png)\n\n### API\n\nFill background with color, call\n\n```public void setBgColor(int bgColor)```\n\nFill background with image,\n\nFirst, provide a simple factory to produce ```ImageView``` to ```LayoutManager```,\n\n```\nthis.mLayoutManager.setLayoutViewFactory(new LayoutViewFactory() {\n            @Override\n            public View generateLayoutView(@NonNull Context context) {\n                return new XXImageView(context);\n            }\n        });\n```\n\nSecond, set image load listeners to ```LayoutHelper```,\n\n```\nbaseHelper.setLayoutViewBindListener(new BindListener(imgUrl));\nbaseHelper.setLayoutViewUnBindListener(new UnbindListener(imgUrl));\n\n\nprivate static class BindListener implements BaseLayoutHelper.LayoutViewBindListener {\n        private String imgUrl;\n\n        public BindListener(String imgUrl) {\n            this.imgUrl = imgUrl;\n        }\n\n        @Override\n        public void onBind(View layoutView, BaseLayoutHelper baseLayoutHelper) {\n            //loading image\n        }\n    }\n\n    private static class UnbindListener implements BaseLayoutHelper.LayoutViewUnBindListener {\n        private String imgUrl;\n\n        public UnbindListener(String imgUrl) {\n            this. imgUrl = imgUrl;\n        }\n\n        @Override\n        public void onUnbind(View layoutView, BaseLayoutHelper baseLayoutHelper) {\n        \t\t//cancel loading image\n        }\n    }\n```\n\n# aspectRatio\n\nIn order to uniform child view's height during layout, vlayout introduces aspectRatio. Generally the view's width is decided by ```LayoutHelper``` while its height is decided by the view itself, using aspectRatio allow ```LayoutHelper``` to decide view's height. Both ```LayoutHelper``` and ```LayoutParams``` of the view inside vlayout have this attribute. It generally means width/height ratio, but has slight difference in these two situations. The latter has a higher priority than the former.\n\n+ The aspectRatio defined by ```LayoutHelper``` means the ratio of width of one entire row's view to height of this row's height. Here ```RecyclerView```'s margin padding and ```LayoutHelper```'s margin padding is not included in the width of entire row's view.\n+ The aspectRatio defined in ```VirtualLayoutManager.LayoutParams``` means the ratio of width of this view to height of this view. The height calculated by this way will override ```LayoutHelper```'s calculation result, so it has higher priority.\n\n![aspectRatio](images/AspectRatio.png)\n\n### API\n\nFor ```LayoutHelper```, call\n\n```public void setAspectRatio(float aspectRatio)```\n\nFor ```LayoutParams```, call\n\n```((VirutalLayoutManager.LayoutParams) layoutParams).mAspectRatio```\n\n# dividerHeight\n\n```LinearLayoutHelper```'s attribute. ```LinearLayoutHelper``` performs like ```ListView```, so as you know, dividerHeight is the gap between items.\n\n![dividerHeight](images/DividerHeight.png)\n\n### API\n\nFor ```LinearLayoutHelper```, call\n\n```public void setDividerHeight(int dividerHeight)```\n\n# weights\n\n```ColumnLayoutHelper``` and ```GridLayoutHelper```'s attribute, they both provide grid layout ability like ```GridLayoutManager```. **You are suggested to use ```GirdLayoutHelper```** since it is more powerful. By default, each column's width is equal and shares the available space. Setting weights allows each column's width occupies a certain percentage of available space. Just sounds like weight in ```LinearLayout```.\n\nThis attribute is a float array, each item in array means the percentage of a column width. The sum of item value in array is 100, otherwise child view may be layouted outside the box. If there are four columns in this layout, the suggested length of weights is four, too. If length of weights array is larger than 4, the extra weight provided is ignored. If length of weights array is less than 4, the remaining views with no weight assigned share the remaining available space.\n\n![weights](images/Weights.png)\n\n### API\n\nFor ```ColumnLayoutHelper```, ```GridLayoutHelper```, call\n\n```public void setWeights(float[] weights)```\n\n# vGap, hGap\n\n```GridLayoutHelper``` and ```StaggeredGridLayoutHelper```'s attribute, vGap defines the vertical gap between child views, hGap defines the horizontal gap between child views.\n\n![vgap-hgap](images/HGapVGap.png)\n\n### API\n\nFor ```GridLayoutHelper```, ```StaggeredGridLayoutHelper```, call\n\n```public void setHGap(int hGap)```\n```public void setVGap(int vGap)```\n\n# spanCount, spanSizeLookup\n\n```GridLayoutHelper```'s attribute, concept borrowed from system's ```GridLayoutManager```. SpanCount specifies the column count, or grid count each line in other word. By default each item view in ```GridLayoutHelper``` occupies one grid, but with custom spanSizeLookup it allows one item view could occupy more than one grid space.\n\n![spanCount-spanSize](images/SpanCountSpanSize.png)\n\n### API\n\nSetting spanCount call\n\n```public void setSpanCount(int spanCount)```\n\nSetting spanSizeLookup call\n\n```public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup)```\n\n# autoExpand\n\n```GridLayoutHelper```'s attribute. On the occasion of one line view's count is less than spanCount, if autoExpand is set to be true, these view will expand to fill all available space, otherwise they will leave some blank area.\n\n![autoExpand](images/AutoExpand.png)\n\n### API\n\n```public void setAutoExpand(boolean isAutoExpand)```\n\n# lane\n\n```StaggeredGridLayoutHelper```'s attribute, performs like spanCount in ```GridLayoutHelper```.\n\n### API\n\n```public void setLane(int lane)```\n\n# fixAreaAdjuster\n\n```LayoutHelper``` with fix behavior may need an offset to four edges of parent container. For example, an activity has a vlayout container with fullscreen size, there's an title bar on top of vlayout's container which is not part of vlayout. While a view inside vlayout wants to be fixed at top of vlayout, so it needs an offset to top edge to avoid overlapping with title bar outside.\n\n![fixAreaAdjuster](images/FixAreaAdjuster.png)\n\n### API\n\n```public void setAdjuster(FixAreaAdjuster adjuster)```\n\n# alignType, x, y\n\n```FixLayoutHelper```, ```ScrollFixLayoutHelper```, ```FloatLayoutHelper```'s attributes, alignType defines the base position of fix area, there are four available values: ```TOP_LEFT```, ```TOP_RIGHT```, ```BOTTOM_LEFT```, ```BOTTOM_RIGHT```. ```x``` and ```y``` are the horizontal offset and vertical offset to this base position. The final offset is calculated with fixAreaAdjuster in addition as former mentioned.\n\n+ ```TOP_LEFT```: base position is top left corner of parent container, ```x``` is the offset from child view's left edge to parent's left edge, ```y``` is the offset from child view's top edge to parent's top edge;\n+ ```TOP_RIGHT```: base position is top right corner of parent container, ```x``` is the offset from child view's right edge to parent's right edge, ```y``` is the offset from child view's top edge to parent's top edge;\n+ ```BOTTOM_LEFT```: base position is bottom left corner of parent container, ```x``` is the offset from child view's left edge to parent's left edge, ```y``` is the offset from child view's bottom edge to parent's bottom edge;\n+ ```BOTTOM_RIGHT```: base position is bottom right corner of parent container, ```x``` is the offset from child view's right edge to parent's right edge, ```y``` is the offset from child view's bottom edge to parent's bottom edge;\n\n![alignTypeXY](images/AlignTypeXY.png)\n\n### API\n\nSetting alignType call\n\n```public void setAlignType(int alignType)```\n\nSetting offset call\n\n```public void setX(int x)```\n\n```public void setY(int y)```\n\n# showType\n\n```ScrollFixLayoutHelper```'s attribute, there are three available values: ```SHOW_ALWAYS```, ```SHOW_ON_ENTER```, ```SHOW_ON_LEAVE```.\n\n+ ```SHOW_ALWAYS```: in this way, ```ScrollFixLayoutHelper``` performs the same as ```FixLayoutHelper```;\n+ ```SHOW_ON_ENTER```: in this way, the child view in ```LayoutHelper``` does not show by default. It shows only when the page scroll to its position.\n+ ```SHOW_ON_LEAVE```: in this way, the child view in ```LayoutHelper``` does not show by default, either. It shows only when the page scroll over its position.\n\n![showType](images/ShowType.png)\n\n### API\n\n```public void setShowType(int showType)```\n\n# stickyStart, offset\n\n```StickyLayoutHelper```'s attribute, when child view's position is inside the screen visible area, the child view scrll with whole page; when child view position scrolls outside the screen, this ```LayoutHelper``` will fix the at top(```stickyStart = true```) or bottom(```stickyStart = false```). The fix position is also affected by offset value.\n\n![stickyStart](images/StickyStart.png)\n\n### API\n\n```public void setStickyStart(boolean stickyStart)```\n\n```public void setOffset(int offset)```"
  },
  {
    "path": "docs/VLayoutFAQ.md",
    "content": "# VLayout FAQ\n\n## 组件复用的问题\n比如碰到卡顿、类型转换异常等等，都有可能是复用的问题引起的。\n\n在使用 `DelegateAdapter` 的时候，每一个 `LayoutHelper` 都对应于一个 `DelegateAdapter.Adapter`，一般情况下使用方只需要提供自定义的 `DelegateAdapter.Adapter`，然后按照正常的使用方式使用。\n\n但这里有个问题，不同的 `DelegateAdapter.Adapter` 之间，他们的 itemType 是不是一样的？这里有一个选择：在 `DelegateAdapter` 的构造函数里有个 `hasConsistItemType` 参数（默认是 false ）：\n\n当 `hasConsistItemType=false` 的时候，即使不同 `DelegateAdapter.Adapter` 里返回的相同的 itemType，对于 `DelegateAdapter` 也会将它转换成不同的值，对于 `RecyclerView` 来说它们是不同的类型。\n\n当 `hasConsistItemType=true` 的时候，不同的 `DelegateAdapter.Adapter` 之间返回相同的 itemType 的时候，他们之间是可以复用的。\n\n因此如果没有处理好这一点，会导致 `ViewHolder` 的类型转换异常等 bug。有一篇更加详细的资料可参考：[PairFunction](http://pingguohe.net/2017/05/03/the-beauty-of-math-in-vlayout.html)\n\n补充：后来发现一个 bug，当 `hasConsistItemType=false`，在同一位置数据变化，前后构造了不一样的 Adapter，它们返回的 itemType 一样，也会导致类型转换出错，详见：[#182](https://github.com/alibaba/vlayout/issues/182)，目前采用人工保证返回不同的 itemType 来规避。\n\n## 设置每种类型回收复用池的大小\n在 README 里写了这么一段 demo：`viewPool.setMaxRecycledViews(0, 10);`，很多人误以为只要这么设置就可以了，实际上有多少种类型的 itemType，就得为它们分别设置复用池大小。比如：\n\n```\nviewPool = new RecyclerView.RecycledViewPool();\nrecyclerView.setRecycledViewPool(viewPool);\nviewPool.setMaxRecycledViews(0, 5);\nviewPool.setMaxRecycledViews(1, 5);\nviewPool.setMaxRecycledViews(2, 5);\nviewPool.setMaxRecycledViews(3, 10);\nviewPool.setMaxRecycledViews(4, 10);\nviewPool.setMaxRecycledViews(5, 10);\n...\n```\n\n## 混淆问题\n\n如果碰到release包（混淆过）无法正常运行，debug包（一般未混淆）可正常运行，检查一下混淆配置是否完整：\n\n```\n-keepattributes InnerClasses\n-keep class com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx { *; }\n-keep class android.support.v7.widget.RecyclerView$LayoutParams { *; }\n-keep class android.support.v7.widget.RecyclerView$ViewHolder { *; }\n-keep class android.support.v7.widget.ChildHelper { *; }\n-keep class android.support.v7.widget.ChildHelper$Bucket { *; }\n-keep class android.support.v7.widget.RecyclerView$LayoutManager { *; }\n```\n\n## 下拉刷新和加载更多\n\nVLayout 只负责布局，下拉刷新和加载更多需要业务方自己处理，当然可能存在和一些下拉刷新控件不兼容的 bug。\n\n下拉刷新，有很多框架是通过判断 `RecyclerView` 的第一个 view 的 top 是否为 0 来触发下拉动作。VLayout 里在处理背景、悬浮态的时候加入了一些对 `LayoutManager` 不可见的 View，但又真实存在与 `RecyclerView` 的视图树里，建议使用 `layoutManager.getChildAt(0)` 来获取第一个 view。\n\n加载更多，可以通过 recyclerView 的滚动状态来触发 load-more 事件，需要使用方注册一个 `OnScrollListener`：\n\n```\nRecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {\n\n            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {\n            }\n\n            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {\n                //hasMore: status of current page, means if there's more data, you have to maintain this status\n                if(hasMore) {\n                    VirtualLayoutManager lm = (VirtualLayoutManager)recyclerView.getLayoutManager();\n                    int first=0, last=0, total=0;\n                    first = ((LinearLayoutManager)lm).findFirstVisibleItemPosition();\n                    last = ((LinearLayoutManager)lm).findLastVisibleItemPosition();\n                    total = recyclerView.getAdapter().getItemCount();\n                    if(last > 0\n                        && last >= total  - earlyCountForAutoLoad) {\n                        //earlyCountForAutoLoad: help to trigger load more listener earlier\n                        //TODO trigger loadmore listener\n                    }\n                }\n            }\n        }\n```\n\n## 横向滑动\n没有实现横向滚动的 `LayoutHelper` ，因为 `LayoutHelper` 目前只能做静态的布局，对于跟数据绑定的动态横向滚动布局，比如 `ViewPager` 或者 `RecyclerView` ，建议使用组件的形式提供。也就是一个 `LinearLayoutHelper` 包一个 Item，这个 Item 是 `ViewPager` 或者横向滚动的 `RecyclerView`，且它们是可以和整个页面的 `RecyclerView` 共用一个回收复用池的，参考 Demo 里的第一个组件的使用方法。\n\n## 设置背景图后触发循环布局\n给 `LayoutHelper` 设置背景图的时候，由于这个过程是在布局 view 的阶段，设置了图片会触发一次新的 layout，从而又导致触发一次背景图设置，最终进入死循环，因此需要使用方在设置背景图的时候判断当前图片是否已经加载过一次并且成功，如果绑定过一次就不需要再设置图片了，阻断死循环的路径。\n具体做法是：\n在 `BaseLayoutHelper.LayoutViewBindListener` 的 `onBind()` 方法里判断是否成功绑定过该背景图。\n在 `BaseLayoutHelper.LayoutViewUnBindListener` 的 `onUnbind()` 方法里清楚绑定成功与否的状态。\n在使用方的图片加载成功回调函数里设置一下图片加载成功的状态，可以自行维护一个 map 或者给 View 设置一个 tag 标记。\n\n我们提供了一个简单的 `DefaultLayoutViewHelper` 封装了这个逻辑，可以参考使用。\n\n## 在可滚动区域里嵌套使用 vlayout 的 `RecyclerView`\n\n不太建议嵌套滚动，除非手势不冲突；如果要完全展开 vlayout 里的内容，牺牲滚动复用，可以调用 `VirtualLayoutManager` 的 `setNoScrolling(true);` 方法设置一下。\n\n## 为 `GridLayoutHelper` 的设置自定义 `SpanSizeLookup`\n\n在 `SpanSizeLookup` 中，`public int getSpanSize(int position)` 方法参数的 position 是整个页面的 position 信息，需要获取当前 layoutHelper 内的相对位置，需要减去一个偏移量，即 `position - getStartPosition()`。\n\n## 获取 `DelegateAdapter` 里数据的相对位置\n\n在 `DelegateAdapter` 里有 `findOffsetPosition(int absolutePosition)` 方法，传入整个页面的绝对位置，获取相对位置。\n或者用\n```\n  public static abstract class Adapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {\n      public abstract LayoutHelper onCreateLayoutHelper();\n\n      protected void onBindViewHolderWithOffset(VH holder, int position, int offsetTotal) {\n\n      }\n  }\n```\n中的 `onBindViewHolderWithOffset()` 方法代替传统的 `onBindViewHolder()` 方法，其中的 `position` 参数也是相对位置。\n\n## `StickyLayoutHelper`里的 item 被其他 item 覆盖\n\n`StickyLayoutHelper`里的 item 在 sticky 状态时是会被添加到 `RecyclerView` 的最顶层，如果它被覆盖，很有可能是其他 item 里设置了一个 z 参数（>= 5.0 系统）或者是被调整了 drawingOrder（`RecyclerView` 有 `setChildDrawingOrderCallback` 接口调整绘制顺序），一个典型的场景是使用了 `CardView`。解决方法是给它设置一个更大的 z 参数（>= 5.0 系统），或者检查一下有没有调整 drawingOrder 的地方。\n\n## 背景图在滑动过程中变形\n\nlayoutHelper 根据 item 元素的位置和大小确定整块背景的大小，当 layoutHelper 在有未显示元素时，不清楚自己的区域到底有多大，在可见元素变化时会动态计算区域大小，并调节背景 view 的大小，于是就导致了背景 view 中图片会根据 view 的大小去调整自己的显示。通过把背景 imageview 的 scaleType 设置为 matrix，这样就不会跟随imageview的宽高进行变化，同时根据不同手机的 dpi 提前调整好背景图片的尺寸后，再放入 imageview 中就能解决问题。[#275](https://github.com/alibaba/vlayout/issues/275)\n\n## 判断 `StickyLayoutHelper` 里的 item 是否到达顶部\n通过 `virtualLayoutManager.findFirstVisibleItemPosition()`，如果大于 `StickyLayoutHelper` 里的 item 的位置，说明已经到顶部。[#277](https://github.com/alibaba/vlayout/issues/277)\n\n## 滚动到某个 item 位置，并带偏移一个距离\n有时候自带的 scrollToPosition 方法或者 smoothScrollToPosition 方法不满足需求，可以尝试自己用动画驱动做一个滚动，下面是一种参考实现，可以基于此调整动画参数；\n\n```\npublic class RecyclerViewFlinger implements Runnable {\n\n    private static final String TAG = \"Flinger\";\n\n    private static final float MILLISECONDS_PER_INCH = 25.0F;\n\n    private RecyclerView mRecyclerView;\n\n    private int targetPosition;\n\n    private int offset;\n\n    private int direction = 1;\n\n    private ScrollFinishedListener mFinishedListener;\n\n    private int lastTop;\n\n    private int step;\n\n    public RecyclerViewFlinger(RecyclerView recyclerView, int targetPosition, int offset,\n            ScrollFinishedListener finishedListener) {\n        this.mRecyclerView = recyclerView;\n        this.targetPosition = targetPosition; //targetPosition 目标item的位置\n        this.offset = offset;//offset 是目标 item 距离顶部的偏移量\n        this.mFinishedListener = finishedListener;//可以设置一个滚动回调\n        if (mRecyclerView != null) {\n            int firstVisibleItemPosition = mRecyclerView.getFirstVisiblePosition();\n            direction = firstVisibleItemPosition < targetPosition ? 1 : -1;\n        }\n        this.step = mRecyclerView.getMeasuredHeight() / 2; //滚动步长，时间等都可以细调\n    }\n\n    @Override\n    public void run() {\n        if (mRecyclerView != null) {\n            int firstVisibleItemPosition = mRecyclerView.getFirstVisiblePosition();\n            int lastVisibleItemPosition = mRecyclerView.getLastVisiblePosition();\n            boolean inscreen = targetPosition >= firstVisibleItemPosition && targetPosition <= lastVisibleItemPosition;\n            if (inscreen) {\n                View targetView = mRecyclerView.getLayoutManager().findViewByPosition(targetPosition);\n                if (targetView != null) {\n                    int top = targetView.getTop();\n                    int dy = top - offset;\n                    mRecyclerView.smoothScrollBy(0, dy);\n                    if (lastTop == top) {\n                        if (mFinishedListener != null) {\n                            mFinishedListener.onPostExecute(targetView);\n                        }\n                    } else {\n                        lastTop = top;\n                        postOnAnimation();\n                    }\n                }\n            } else {\n                mRecyclerView.smoothScrollBy(0, step * direction);\n                postOnAnimation();\n            }\n\n        }\n    }\n\n    public void postOnAnimation() {\n        if (mRecyclerView == null) {\n            return;\n        }\n        ViewCompat.postOnAnimation(mRecyclerView, this);\n    }\n\n    public void stop() {\n        mFinishedListener = null;\n        if (mRecyclerView == null) {\n            return;\n        }\n        mRecyclerView.removeCallbacks(this);\n    }\n\n    public interface ScrollFinishedListener {\n        void onPostExecute(View view);\n    }\n\n}\n```\n\n\n\n"
  },
  {
    "path": "examples/.gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n.classpath\n.project\n.settings/\n\n# Proguard folder generated by Eclipse\nproguard/\n\n#Log Files\n*.log\n\n# OS X\n.DS_Store\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.war\n*.ear\n*.iml\n\n# IDEA Files\n.idea/\nout/\n\n# MAVEN COMPILE Files\ntarget/\nlint.xml\n"
  },
  {
    "path": "examples/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\n/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n// apply plugin: 'view-inspector'\n\n\nandroid {\n    compileSdkVersion Integer.parseInt(System.properties['compileSdkVersion'])\n    buildToolsVersion System.properties['buildToolsVersion']\n\n    defaultConfig {\n        applicationId \"com.alibaba.android.vlayout.example\"\n        minSdkVersion 14\n        targetSdkVersion Integer.parseInt(System.properties['targetSdkVersion'])\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        debug {\n            minifyEnabled false\n            debuggable true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    compile project(':vlayout')\n    compile 'com.crittercism.dexmaker:dexmaker:1.4'\n    compile 'com.squareup.picasso:picasso:2.5.2'\n    compile 'com.android.support:appcompat-v7:21.0.0'\n    compile 'com.android.support:support-annotations:21.0.0'\n    compile 'com.android.support:cardview-v7:23.1.1'\n}\n"
  },
  {
    "path": "examples/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/villadora/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n-keepattributes InnerClasses\n-keep class android.support.v7.widget.RecyclerView$LayoutParams {\n    *;\n}\n\n-keep class android.support.v7.widget.RecyclerView$ViewHolder {\n    *;\n}\n\n-keep class android.support.v7.widget.ChildHelper {\n    *;\n}\n\n-keep class android.support.v7.widget.RecyclerView$LayoutManager {\n    *;\n}\n\n-dontwarn **"
  },
  {
    "path": "examples/src/androidTest/java/com/alibaba/android/vlayout/ApplicationTest.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n\n}"
  },
  {
    "path": "examples/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<manifest\n    package=\"com.alibaba.android.vlayout.example\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity android:name=\".RootActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n        <activity android:name=\"com.alibaba.android.vlayout.example.OnePlusNLayoutActivity\">\n\n        </activity>\n\n        <activity android:name=\"com.alibaba.android.vlayout.example.MainActivity\">\n\n        </activity>\n\n        <activity android:name=\"com.alibaba.android.vlayout.example.TestActivity\">\n\n        </activity>\n\n        <activity android:name=\"com.alibaba.android.vlayout.example.VLayoutActivity\">\n\n        </activity>\n\n        <activity android:name=\"com.alibaba.android.vlayout.example.DebugActivity\">\n\n        </activity>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "examples/src/main/java/com/alibaba/android/vlayout/example/DebugActivity.java",
    "content": "package com.alibaba.android.vlayout.example;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.android.vlayout.DelegateAdapter;\nimport com.alibaba.android.vlayout.DelegateAdapter.Adapter;\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.layout.FixLayoutHelper;\nimport com.alibaba.android.vlayout.layout.GridLayoutHelper;\nimport com.alibaba.android.vlayout.layout.LinearLayoutHelper;\nimport com.alibaba.android.vlayout.layout.StickyLayoutHelper;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport static com.alibaba.android.vlayout.layout.FixLayoutHelper.TOP_RIGHT;\n\n/**\n * Created by longerian on 2017/11/14.\n *\n * @author longerian\n * @date 2017/11/14\n */\n\npublic class DebugActivity extends Activity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.main_activity);\n        RecyclerView recyclerView = (RecyclerView)findViewById(R.id.main_view);\n        VirtualLayoutManager virtualLayoutManager = new VirtualLayoutManager(this);\n        DelegateAdapter delegateAdapter = new DelegateAdapter(virtualLayoutManager);\n        List<Adapter> adapterList = new ArrayList<>();\n        adapterList.add(new SubAdapter(new LinearLayoutHelper(20), 20));\n        adapterList.add(new SubAdapter(new StickyLayoutHelper(true), 1));\n        adapterList.add(new SubAdapter(new LinearLayoutHelper(20), 20));\n        adapterList.add(new SubAdapter(new GridLayoutHelper(4), 80));\n        // adapterList.add(new SubAdapter(new FixLayoutHelper(0, 0), 1));\n        adapterList.add(new SubAdapter(new FixLayoutHelper(TOP_RIGHT, 0, 0), 1));\n        delegateAdapter.addAdapters(adapterList);\n        recyclerView.setLayoutManager(virtualLayoutManager);\n        recyclerView.setAdapter(delegateAdapter);\n    }\n\n    private static class SubAdapter extends DelegateAdapter.Adapter<SubViewHolder> {\n\n        private LayoutHelper mLayoutHelper;\n        private int mItemCount;\n\n        private SubAdapter(LayoutHelper layoutHelper, int itemCount) {\n            mLayoutHelper = layoutHelper;\n            mItemCount = itemCount;\n        }\n\n        @Override\n        public LayoutHelper onCreateLayoutHelper() {\n            return mLayoutHelper;\n        }\n\n        @Override\n        public SubViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            LayoutInflater inflater = LayoutInflater.from(parent.getContext());\n            return new SubViewHolder(inflater.inflate(R.layout.item, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(SubViewHolder holder, int position) {\n            // do nothing\n        }\n\n        @Override\n        protected void onBindViewHolderWithOffset(SubViewHolder holder, int position, int offsetTotal) {\n            super.onBindViewHolderWithOffset(holder, position, offsetTotal);\n            holder.setText(String.valueOf(offsetTotal));\n            holder.itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mItemCount;\n        }\n\n    }\n\n    private static class SubViewHolder extends RecyclerView.ViewHolder {\n\n        public static volatile int existing = 0;\n        public static int createdTimes = 0;\n\n        public SubViewHolder(View itemView) {\n            super(itemView);\n            createdTimes++;\n            existing++;\n        }\n\n        public void setText(String title) {\n            ((TextView) itemView.findViewById(R.id.title)).setText(title);\n        }\n\n        @Override\n        protected void finalize() throws Throwable {\n            existing--;\n            super.finalize();\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "examples/src/main/java/com/alibaba/android/vlayout/example/MainActivity.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.example;\n\nimport android.app.Activity;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.VirtualLayoutAdapter;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.layout.DefaultLayoutHelper;\nimport com.alibaba.android.vlayout.layout.FixLayoutHelper;\nimport com.alibaba.android.vlayout.layout.GridLayoutHelper;\nimport com.alibaba.android.vlayout.layout.ScrollFixLayoutHelper;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * Created by villadora on 15/8/3.\n */\npublic class MainActivity extends Activity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.main_activity);\n\n        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_view);\n\n        VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);\n\n        recyclerView.setLayoutManager(layoutManager);\n\n        //layoutManager.setReverseLayout(true);\n\n        recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {\n            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n                outRect.set(10, 10, 10, 10);\n            }\n        });\n\n        final List<LayoutHelper> helpers = new LinkedList<>();\n\n        final GridLayoutHelper gridLayoutHelper = new GridLayoutHelper(4);\n        gridLayoutHelper.setItemCount(25);\n\n\n        final ScrollFixLayoutHelper scrollFixLayoutHelper = new ScrollFixLayoutHelper(FixLayoutHelper.TOP_RIGHT, 100, 100);\n\n        helpers.add(DefaultLayoutHelper.newHelper(7));\n        helpers.add(scrollFixLayoutHelper);\n        helpers.add(DefaultLayoutHelper.newHelper(2));\n        helpers.add(gridLayoutHelper);\n\n        layoutManager.setLayoutHelpers(helpers);\n\n        recyclerView.setAdapter(\n                new VirtualLayoutAdapter(layoutManager) {\n                    @Override\n                    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n                        return new MainViewHolder(new TextView(MainActivity.this));\n                    }\n\n                    @Override\n                    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n                        VirtualLayoutManager.LayoutParams layoutParams = new VirtualLayoutManager.LayoutParams(\n                                ViewGroup.LayoutParams.MATCH_PARENT, 300);\n                        holder.itemView.setLayoutParams(layoutParams);\n\n                        ((TextView) holder.itemView).setText(Integer.toString(position));\n\n                        if (position == 7) {\n                            layoutParams.height = 60;\n                            layoutParams.width = 60;\n                        } else if (position > 35) {\n                            layoutParams.height = 200 + (position - 30) * 100;\n                        }\n\n                        if (position > 35) {\n                            holder.itemView.setBackgroundColor(0x66cc0000 + (position - 30) * 128);\n                        } else if (position % 2 == 0) {\n                            holder.itemView.setBackgroundColor(0xaa00ff00);\n                        } else {\n                            holder.itemView.setBackgroundColor(0xccff00ff);\n                        }\n                    }\n\n                    @Override\n                    public int getItemCount() {\n                        List<LayoutHelper> helpers = getLayoutHelpers();\n                        if (helpers == null) {\n                            return 0;\n                        }\n                        int count = 0;\n                        for (int i = 0, size = helpers.size(); i < size; i++) {\n                            count += helpers.get(i).getItemCount();\n                        }\n                        return count;\n                    }\n                });\n\n        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                recyclerView.scrollToPosition(7);\n                recyclerView.getAdapter().notifyDataSetChanged();\n            }\n        }, 6000);\n    }\n\n\n    static class MainViewHolder extends RecyclerView.ViewHolder {\n\n        public MainViewHolder(View itemView) {\n            super(itemView);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/src/main/java/com/alibaba/android/vlayout/example/OnePlusNLayoutActivity.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.example;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.NonNull;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.RecyclerView;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.alibaba.android.vlayout.DelegateAdapter;\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.RecyclablePagerAdapter;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\nimport com.alibaba.android.vlayout.layout.FixLayoutHelper;\nimport com.alibaba.android.vlayout.layout.GridLayoutHelper;\nimport com.alibaba.android.vlayout.layout.LinearLayoutHelper;\nimport com.alibaba.android.vlayout.layout.OnePlusNLayoutHelper;\nimport com.alibaba.android.vlayout.layout.OnePlusNLayoutHelperEx;\nimport com.alibaba.android.vlayout.layout.ScrollFixLayoutHelper;\nimport com.alibaba.android.vlayout.layout.StickyLayoutHelper;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * @author villadora\n */\npublic class OnePlusNLayoutActivity extends Activity {\n\n    private static final boolean BANNER_LAYOUT = true;\n\n    private static final boolean LINEAR_LAYOUT = true;\n\n    private static final boolean ONEN_LAYOUT = true;\n\n    private static final boolean GRID_LAYOUT = true;\n\n    private static final boolean STICKY_LAYOUT = true;\n\n    private static final boolean HORIZONTAL_SCROLL_LAYOUT = true;\n\n    private static final boolean SCROLL_FIX_LAYOUT = true;\n\n    private TextView mFirstText;\n    private TextView mLastText;\n\n    private TextView mCountText;\n\n    private TextView mTotalOffsetText;\n\n    private Runnable trigger;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.main_activity);\n\n        mFirstText = (TextView) findViewById(R.id.first);\n        mLastText = (TextView) findViewById(R.id.last);\n        mCountText = (TextView) findViewById(R.id.count);\n        mTotalOffsetText = (TextView) findViewById(R.id.total_offset);\n\n        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_view);\n\n        findViewById(R.id.jump).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                EditText position = (EditText) findViewById(R.id.position);\n                if (!TextUtils.isEmpty(position.getText())) {\n                    try {\n                        int pos = Integer.parseInt(position.getText().toString());\n                        recyclerView.scrollToPosition(pos);\n                    } catch (Exception e) {\n                        Log.e(\"VlayoutActivity\", e.getMessage(), e);\n                    }\n                } else {\n                    recyclerView.requestLayout();\n                }\n            }\n        });\n\n\n        final VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);\n\n        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {\n\n            }\n\n            @Override\n            public void onScrolled(RecyclerView recyclerView, int i, int i2) {\n                mFirstText.setText(\"First: \" + layoutManager.findFirstVisibleItemPosition());\n                mLastText.setText(\"Existing: \" + MainViewHolder.existing + \" Created: \" + MainViewHolder.createdTimes);\n                mCountText.setText(\"Count: \" + recyclerView.getChildCount());\n                mTotalOffsetText.setText(\"Total Offset: \" + layoutManager.getOffsetToStart());\n            }\n        });\n\n\n        recyclerView.setLayoutManager(layoutManager);\n\n        // layoutManager.setReverseLayout(true);\n\n        RecyclerView.ItemDecoration itemDecoration = new RecyclerView.ItemDecoration() {\n            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n                int position = ((LayoutParams) view.getLayoutParams()).getViewPosition();\n                outRect.set(4, 4, 4, 4);\n            }\n        };\n\n\n        final RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();\n\n        recyclerView.setRecycledViewPool(viewPool);\n\n        // recyclerView.addItemDecoration(itemDecoration);\n\n        viewPool.setMaxRecycledViews(0, 20);\n\n        final DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true);\n\n        recyclerView.setAdapter(delegateAdapter);\n\n        List<DelegateAdapter.Adapter> adapters = new LinkedList<>();\n\n\n        if (BANNER_LAYOUT) {\n            adapters.add(new SubAdapter(this, new LinearLayoutHelper(), 1) {\n\n                @Override\n                public void onViewRecycled(MainViewHolder holder) {\n                    if (holder.itemView instanceof ViewPager) {\n                        ((ViewPager) holder.itemView).setAdapter(null);\n                    }\n                }\n\n                @Override\n                public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n                    if (viewType == 1)\n                        return new MainViewHolder(\n                                LayoutInflater.from(OnePlusNLayoutActivity.this).inflate(R.layout.view_pager, parent, false));\n\n                    return super.onCreateViewHolder(parent, viewType);\n                }\n\n                @Override\n                public int getItemViewType(int position) {\n                    return 1;\n                }\n\n                @Override\n                protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {\n\n                }\n\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    if (holder.itemView instanceof ViewPager) {\n                        ViewPager viewPager = (ViewPager) holder.itemView;\n\n                        viewPager.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200));\n\n                        // from position to get adapter\n                        viewPager.setAdapter(new PagerAdapter(this, viewPool));\n                    }\n                }\n            });\n        }\n\n        if (GRID_LAYOUT) {\n            GridLayoutHelper layoutHelper;\n            layoutHelper = new GridLayoutHelper(4);\n            layoutHelper.setMargin(0, 10, 0, 10);\n            layoutHelper.setHGap(3);\n            layoutHelper.setAspectRatio(4f);\n            adapters.add(new SubAdapter(this, layoutHelper, 8));\n        }\n\n        if (HORIZONTAL_SCROLL_LAYOUT) {\n\n        }\n\n        if (GRID_LAYOUT) {\n            GridLayoutHelper layoutHelper;\n            layoutHelper = new GridLayoutHelper(2);\n            layoutHelper.setMargin(0, 10, 0, 10);\n            layoutHelper.setHGap(3);\n            layoutHelper.setAspectRatio(3f);\n            adapters.add(new SubAdapter(this, layoutHelper, 2));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(10, 10, 10, 10);\n            helper.setPadding(10, 10, 10, 10);\n            adapters.add(new SubAdapter(this, helper, 3) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n//                    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n//                    layoutParams.leftMargin = 10;\n//                    layoutParams.topMargin = 10;\n//                    layoutParams.rightMargin = 10;\n//                    layoutParams.bottomMargin = 10;\n//                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            adapters.add(new SubAdapter(this, helper, 4));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            adapters.add(new SubAdapter(this, helper, 5));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            adapters.add(new SubAdapter(this, helper, 5));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            helper.setColWeights(new float[]{40f, 45f, 15f, 60f, 0f});\n            adapters.add(new SubAdapter(this, helper, 5));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            helper.setColWeights(new float[]{20f, 80f, 0f, 60f, 20f});\n            helper.setAspectRatio(4);\n            adapters.add(new SubAdapter(this, helper, 5));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            adapters.add(new SubAdapter(this, helper, 6));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            adapters.add(new SubAdapter(this, helper, 7));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xff876384);\n            helper.setMargin(0, 10, 0, 10);\n            helper.setColWeights(new float[]{40f, 45f, 15f, 60f, 0f, 30f, 30f});\n            adapters.add(new SubAdapter(this, helper, 7));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelperEx helper = new OnePlusNLayoutHelperEx();\n            helper.setBgColor(0xffed7612);\n//            helper.setMargin(10, 10, 10, 10);\n//            helper.setPadding(10, 10, 10, 10);\n            helper.setColWeights(new float[]{30f, 20f, 50f, 40f, 30f, 35f, 35f});\n            adapters.add(new SubAdapter(this, helper, 7) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n//                    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n//                    layoutParams.leftMargin = 10;\n//                    layoutParams.topMargin = 10;\n//                    layoutParams.rightMargin = 10;\n//                    layoutParams.bottomMargin = 10;\n//                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        if (STICKY_LAYOUT) {\n            StickyLayoutHelper layoutHelper = new StickyLayoutHelper();\n            layoutHelper.setAspectRatio(4);\n            adapters.add(new SubAdapter(this, layoutHelper, 1, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));\n        }\n\n        if (SCROLL_FIX_LAYOUT) {\n            ScrollFixLayoutHelper layoutHelper = new ScrollFixLayoutHelper(FixLayoutHelper.BOTTOM_RIGHT, 20, 20);\n            layoutHelper.setShowType(ScrollFixLayoutHelper.SHOW_ON_LEAVE);\n            adapters.add(new SubAdapter(this, layoutHelper, 1) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams layoutParams = new LayoutParams(50, 50);\n                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        if (LINEAR_LAYOUT)\n            adapters.add(new SubAdapter(this, new LinearLayoutHelper(), 100));\n\n        delegateAdapter.setAdapters(adapters);\n\n        final Handler mainHandler = new Handler(Looper.getMainLooper());\n\n        trigger = new Runnable() {\n            @Override\n            public void run() {\n                // recyclerView.scrollToPosition(22);\n                // recyclerView.getAdapter().notifyDataSetChanged();\n                recyclerView.requestLayout();\n                // mainHandler.postDelayed(trigger, 1000);\n            }\n        };\n\n\n        mainHandler.postDelayed(trigger, 1000);\n    }\n\n    // RecyclableViewPager\n\n    static class PagerAdapter extends RecyclablePagerAdapter<MainViewHolder> {\n        public PagerAdapter(SubAdapter adapter, RecyclerView.RecycledViewPool pool) {\n            super(adapter, pool);\n        }\n\n        @Override\n        public int getCount() {\n            return 6;\n        }\n\n        @Override\n        public void onBindViewHolder(MainViewHolder viewHolder, int position) {\n            // only vertical\n            viewHolder.itemView.setLayoutParams(\n                    new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n            ((TextView) viewHolder.itemView.findViewById(R.id.title)).setText(\"Banner: \" + position);\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return 0;\n        }\n    }\n\n\n    static class SubAdapter extends DelegateAdapter.Adapter<MainViewHolder> {\n\n        private Context mContext;\n\n        private LayoutHelper mLayoutHelper;\n\n\n        private LayoutParams mLayoutParams;\n        private int mCount = 0;\n\n\n        public SubAdapter(Context context, LayoutHelper layoutHelper, int count) {\n            this(context, layoutHelper, count, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300));\n        }\n\n        public SubAdapter(Context context, LayoutHelper layoutHelper, int count, @NonNull LayoutParams layoutParams) {\n            this.mContext = context;\n            this.mLayoutHelper = layoutHelper;\n            this.mCount = count;\n            this.mLayoutParams = layoutParams;\n        }\n\n        @Override\n        public LayoutHelper onCreateLayoutHelper() {\n            return mLayoutHelper;\n        }\n\n        @Override\n        public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new MainViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(MainViewHolder holder, int position) {\n            // only vertical\n            holder.itemView.setLayoutParams(\n                    new LayoutParams(mLayoutParams));\n        }\n\n\n        @Override\n        protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {\n            ((TextView) holder.itemView.findViewById(R.id.title)).setText(Integer.toString(offsetTotal));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mCount;\n        }\n    }\n\n\n    static class MainViewHolder extends RecyclerView.ViewHolder {\n\n        public static volatile int existing = 0;\n        public static int createdTimes = 0;\n\n        public MainViewHolder(View itemView) {\n            super(itemView);\n            createdTimes++;\n            existing++;\n        }\n\n        @Override\n        protected void finalize() throws Throwable {\n            existing--;\n            super.finalize();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/src/main/java/com/alibaba/android/vlayout/example/RootActivity.java",
    "content": "package com.alibaba.android.vlayout.example;\n\nimport android.app.ListActivity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.view.View;\nimport android.widget.ArrayAdapter;\nimport android.widget.ListView;\n\n/**\n * Created by J!nl!n on 2017/3/9.\n */\npublic class RootActivity extends ListActivity {\n\n    private String[] mTitles = new String[]{\n            VLayoutActivity.class.getSimpleName(),\n            MainActivity.class.getSimpleName(),\n            TestActivity.class.getSimpleName(),\n            OnePlusNLayoutActivity.class.getSimpleName(),\n            DebugActivity.class.getSimpleName()\n    };\n\n    private Class[] mActivities = new Class[]{\n            VLayoutActivity.class,\n            MainActivity.class,\n            TestActivity.class,\n            OnePlusNLayoutActivity.class,\n            DebugActivity.class\n    };\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mTitles));\n    }\n\n    @Override\n    protected void onListItemClick(ListView l, View v, int position, long id) {\n        startActivity(new Intent(this, mActivities[position]));\n    }\n\n}\n"
  },
  {
    "path": "examples/src/main/java/com/alibaba/android/vlayout/example/TestActivity.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.example;\n\nimport android.app.Activity;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.StaggeredGridLayoutManager;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.EditText;\nimport android.widget.FrameLayout;\nimport android.widget.TextView;\n\n/**\n * Created by villadora on 15/8/3.\n */\npublic class TestActivity extends Activity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.main_activity);\n\n        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_view);\n\n        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);\n\n        recyclerView.setLayoutManager(layoutManager);\n\n        findViewById(R.id.jump).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                EditText position = (EditText) findViewById(R.id.position);\n                if (!TextUtils.isEmpty(position.getText())) {\n                    try {\n                        int pos = Integer.parseInt(position.getText().toString());\n                        recyclerView.scrollToPosition(pos);\n                    } catch (Exception e) {\n                        Log.e(\"VlayoutActivity\", e.getMessage(), e);\n                    }\n                }\n            }\n        });\n\n\n        recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {\n            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n                outRect.set(4, 4, 4, 4);\n            }\n        });\n\n\n        recyclerView.setAdapter(\n                new RecyclerView.Adapter() {\n                    @Override\n                    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n//                        TextView view = (TextView) LayoutInflater.from(TestActivity.this).inflate(R.layout.item, parent, false);\n//                        FrameLayout frameLayout = new FrameLayout(TestActivity.this);\n                        FrameLayout frameLayout = (FrameLayout) LayoutInflater.from(TestActivity.this).inflate(R.layout.item, parent, false);;\n//                        frameLayout.addView(view);\n                        return new MainViewHolder(frameLayout);\n                    }\n\n                    @Override\n                    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n                        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(\n                                ViewGroup.LayoutParams.MATCH_PARENT, 300);\n                        layoutParams.height = (int) (200 + (position % 15) * 10);\n\n                        holder.itemView.findViewById(R.id.title).setLayoutParams(layoutParams);\n                        if (position == 30) {\n                            StaggeredGridLayoutManager.LayoutParams lp = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n                            lp.setFullSpan(true);\n                            holder.itemView.setLayoutParams(lp);\n                        } else {\n                            ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();\n                            if (lp instanceof StaggeredGridLayoutManager.LayoutParams) {\n                                ((StaggeredGridLayoutManager.LayoutParams) lp).setFullSpan(false);\n                            }\n                        }\n                        ((TextView) holder.itemView.findViewById(R.id.title)).setText(Integer.toString(position));\n                    }\n\n                    @Override\n                    public int getItemCount() {\n                        return 60;\n                    }\n                });\n    }\n\n\n    static class MainViewHolder extends RecyclerView.ViewHolder {\n\n        public MainViewHolder(View itemView) {\n            super(itemView);\n        }\n    }\n}\n"
  },
  {
    "path": "examples/src/main/java/com/alibaba/android/vlayout/example/VLayoutActivity.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.example;\n\nimport com.alibaba.android.vlayout.DelegateAdapter;\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.RecyclablePagerAdapter;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\nimport com.alibaba.android.vlayout.extend.LayoutManagerCanScrollListener;\nimport com.alibaba.android.vlayout.extend.PerformanceMonitor;\nimport com.alibaba.android.vlayout.extend.ViewLifeCycleListener;\nimport com.alibaba.android.vlayout.layout.ColumnLayoutHelper;\nimport com.alibaba.android.vlayout.layout.FixLayoutHelper;\nimport com.alibaba.android.vlayout.layout.FloatLayoutHelper;\nimport com.alibaba.android.vlayout.layout.GridLayoutHelper;\nimport com.alibaba.android.vlayout.layout.LinearLayoutHelper;\nimport com.alibaba.android.vlayout.layout.OnePlusNLayoutHelper;\nimport com.alibaba.android.vlayout.layout.RangeGridLayoutHelper;\nimport com.alibaba.android.vlayout.layout.RangeGridLayoutHelper.GridRangeStyle;\nimport com.alibaba.android.vlayout.layout.ScrollFixLayoutHelper;\nimport com.alibaba.android.vlayout.layout.SingleLayoutHelper;\nimport com.alibaba.android.vlayout.layout.StaggeredGridLayoutHelper;\nimport com.alibaba.android.vlayout.layout.StickyLayoutHelper;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.support.annotation.NonNull;\nimport android.support.v4.view.ViewPager;\nimport android.support.v4.widget.SwipeRefreshLayout;\nimport android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;\nimport android.support.v7.widget.RecyclerView;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver.OnGlobalLayoutListener;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * @author villadora\n */\npublic class VLayoutActivity extends Activity {\n\n    private static final boolean BANNER_LAYOUT = true;\n\n    private static final boolean FIX_LAYOUT = true;\n\n    private static final boolean LINEAR_LAYOUT = true;\n\n    private static final boolean SINGLE_LAYOUT = true;\n\n    private static final boolean FLOAT_LAYOUT = true;\n\n    private static final boolean ONEN_LAYOUT = true;\n\n    private static final boolean COLUMN_LAYOUT = true;\n\n    private static final boolean GRID_LAYOUT = true;\n\n    private static final boolean STICKY_LAYOUT = true;\n\n    private static final boolean STAGGER_LAYOUT = true;\n\n    private SwipeRefreshLayout mSwipeRefreshLayout;\n\n    private TextView mFirstText;\n    private TextView mLastText;\n\n    private TextView mCountText;\n\n    private TextView mTotalOffsetText;\n\n    private Runnable trigger;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.main_activity);\n\n        mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);\n        ;\n        mFirstText = (TextView) findViewById(R.id.first);\n        mLastText = (TextView) findViewById(R.id.last);\n        mCountText = (TextView) findViewById(R.id.count);\n        mTotalOffsetText = (TextView) findViewById(R.id.total_offset);\n\n        final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_view);\n\n        final VirtualLayoutManager layoutManager = new VirtualLayoutManager(this);\n        layoutManager.setPerformanceMonitor(new PerformanceMonitor() {\n\n            long start;\n            long end;\n\n            @Override\n            public void recordStart(String phase, View view) {\n                start = System.currentTimeMillis();\n            }\n\n            @Override\n            public void recordEnd(String phase, View view) {\n                end = System.currentTimeMillis();\n                Log.d(\"VLayoutActivity\", view.getClass().getName() + \" \" + (end - start));\n            }\n        });\n        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {\n\n            }\n\n            @Override\n            public void onScrolled(RecyclerView recyclerView, int i, int i2) {\n                mFirstText.setText(\"First: \" + layoutManager.findFirstVisibleItemPosition());\n                mLastText.setText(\"Existing: \" + MainViewHolder.existing + \" Created: \" + MainViewHolder.createdTimes);\n                mCountText.setText(\"Count: \" + recyclerView.getChildCount());\n                mTotalOffsetText.setText(\"Total Offset: \" + layoutManager.getOffsetToStart());\n            }\n        });\n\n        recyclerView.setLayoutManager(layoutManager);\n\n        // layoutManager.setReverseLayout(true);\n\n        RecyclerView.ItemDecoration itemDecoration = new RecyclerView.ItemDecoration() {\n            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n                int position = ((LayoutParams) view.getLayoutParams()).getViewPosition();\n                outRect.set(4, 4, 4, 4);\n            }\n        };\n\n\n        final RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();\n\n        recyclerView.setRecycledViewPool(viewPool);\n\n        // recyclerView.addItemDecoration(itemDecoration);\n\n        viewPool.setMaxRecycledViews(0, 20);\n\n        layoutManager.setRecycleOffset(300);\n\n        // viewLifeCycleListener should be used with setRecycleOffset()\n        layoutManager.setViewLifeCycleListener(new ViewLifeCycleListener() {\n            @Override\n            public void onAppearing(View view) {\n//                Log.e(\"ViewLifeCycleTest\", \"onAppearing: \" + view);\n            }\n\n            @Override\n            public void onDisappearing(View view) {\n//                Log.e(\"ViewLifeCycleTest\", \"onDisappearing: \" + view);\n            }\n\n            @Override\n            public void onAppeared(View view) {\n//                Log.e(\"ViewLifeCycleTest\", \"onAppeared: \" + view);\n            }\n\n            @Override\n            public void onDisappeared(View view) {\n//                Log.e(\"ViewLifeCycleTest\", \"onDisappeared: \" + view);\n            }\n        });\n\n        layoutManager.setLayoutManagerCanScrollListener(new LayoutManagerCanScrollListener() {\n            @Override\n            public boolean canScrollVertically() {\n                Log.i(\"vlayout\", \"canScrollVertically: \");\n                return true;\n            }\n\n            @Override\n            public boolean canScrollHorizontally() {\n                Log.i(\"vlayout\", \"canScrollHorizontally: \");\n                return true;\n            }\n        });\n\n        final DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true);\n\n        recyclerView.setAdapter(delegateAdapter);\n\n        final List<DelegateAdapter.Adapter> adapters = new LinkedList<>();\n\n        if (BANNER_LAYOUT) {\n            adapters.add(new SubAdapter(this, new LinearLayoutHelper(), 1) {\n\n                @Override\n                public void onViewRecycled(MainViewHolder holder) {\n                    if (holder.itemView instanceof ViewPager) {\n                        ((ViewPager) holder.itemView).setAdapter(null);\n                    }\n                }\n\n                @Override\n                public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n                    if (viewType == 1)\n                        return new MainViewHolder(\n                                LayoutInflater.from(VLayoutActivity.this).inflate(R.layout.view_pager, parent, false));\n\n                    return super.onCreateViewHolder(parent, viewType);\n                }\n\n                @Override\n                public int getItemViewType(int position) {\n                    return 1;\n                }\n\n                @Override\n                protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {\n\n                }\n\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    if (holder.itemView instanceof ViewPager) {\n                        ViewPager viewPager = (ViewPager) holder.itemView;\n\n                        viewPager.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200));\n\n                        // from position to get adapter\n                        viewPager.setAdapter(new PagerAdapter(this, viewPool));\n                    }\n                }\n            });\n        }\n\n        //{\n        //    GridLayoutHelper helper = new GridLayoutHelper(10);\n        //    helper.setAspectRatio(4f);\n        //    helper.setGap(10);\n        //    adapters.add(new SubAdapter(this, helper, 80));\n        //}\n\n        if (FLOAT_LAYOUT) {\n            FloatLayoutHelper layoutHelper = new FloatLayoutHelper();\n            layoutHelper.setAlignType(FixLayoutHelper.BOTTOM_RIGHT);\n            layoutHelper.setDefaultLocation(100, 400);\n            LayoutParams layoutParams = new LayoutParams(150, 150);\n            adapters.add(new SubAdapter(this, layoutHelper, 1, layoutParams));\n        }\n\n        if (LINEAR_LAYOUT) {\n            LinearLayoutHelper layoutHelper1 = new LinearLayoutHelper();\n            layoutHelper1.setBgColor(Color.YELLOW);\n            layoutHelper1.setAspectRatio(2.0f);\n            layoutHelper1.setMargin(10, 10, 10, 10);\n            layoutHelper1.setPadding(10, 10, 10, 10);\n            LinearLayoutHelper layoutHelper2 = new LinearLayoutHelper();\n            layoutHelper2.setAspectRatio(4.0f);\n            layoutHelper2.setDividerHeight(10);\n            layoutHelper2.setMargin(10, 0, 10, 10);\n            layoutHelper2.setPadding(10, 0, 10, 10);\n            layoutHelper2.setBgColor(0xFFF5A623);\n            final Handler mainHandler = new Handler(Looper.getMainLooper());\n            adapters.add(new SubAdapter(this, layoutHelper1, 1) {\n                @Override\n                public void onBindViewHolder(final MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    final SubAdapter subAdapter = this;\n                    //mainHandler.postDelayed(new Runnable() {\n                    //    @Override\n                    //    public void run() {\n                    //        //delegateAdapter.removeAdapter(subAdapter);\n                    //        //notifyItemRemoved(1);\n                    //        holder.itemView.setVisibility(View.GONE);\n                    //        notifyItemChanged(1);\n                    //        layoutManager.runAdjustLayout();\n                    //    }\n                    //}, 2000L);\n                }\n            });\n            adapters.add(new SubAdapter(this, layoutHelper2, 6) {\n\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    if (position % 2 == 0) {\n                        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n                        layoutParams.mAspectRatio = 5;\n                        holder.itemView.setLayoutParams(layoutParams);\n                    }\n                }\n            });\n        }\n\n//        {\n//            RangeGridLayoutHelper layoutHelper = new RangeGridLayoutHelper(4);\n//            layoutHelper.setBgColor(Color.GREEN);\n//            layoutHelper.setWeights(new float[]{20f, 26.665f});\n//            layoutHelper.setPadding(15, 15, 15, 15);\n//            layoutHelper.setMargin(15, 50, 15, 150);\n//            layoutHelper.setHGap(10);\n//            layoutHelper.setVGap(10);\n//            GridRangeStyle rangeStyle = new GridRangeStyle();\n//            rangeStyle.setBgColor(Color.RED);\n//            rangeStyle.setSpanCount(2);\n//            rangeStyle.setWeights(new float[]{46.665f});\n//            rangeStyle.setPadding(15, 15, 15, 15);\n//            rangeStyle.setMargin(15, 15, 15, 15);\n//            rangeStyle.setHGap(5);\n//            rangeStyle.setVGap(5);\n//            layoutHelper.addRangeStyle(0, 7, rangeStyle);\n//\n//            GridRangeStyle rangeStyle1 = new GridRangeStyle();\n//            rangeStyle1.setBgColor(Color.YELLOW);\n//            rangeStyle1.setSpanCount(2);\n//            rangeStyle1.setWeights(new float[]{46.665f});\n//            rangeStyle1.setPadding(15, 15, 15, 15);\n//            rangeStyle1.setMargin(15, 15, 15, 15);\n//            rangeStyle1.setHGap(5);\n//            rangeStyle1.setVGap(5);\n//            layoutHelper.addRangeStyle(8, 15, rangeStyle1);\n//\n//            GridRangeStyle rangeStyle2 = new GridRangeStyle();\n//            rangeStyle2.setBgColor(Color.CYAN);\n//            rangeStyle2.setSpanCount(2);\n//            rangeStyle2.setWeights(new float[]{46.665f});\n//            rangeStyle2.setPadding(15, 15, 15, 15);\n//            rangeStyle2.setMargin(15, 15, 15, 15);\n//            rangeStyle2.setHGap(5);\n//            rangeStyle2.setVGap(5);\n//            layoutHelper.addRangeStyle(16, 22, rangeStyle2);\n//            GridRangeStyle rangeStyle3 = new GridRangeStyle();\n//            rangeStyle3.setBgColor(Color.DKGRAY);\n//            rangeStyle3.setSpanCount(1);\n//            rangeStyle3.setWeights(new float[]{46.665f});\n//            rangeStyle3.setPadding(15, 15, 15, 15);\n//            rangeStyle3.setMargin(15, 15, 15, 15);\n//            rangeStyle3.setHGap(5);\n//            rangeStyle3.setVGap(5);\n//            rangeStyle2.addChildRangeStyle(0, 2, rangeStyle3);\n//            GridRangeStyle rangeStyle4 = new GridRangeStyle();\n//            rangeStyle4.setBgColor(Color.BLUE);\n//            rangeStyle4.setSpanCount(2);\n//            rangeStyle4.setWeights(new float[]{46.665f});\n//            rangeStyle4.setPadding(15, 15, 15, 15);\n//            rangeStyle4.setMargin(15, 15, 15, 15);\n//            rangeStyle4.setHGap(5);\n//            rangeStyle4.setVGap(5);\n//            rangeStyle2.addChildRangeStyle(3, 6, rangeStyle4);\n//\n//            GridRangeStyle rangeStyle5 = new GridRangeStyle();\n//            rangeStyle5.setBgColor(Color.RED);\n//            rangeStyle5.setSpanCount(2);\n//            rangeStyle5.setPadding(15, 15, 15, 15);\n//            rangeStyle5.setMargin(15, 15, 15, 15);\n//            rangeStyle5.setHGap(5);\n//            rangeStyle5.setVGap(5);\n//            layoutHelper.addRangeStyle(23, 30, rangeStyle5);\n//            GridRangeStyle rangeStyle6 = new GridRangeStyle();\n//            rangeStyle6.setBgColor(Color.MAGENTA);\n//            rangeStyle6.setSpanCount(2);\n//            rangeStyle6.setPadding(15, 15, 15, 15);\n//            rangeStyle6.setMargin(15, 15, 15, 15);\n//            rangeStyle6.setHGap(5);\n//            rangeStyle6.setVGap(5);\n//            rangeStyle5.addChildRangeStyle(0, 7, rangeStyle6);\n//\n//            adapters.add(new SubAdapter(this, layoutHelper, 23));\n//        }\n\n        {\n            SingleLayoutHelper layoutHelper = new SingleLayoutHelper();\n            layoutHelper.setBgColor(Color.BLUE);\n            layoutHelper.setMargin(0, 30, 0, 200);\n            adapters.add(new SubAdapter(this, layoutHelper, 1, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));\n        }\n\n        if (STICKY_LAYOUT) {\n            StickyLayoutHelper layoutHelper = new StickyLayoutHelper();\n            //layoutHelper.setOffset(100);\n            layoutHelper.setAspectRatio(4);\n            adapters.add(new SubAdapter(this, layoutHelper, 1, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));\n        }\n\n        //{\n        //    final StaggeredGridLayoutHelper helper = new StaggeredGridLayoutHelper(3, 10);\n        //    helper.setBgColor(0xFF86345A);\n        //    adapters.add(new SubAdapter(this, helper, 4) {\n        //\n        //        @Override\n        //        public void onBindViewHolder(MainViewHolder holder, int position) {\n        //            super.onBindViewHolder(holder, position);\n        //            LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n        //            if (position % 2 == 0) {\n        //                layoutParams.mAspectRatio = 1.0f;\n        //            } else {\n        //                layoutParams.height = 340 + position % 7 * 20;\n        //            }\n        //            holder.itemView.setLayoutParams(layoutParams);\n        //        }\n        //    });\n        //}\n        {\n\n            final GridLayoutHelper helper = new GridLayoutHelper(3, 4);\n            helper.setBgColor(0xFF86345A);\n            adapters.add(new SubAdapter(this, helper, 4) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        {\n            RangeGridLayoutHelper layoutHelper = new RangeGridLayoutHelper(4);\n            layoutHelper.setBgColor(Color.GREEN);\n            layoutHelper.setWeights(new float[]{20f, 26.665f});\n            layoutHelper.setPadding(15, 15, 15, 15);\n            layoutHelper.setMargin(15, 15, 15, 15);\n            layoutHelper.setHGap(10);\n            layoutHelper.setVGap(10);\n            GridRangeStyle rangeStyle = new GridRangeStyle();\n            rangeStyle.setBgColor(Color.RED);\n            rangeStyle.setSpanCount(2);\n            rangeStyle.setWeights(new float[]{46.665f});\n            rangeStyle.setPadding(15, 15, 15, 15);\n            rangeStyle.setMargin(15, 15, 15, 15);\n            rangeStyle.setHGap(5);\n            rangeStyle.setVGap(5);\n            layoutHelper.addRangeStyle(4, 7, rangeStyle);\n            GridRangeStyle rangeStyle1 = new GridRangeStyle();\n            rangeStyle1.setBgColor(Color.YELLOW);\n            rangeStyle1.setSpanCount(2);\n            rangeStyle1.setWeights(new float[]{46.665f});\n            rangeStyle1.setPadding(15, 15, 15, 15);\n            rangeStyle1.setMargin(15, 15, 15, 15);\n            rangeStyle1.setHGap(5);\n            rangeStyle1.setVGap(5);\n            layoutHelper.addRangeStyle(8, 11, rangeStyle1);\n            adapters.add(new SubAdapter(this, layoutHelper, 16));\n\n        }\n\n        if (SINGLE_LAYOUT) {\n            SingleLayoutHelper layoutHelper = new SingleLayoutHelper();\n            layoutHelper.setBgColor(Color.rgb(135, 225, 90));\n            layoutHelper.setAspectRatio(4);\n            layoutHelper.setMargin(10, 20, 10, 20);\n            layoutHelper.setPadding(10, 10, 10, 10);\n            adapters.add(new SubAdapter(this, layoutHelper, 1, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));\n        }\n\n        if (COLUMN_LAYOUT) {\n            ColumnLayoutHelper layoutHelper = new ColumnLayoutHelper();\n            layoutHelper.setBgColor(0xff00f0f0);\n            layoutHelper.setWeights(new float[]{40.0f, Float.NaN, 40});\n            adapters.add(new SubAdapter(this, layoutHelper, 5) {\n\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    if (position == 0) {\n                        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n                        layoutParams.mAspectRatio = 4;\n                        holder.itemView.setLayoutParams(layoutParams);\n                    } else {\n                        LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300);\n                        layoutParams.mAspectRatio = Float.NaN;\n                        holder.itemView.setLayoutParams(layoutParams);\n                    }\n                }\n\n            });\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();\n            helper.setBgColor(0xff876384);\n            helper.setAspectRatio(4.0f);\n            helper.setColWeights(new float[]{40f, 45f});\n            helper.setMargin(10, 20, 10, 20);\n            helper.setPadding(10, 10, 10, 10);\n            adapters.add(new SubAdapter(this, helper, 2));\n        }\n\n        if (ONEN_LAYOUT) {\n            OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();\n            helper.setBgColor(0xffef8ba3);\n            helper.setAspectRatio(2.0f);\n            helper.setColWeights(new float[]{40f});\n            helper.setRowWeight(30f);\n            helper.setMargin(10, 20, 10, 20);\n            helper.setPadding(10, 10, 10, 10);\n            adapters.add(new SubAdapter(this, helper, 4) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams lp = (LayoutParams) holder.itemView.getLayoutParams();\n                    if (position == 0) {\n                        lp.rightMargin = 1;\n                    } else if (position == 1) {\n\n                    } else if (position == 2) {\n                        lp.topMargin = 1;\n                        lp.rightMargin = 1;\n                    }\n                }\n            });\n        }\n\n        if (ONEN_LAYOUT) {\n            adapters.add(new SubAdapter(this, new OnePlusNLayoutHelper(), 0));\n            OnePlusNLayoutHelper helper = new OnePlusNLayoutHelper();\n            helper.setBgColor(0xff87e543);\n            helper.setAspectRatio(1.8f);\n            helper.setColWeights(new float[]{33.33f, 50f, 40f});\n            helper.setMargin(10, 20, 10, 20);\n            helper.setPadding(10, 10, 10, 10);\n            LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            adapters.add(new SubAdapter(this, helper, 3, lp) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams lp = (LayoutParams) holder.itemView.getLayoutParams();\n                    if (position == 0) {\n                        lp.rightMargin = 1;\n                    }\n                }\n            });\n        }\n\n        if (COLUMN_LAYOUT) {\n            adapters.add(new SubAdapter(this, new ColumnLayoutHelper(), 0));\n            adapters.add(new SubAdapter(this, new ColumnLayoutHelper(), 4));\n        }\n\n        if (FIX_LAYOUT) {\n            FixLayoutHelper layoutHelper = new FixLayoutHelper(10, 10);\n            adapters.add(new SubAdapter(this, layoutHelper, 0));\n\n            layoutHelper = new FixLayoutHelper(FixLayoutHelper.TOP_RIGHT, 20, 20);\n\n            adapters.add(new SubAdapter(this, layoutHelper, 1) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);\n                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        //if (STICKY_LAYOUT) {\n        //    StickyLayoutHelper layoutHelper = new StickyLayoutHelper(false);\n        //    adapters.add(new SubAdapter(this, layoutHelper, 0));\n        //    layoutHelper = new StickyLayoutHelper(false);\n        //    layoutHelper.setOffset(100);\n        //    adapters.add(new SubAdapter(this, layoutHelper, 1, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100)));\n        //}\n\n        if (GRID_LAYOUT) {\n            GridLayoutHelper layoutHelper = new GridLayoutHelper(2);\n            layoutHelper.setMargin(7, 0, 7, 0);\n            layoutHelper.setWeights(new float[]{46.665f});\n            layoutHelper.setHGap(3);\n            adapters.add(new SubAdapter(this, layoutHelper, 2));\n\n            layoutHelper = new GridLayoutHelper(4);\n            layoutHelper.setWeights(new float[]{20f, 26.665f});\n            layoutHelper.setMargin(7, 0, 7, 0);\n            layoutHelper.setHGap(3);\n            adapters.add(new SubAdapter(this, layoutHelper, 8));\n        }\n\n\n        if (GRID_LAYOUT) {\n            adapters.add(new SubAdapter(this, new GridLayoutHelper(4), 0));\n\n            GridLayoutHelper helper = new GridLayoutHelper(4);\n            helper.setAspectRatio(4f);\n            //helper.setColWeights(new float[]{40, 20, 30, 30});\n            // helper.setMargin(0, 10, 0, 10);\n            helper.setGap(10);\n            adapters.add(new SubAdapter(this, helper, 80) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams lp = (LayoutParams) holder.itemView.getLayoutParams();\n                    // lp.bottomMargin = 1;\n                    // lp.rightMargin = 1;\n                }\n            });\n        }\n\n        if (FIX_LAYOUT) {\n            adapters.add(new SubAdapter(this, new ScrollFixLayoutHelper(20, 20), 1) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams layoutParams = new LayoutParams(200, 200);\n                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        if (LINEAR_LAYOUT)\n            adapters.add(new SubAdapter(this, new LinearLayoutHelper(), 10));\n\n        if (GRID_LAYOUT) {\n            GridLayoutHelper helper = new GridLayoutHelper(3);\n            helper.setMargin(0, 10, 0, 10);\n            adapters.add(new SubAdapter(this, helper, 3));\n        }\n\n        if (STAGGER_LAYOUT) {\n            // adapters.add(new SubAdapter(this, new StaggeredGridLayoutHelper(2, 0), 0));\n            final StaggeredGridLayoutHelper helper = new StaggeredGridLayoutHelper(2, 10);\n            helper.setMargin(20, 10, 10, 10);\n            helper.setPadding(10, 10, 20, 10);\n            helper.setBgColor(0xFF86345A);\n            adapters.add(new SubAdapter(this, helper, 27) {\n                @Override\n                public void onBindViewHolder(MainViewHolder holder, int position) {\n                    super.onBindViewHolder(holder, position);\n                    LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 200);\n                    if (position % 2 == 0) {\n                        layoutParams.mAspectRatio = 1.0f;\n                    } else {\n                        layoutParams.height = 340 + position % 7 * 20;\n                    }\n                    holder.itemView.setLayoutParams(layoutParams);\n                }\n            });\n        }\n\n        if (COLUMN_LAYOUT) {\n            // adapters.add(new SubAdapter(this, new ColumnLayoutHelper(), 3));\n        }\n\n        if (GRID_LAYOUT) {\n            // adapters.add(new SubAdapter(this, new GridLayoutHelper(4), 24));\n        }\n\n        adapters.add(\n                new FooterAdapter(recyclerView, VLayoutActivity.this, new GridLayoutHelper(1), 1));\n\n        delegateAdapter.setAdapters(adapters);\n\n\n        final Handler mainHandler = new Handler(Looper.getMainLooper());\n\n        trigger = new Runnable() {\n            @Override\n            public void run() {\n                //recyclerView.scrollToPosition(22);\n                //recyclerView.getAdapter().notifyDataSetChanged();\n                //mainHandler.postDelayed(trigger, 1000);\n                //List<DelegateAdapter.Adapter> newAdapters = new ArrayList<>();\n                //newAdapters.add((new SubAdapter(VLayoutActivity.this, new ColumnLayoutHelper(), 3)));\n                //newAdapters.add((new SubAdapter(VLayoutActivity.this, new GridLayoutHelper(4), 24)));\n                //delegateAdapter.addAdapter(0, new SubAdapter(VLayoutActivity.this, new ColumnLayoutHelper(), 3));\n                //delegateAdapter.addAdapter(1, new SubAdapter(VLayoutActivity.this, new GridLayoutHelper(4), 24));\n                //delegateAdapter.notifyDataSetChanged();\n            }\n        };\n\n        findViewById(R.id.jump).setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                EditText position = (EditText) findViewById(R.id.position);\n                if (!TextUtils.isEmpty(position.getText())) {\n                    try {\n                        int pos = Integer.parseInt(position.getText().toString());\n                        recyclerView.scrollToPosition(pos);\n                    } catch (Exception e) {\n                        Log.e(\"VlayoutActivity\", e.getMessage(), e);\n                    }\n                } else {\n                    recyclerView.requestLayout();\n                }\n                //FooterAdapter footer = (FooterAdapter)adapters.get(adapters.size() - 1);\n                //footer.toggleFoot();\n            }\n        });\n\n\n        mainHandler.postDelayed(trigger, 1000);\n\n        mSwipeRefreshLayout.setOnRefreshListener(new OnRefreshListener() {\n            @Override\n            public void onRefresh() {\n                mainHandler.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mSwipeRefreshLayout.setRefreshing(false);\n                    }\n                }, 2000L);\n            }\n        });\n        setListenerToRootView();\n    }\n\n    boolean isOpened = false;\n\n    public void setListenerToRootView() {\n        final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);\n        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {\n            @Override\n            public void onGlobalLayout() {\n\n                int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();\n                if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.\n                    if (isOpened == false) {\n                        //Do two things, make the view top visible and the editText smaller\n                    }\n                    isOpened = true;\n                } else if (isOpened == true) {\n                    isOpened = false;\n                    final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_view);\n                    recyclerView.getAdapter().notifyDataSetChanged();\n                }\n            }\n        });\n    }\n\n    static class FooterAdapter extends DelegateAdapter.Adapter<MainViewHolder> {\n\n        private RecyclerView mRecyclerView;\n\n        private Context mContext;\n\n        private LayoutHelper mLayoutHelper;\n\n        private LayoutParams mLayoutParams;\n        private int mCount = 0;\n\n        private boolean showFooter = false;\n\n        public FooterAdapter(RecyclerView recyclerView, Context context, LayoutHelper layoutHelper, int count) {\n            this(recyclerView, context, layoutHelper, count, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300));\n        }\n\n        public FooterAdapter(RecyclerView recyclerView, Context context, LayoutHelper layoutHelper, int count, @NonNull LayoutParams layoutParams) {\n            this.mRecyclerView = recyclerView;\n            this.mContext = context;\n            this.mLayoutHelper = layoutHelper;\n            this.mCount = count;\n            this.mLayoutParams = layoutParams;\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return 100;\n        }\n\n        @Override\n        public LayoutHelper onCreateLayoutHelper() {\n            return mLayoutHelper;\n        }\n\n        @Override\n        public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new MainViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(MainViewHolder holder, int position) {\n            LayoutParams lp = (LayoutParams) holder.itemView.getLayoutParams();\n            if (showFooter) {\n                lp.height = 300;\n            } else {\n                lp.height = 0;\n            }\n            holder.itemView.setLayoutParams(lp);\n        }\n\n\n        @Override\n        protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {\n            ((TextView) holder.itemView.findViewById(R.id.title)).setText(Integer.toString(offsetTotal));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mCount;\n        }\n\n        public void toggleFoot() {\n            this.showFooter = !this.showFooter;\n            mRecyclerView.getAdapter().notifyItemChanged(205);\n            mRecyclerView.post(new Runnable() {\n                @Override\n                public void run() {\n                    mRecyclerView.scrollToPosition(205);\n                    mRecyclerView.requestLayout();\n                }\n            });\n        }\n\n    }\n\n    // RecyclableViewPager\n\n    static class PagerAdapter extends RecyclablePagerAdapter<MainViewHolder> {\n        public PagerAdapter(SubAdapter adapter, RecyclerView.RecycledViewPool pool) {\n            super(adapter, pool);\n        }\n\n        @Override\n        public int getCount() {\n            return 6;\n        }\n\n        @Override\n        public void onBindViewHolder(MainViewHolder viewHolder, int position) {\n            // only vertical\n            viewHolder.itemView.setLayoutParams(\n                    new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n            ((TextView) viewHolder.itemView.findViewById(R.id.title)).setText(\"Banner: \" + position);\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return 0;\n        }\n    }\n\n\n    static class SubAdapter extends DelegateAdapter.Adapter<MainViewHolder> {\n\n        private Context mContext;\n\n        private LayoutHelper mLayoutHelper;\n\n\n        private LayoutParams mLayoutParams;\n        private int mCount = 0;\n\n\n        public SubAdapter(Context context, LayoutHelper layoutHelper, int count) {\n            this(context, layoutHelper, count, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300));\n        }\n\n        public SubAdapter(Context context, LayoutHelper layoutHelper, int count, @NonNull LayoutParams layoutParams) {\n            this.mContext = context;\n            this.mLayoutHelper = layoutHelper;\n            this.mCount = count;\n            this.mLayoutParams = layoutParams;\n        }\n\n        @Override\n        public LayoutHelper onCreateLayoutHelper() {\n            return mLayoutHelper;\n        }\n\n        @Override\n        public MainViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new MainViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item, parent, false));\n        }\n\n        @Override\n        public void onBindViewHolder(MainViewHolder holder, int position) {\n            // only vertical\n            holder.itemView.setLayoutParams(\n                    new LayoutParams(mLayoutParams));\n        }\n\n\n        @Override\n        protected void onBindViewHolderWithOffset(MainViewHolder holder, int position, int offsetTotal) {\n            ((TextView) holder.itemView.findViewById(R.id.title)).setText(Integer.toString(offsetTotal));\n        }\n\n        @Override\n        public int getItemCount() {\n            return mCount;\n        }\n    }\n\n\n    static class MainViewHolder extends RecyclerView.ViewHolder {\n\n        public static volatile int existing = 0;\n        public static int createdTimes = 0;\n\n        public MainViewHolder(View itemView) {\n            super(itemView);\n            createdTimes++;\n            existing++;\n        }\n\n        @Override\n        protected void finalize() throws Throwable {\n            existing--;\n            super.finalize();\n        }\n    }\n}\n"
  },
  {
    "path": "examples/src/main/res/drawable/border_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n    <padding\n        android:bottom=\"2dp\"\n        android:left=\"2dp\"\n        android:right=\"2dp\"\n        android:top=\"2dp\"/>\n    <stroke\n        android:width=\"2px\"\n        android:color=\"#99000000\"/>\n</shape>"
  },
  {
    "path": "examples/src/main/res/drawable/item_background.xml",
    "content": "<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/item_activated_color\"\n          android:state_activated=\"true\"/>\n\n    <item android:drawable=\"@color/item_pressed_color\"\n          android:state_pressed=\"true\"/>\n\n    <item android:drawable=\"@color/item_default_color\"/>\n</selector>"
  },
  {
    "path": "examples/src/main/res/layout/card_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<android.support.v7.widget.CardView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/border_bg\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@drawable/item_background\"\n        android:gravity=\"center\"\n        android:textColor=\"#999999\"\n        android:textSize=\"22sp\"\n        android:textStyle=\"bold\"/>\n</android.support.v7.widget.CardView>"
  },
  {
    "path": "examples/src/main/res/layout/item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/border_bg\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@drawable/item_background\"\n        android:gravity=\"center\"\n        android:textColor=\"#999999\"\n        android:textSize=\"22sp\"\n        android:textStyle=\"bold\"/>\n</FrameLayout>"
  },
  {
    "path": "examples/src/main/res/layout/main_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n             android:layout_width=\"match_parent\"\n             android:layout_height=\"match_parent\">\n\n    <android.support.v4.widget.SwipeRefreshLayout\n            android:id=\"@+id/swipe_container\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n        <android.support.v7.widget.RecyclerView\n            android:id=\"@+id/main_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"#aaaaaa\"\n            android:clipToPadding=\"true\"\n            android:paddingLeft=\"0dp\"\n            android:paddingRight=\"0dp\"\n            android:requiresFadingEdge=\"none\"\n            android:scrollbars=\"vertical\"/>\n    </android.support.v4.widget.SwipeRefreshLayout>\n\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|center\"\n        android:orientation=\"vertical\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <TextView\n                android:id=\"@+id/first\"\n                style=\"@style/IndicateText\"/>\n\n            <TextView\n                android:id=\"@+id/last\"\n                style=\"@style/IndicateText\"/>\n\n            <TextView\n                android:id=\"@+id/count\"\n                style=\"@style/IndicateText\"/>\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"#33000000\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"Jump Position\"/>\n\n            <EditText\n                android:id=\"@+id/position\"\n                android:layout_width=\"100dp\"\n                android:layout_height=\"wrap_content\"\n                android:numeric=\"integer\"/>\n\n            <Button\n                android:id=\"@+id/jump\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"Jump\"/>\n\n            <TextView\n                android:id=\"@+id/total_offset\"\n                style=\"@style/IndicateText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@android:color/transparent\"\n                android:paddingLeft=\"20dp\"/>\n        </LinearLayout>\n    </LinearLayout>\n</FrameLayout>"
  },
  {
    "path": "examples/src/main/res/layout/view_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<android.support.v4.view.ViewPager\n    android:id=\"@+id/pager\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"280dp\">\n\n\n</android.support.v4.view.ViewPager>"
  },
  {
    "path": "examples/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<resources>\n    <color name=\"item_border_color\">#666666</color>\n    <color name=\"item_default_color\">#22EEEEEE</color>\n    <color name=\"item_pressed_color\">#CCCCCC</color>\n    <color name=\"item_activated_color\">#77CEEE</color>\n    \n</resources>"
  },
  {
    "path": "examples/src/main/res/values/strings.xml",
    "content": "<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<resources>\n    <string name=\"app_name\">Example for VirtualLayout</string>\n</resources>\n"
  },
  {
    "path": "examples/src/main/res/values/styles.xml",
    "content": "<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n    </style>\n\n\n    <style name=\"IndicateText\">\n        <item name=\"android:layout_width\">0dp</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:padding\">4dp</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:textColor\">#ffffff</item>\n        <item name=\"android:background\">#33000000</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#\n# MIT License\n#\n# Copyright (c) 2016 Alibaba Group\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n#\n\n#Tue Nov 29 17:56:32 CST 2016\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-3.3-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "#\n# MIT License\n#\n# Copyright (c) 2016 Alibaba Group\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n#\n\n# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n\nGROUP=com.alibaba.android\nARTIFACT=vlayout\nVERSION=1\nVERSION_NAME=1.2.39\nPACKAGING_TYPE=aar\nuseNewSupportLibrary=true\nsystemProp.compileSdkVersion=26\nsystemProp.targetSdkVersion=26\nsystemProp.buildToolsVersion=26.0.2\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "jcenterDeploy.gradle",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2017 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\napply plugin: 'com.jfrog.bintray'\napply plugin: 'com.jfrog.artifactory'\n\nversion = libraryVersion\n\nif (project.hasProperty('deployVersion')) {\n    version = project.getProperty('deployVersion')\n}\n\nif (project.hasProperty(\"android\")) { // Android libraries\n    task sourcesJar(type: Jar) {\n        classifier = 'sources'\n        from android.sourceSets.main.java.srcDirs\n    }\n\n//    task javadoc(type: Javadoc) {\n//        source = android.sourceSets.main.java.srcDirs\n//        classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n\n//        android.libraryVariants.all { variant ->\n//            println variant.javaCompile.classpath.files\n//            if(variant.name == 'release') { //我们只需 release 的 javadoc\n//                task(\"generate${variant.name.capitalize()}Javadoc\", type: Javadoc) {\n//                    // title = ''\n//                    // description = ''\n//                    source = variant.javaCompile.source\n//                    classpath = files(variant.javaCompile.classpath.files, project.android.getBootClasspath())\n//                    options {\n//                        encoding \"utf-8\"\n//                        links \"http://docs.oracle.com/javase/7/docs/api/\"\n//                        linksOffline \"http://d.android.com/reference\", \"${android.sdkDirectory}/docs/reference\"\n//                    }\n//                    exclude '**/BuildConfig.java'\n//                    exclude '**/R.java'\n//                }\n//                task(\"javadoc${variant.name.capitalize()}Jar\", type: Jar, dependsOn: \"generate${variant.name.capitalize()}Javadoc\") {\n//                    classifier = 'javadoc'\n//                    from tasks.getByName(\"generate${variant.name.capitalize()}Javadoc\").destinationDir\n//                }\n//                artifacts {\n//                    archives tasks.getByName(\"javadoc${variant.name.capitalize()}Jar\")\n//                }\n//            }\n//        }\n//\n//    }\n} else { // Java libraries\n    task sourcesJar(type: Jar, dependsOn: classes) {\n        classifier = 'sources'\n        from sourceSets.main.allSource\n    }\n}\n\n//task javadocJar(type: Jar, dependsOn: javadoc) {\n//    classifier = 'javadoc'\n//    from javadoc.destinationDir\n//}\n\nartifacts {\n//    archives javadocJar\n    archives sourcesJar\n}\n\n// Bintray\nProperties properties = new Properties()\nFile localProperties = project.rootProject.file('local.properties')\nif (localProperties.exists()) {\n    properties.load(project.rootProject.file('local.properties').newDataInputStream())\n}\n\nbintray {\n    user = properties.getProperty(\"bintray.user\")\n    key = properties.getProperty(\"bintray.apikey\")\n    if (!user) {\n        user = project.hasProperty('bintrayUser') ? project.getProperty('bintrayUser') : \"\"\n    }\n    if (!key) {\n        key = project.hasProperty('bintrayApikey') ? project.getProperty('bintrayApikey') : \"\"\n    }\n\n    configurations = ['archives']\n    pkg {\n        repo = bintrayRepo\n        name = bintrayName\n        desc = libraryDescription\n        websiteUrl = siteUrl\n        vcsUrl = gitUrl\n        licenses = allLicenses\n        publish = true\n        publicDownloadNumbers = true\n        version {\n            desc = libraryDescription\n            gpg {\n                sign = true //Determines whether to GPG sign the files. The default is false\n                passphrase = properties.getProperty(\"bintray.gpg.password\")\n                if (!passphrase) {\n                    passphrase = project.hasProperty('bintrayGPG') ? project.getProperty('bintrayGPG') : \"\"\n                }\n                //Optional. The passphrase for GPG signing'\n            }\n\n            mavenCentralSync {\n                sync = true\n                user = properties.getProperty('bintray.oss.user')\n                if (!user) {\n                    user = project.hasProperty('bintray.oss.user') ? project.getProperty('bintray.oss.user') : \"\"\n                }\n                password = properties.getProperty('bintray.oss.password')\n                if (!password) {\n                    password = project.hasProperty('bintray.oss.password') ? project.getProperty('bintray.oss.password') : \"\"\n                }\n                close = '1'\n            }\n        }\n    }\n}\n\nartifactory {\n    contextUrl = 'http://oss.jfrog.org/artifactory' //The base Artifactory URL if not overridden by the publisher/resolver\n    resolve {\n        repository {\n            repoKey = 'libs-release'\n        }\n    }\n    publish {\n        repository {\n            repoKey = 'oss-snapshot-local' //The Artifactory repository key to publish to\n            username = bintray.user\n            password = bintray.key\n            maven = true\n        }\n        defaults {\n//            the name is the same with that defined in bintray.configurations\n            publishConfigs('archives')\n        }\n    }\n}\n\nbintrayUpload.onlyIf {\n    !version.endsWith(\"-SNAPSHOT\")\n}\n\nartifactoryPublish.onlyIf {\n    version.endsWith(\"-SNAPSHOT\")\n}\n\ntask deploy(dependsOn: ['install', 'bintrayUpload', 'artifactoryPublish']) << {\n    println \"deploy ....\"\n}"
  },
  {
    "path": "jcenterInstall.gradle",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2017 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\napply plugin: 'com.github.dcendents.android-maven'\n\ngroup = GROUP\n\ninstall {\n    repositories.mavenInstaller {\n        // This generates POM.xml with proper parameters\n        pom {\n            project {\n                packaging 'aar'\n                groupId publishedGroupId\n                artifactId artifact\n\n                // Add your description here\n                name libraryName\n                description libraryDescription\n                url siteUrl\n\n                // Set your license\n                licenses {\n                    license {\n                        name licenseName\n                        url licenseUrl\n                    }\n                }\n                developers {\n                    developer {\n                        id developerId\n                        name developerName\n                        email developerEmail\n                    }\n                }\n                scm {\n                    connection gitUrl\n                    developerConnection gitUrl\n                    url siteUrl\n\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\ninclude ':vlayout'\ninclude ':examples'\n"
  },
  {
    "path": "vlayout/.gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n.classpath\n.project\n.settings/\n\n# Proguard folder generated by Eclipse\nproguard/\n\n#Log Files\n*.log\n\n# OS X\n.DS_Store\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.war\n*.ear\n*.iml\n\n# IDEA Files\n.idea/\nout/\n\n# MAVEN COMPILE Files\ntarget/\nlint.xml\n"
  },
  {
    "path": "vlayout/DESIGN.md",
    "content": "# VirtualLayout 设计说明\n\n# VirtualLayout Design Note"
  },
  {
    "path": "vlayout/build.gradle",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\napply plugin: 'com.android.library'\n\nbuildscript {\n    repositories {\n        maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }\n        maven { url \"http://oss.jfrog.org/oss-snapshot-local/\" }\n        mavenCentral()\n        jcenter()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.3.0'\n    }\n}\n\nrepositories {\n    maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }\n    maven { url \"http://oss.jfrog.org/oss-snapshot-local/\" }\n    jcenter()\n    mavenLocal()\n}\n\n\next {\n    bintrayRepo = 'Tangram'\n    bintrayName = 'vlayout'\n\n    publishedGroupId = project.hasProperty('GROUP') ? GROUP : ''\n    libraryName = project.hasProperty('ARTIFACT') ? ARTIFACT : ''\n    artifact = project.hasProperty('ARTIFACT') ? ARTIFACT : ''\n\n    libraryDescription = 'Project vlayout is a powerfull LayoutManager extension for RecyclerView, it provides a group of layouts for RecyclerView. Make it able to handle a complicate situation when grid, list and other layouts in the same recyclerview.'\n\n    siteUrl = 'https://github.com/alibaba/vlayout'\n    gitUrl = 'https://github.com/alibaba/vlayout.git'\n\n    libraryVersion = project.hasProperty('VERSION_NAME') ? VERSION_NAME : ''\n\n    developerId = 'longerian'\n    developerName = 'longerian'\n    developerEmail = 'xlhongultimate@gmail.com'\n\n    licenseName = 'MIT'\n    licenseUrl = 'https://opensource.org/licenses/MIT'\n    allLicenses = [\"MIT\"]\n}\n\ndef VERSION = System.properties['version'] ?: '10'\ndef VERSION_NAME = System.properties['versionName'] ?: '0.1.0'\n\nandroid {\n    compileSdkVersion Integer.parseInt(System.properties['compileSdkVersion'] ?: '22')\n    buildToolsVersion System.properties['buildToolsVersion']\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion Integer.parseInt(System.properties['targetSdkVersion'] ?: '14')\n        versionCode Integer.parseInt(VERSION ?: '10')\n        versionName VERSION_NAME\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_7\n        targetCompatibility JavaVersion.VERSION_1_7\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    // compile project(':extension')\n    if (project.hasProperty('useNewSupportLibrary')) {\n        compile 'com.android.support:recyclerview-v7:25.2.0@aar'\n        compile('com.android.support:support-v4:25.2.0@aar')\n        compile 'com.android.support:support-annotations:25.2.0'\n        compile 'com.android.support:support-compat:25.2.0'\n        compile 'com.android.support:support-core-ui:25.2.0'\n    } else {\n        compile 'com.android.support:recyclerview-v7:23.1.1@aar'\n        compile('com.android.support:support-v4:23.1.1@aar') {\n            exclude group: 'com.android.support', module: 'support-annotations'\n        }\n        compile 'com.android.support:support-annotations:23.1.1'\n    }\n\n    androidTestCompile \"org.robolectric:robolectric:3.0\"\n}\n\n\nFile deployConfig = rootProject.file('deploy.gradle')\nif (deployConfig.exists()) {\n    apply from: rootProject.file('deploy.gradle')\n}\n\ndeployConfig = rootProject.file('jcenterInstall.gradle')\nif (deployConfig.exists()) {\n    apply from: rootProject.file('jcenterInstall.gradle')\n}\n\ndeployConfig = rootProject.file('jcenterDeploy.gradle')\nif (deployConfig.exists()) {\n    apply from: rootProject.file('jcenterDeploy.gradle')\n}"
  },
  {
    "path": "vlayout/jacoco.gradle",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\napply plugin: 'jacoco'\n\njacoco {\n    toolVersion = \"0.7.1.201405082137\"\n    reportsDir = file(\"$buildDir/reports/jacoco\")\n}\n\njacocoTestReport {\n    reports {\n        xml.enabled false\n        csv.enabled false\n        html.destination \"${buildDir}/jacocoHtml\"\n    }\n}\n\n\nandroid.testOptions.unitTests.all {\n    // configure the set of classes for JUnit tests\n    include 'com/alibaba/android/**/*Test.class'\n    //exclude '**/espresso/**/*.class'\n\n    // configure max heap size of the test JVM\n    maxHeapSize = \"2048m\"\n}\n\n\ndef coverageSourceDirs = [\n        '../app/src/main/java'\n]\n\ntask jacocoTestReport(type: JacocoReport, dependsOn: \"testDebug\") {\n    group = \"Reporting\"\n\n    description = \"Generate Jacoco coverage reports\"\n\n    classDirectories = fileTree(\n            dir: '../app/build/intermediates/classes/debug',\n            excludes: ['**/R.class',\n                       '**/R$*.class',\n                       '**/*$ViewInjector*.*',\n                       '**/BuildConfig.*',\n                       '**/Manifest*.*']\n    )\n\n    additionalSourceDirs = files(coverageSourceDirs)\n    sourceDirectories = files(coverageSourceDirs)\n    executionData = files('../app/build/jacoco/testDebug.exec')\n\n    reports {\n        xml.enabled = false\n        html.enabled = true\n    }\n\n}"
  },
  {
    "path": "vlayout/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/villadora/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n-keepattributes InnerClasses\n-keep class android.support.v7.widget.RecyclerView$LayoutParams {\n    *;\n}\n\n-keep class android.support.v7.widget.RecyclerView$ViewHolder {\n    *;\n}\n\n-keep class android.support.v7.widget.ChildHelper {\n    *;\n}\n\n-keep class android.support.v7.widget.RecyclerView$LayoutManager {\n    *;\n}\n\n-keep class android.support.v7.widget.LinearLayoutManager {\n    void ensureLayoutState();\n    void resolveShouldLayoutReverse();\n}"
  },
  {
    "path": "vlayout/src/androidTest/java/com/alibaba/android/vlayout/ViewHolderHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.v7.widget.RecyclerView;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\n/**\n * Created by villadora on 16/2/24.\n */\npublic class ViewHolderHelper {\n\n    private static Field vhField = null;\n\n\n    public static void setField(RecyclerView.ViewHolder holder, String fieldName, Object value) {\n        try {\n            Field f = RecyclerView.LayoutParams.class.getDeclaredField(fieldName);\n            f.setAccessible(true);\n            f.set(holder, value);\n        } catch (NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n    }\n\n\n    public static RecyclerView.ViewHolder getViewHolder(RecyclerView.LayoutParams params) {\n        try {\n            if (vhField == null) {\n                vhField = RecyclerView.LayoutParams.class.getDeclaredField(\"mViewHolder\");\n                vhField.setAccessible(true);\n            }\n\n            //vhField.set(params, holder);\n\n            return (RecyclerView.ViewHolder) vhField.get(params);\n        } catch (NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n\n        return null;\n    }\n\n\n    private static Method vhSetFlags;\n\n    public static void addViewHolderFlag(RecyclerView.ViewHolder holder, int flag) {\n        try {\n            if (vhSetFlags == null) {\n                vhSetFlags = RecyclerView.ViewHolder.class.getDeclaredMethod(\"addFlags\", int.class);\n                vhSetFlags.setAccessible(true);\n            }\n\n            vhSetFlags.invoke(holder, flag);\n        } catch (Exception e) {\n\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/androidTest/java/com/alibaba/android/vlayout/VirtualLayoutManagerTest.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Looper;\nimport android.support.v7.widget.RecyclerView;\nimport android.test.ActivityInstrumentationTestCase2;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.HORIZONTAL;\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * Created by villadora on 16/2/24.\n */\npublic class VirtualLayoutManagerTest extends ActivityInstrumentationTestCase2<Activity> {\n\n    private static final String TAG = \"VLMTest\";\n\n    private static final boolean DEBUG = false;\n\n    protected RecyclerView mRecyclerView;\n\n    protected Throwable mainThreadException;\n\n    protected WrappedLinearLayoutManager mLayoutManager;\n\n    private TestAdapter mTestAdapter;\n\n    public VirtualLayoutManagerTest() {\n        super(\"com.tmall.wireless.tangram\", Activity.class);\n    }\n\n\n    @Override\n    protected void setUp() throws Exception {\n        super.setUp();\n    }\n\n\n    public void testGetFirstLastChildrenTest() throws Throwable {\n        getFirstLastChildrenTest(new Config().orientation(VERTICAL));\n    }\n\n\n    public void getFirstLastChildrenTest(final Config config) throws Throwable {\n        setupByConfig(config, true);\n        Runnable viewInBoundsTest = new Runnable() {\n            @Override\n            public void run() {\n                VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();\n                final String boundsLog = mLayoutManager.getBoundsLog();\n                assertEquals(config + \":\\nfirst visible child should match traversal result\\n\"\n                                + boundsLog, visibleChildren.firstVisiblePosition,\n                        mLayoutManager.findFirstVisibleItemPosition()\n                );\n                assertEquals(\n                        config + \":\\nfirst fully visible child should match traversal result\\n\"\n                                + boundsLog, visibleChildren.firstFullyVisiblePosition,\n                        mLayoutManager.findFirstCompletelyVisibleItemPosition()\n                );\n\n                assertEquals(config + \":\\nlast visible child should match traversal result\\n\"\n                                + boundsLog, visibleChildren.lastVisiblePosition,\n                        mLayoutManager.findLastVisibleItemPosition()\n                );\n                assertEquals(\n                        config + \":\\nlast fully visible child should match traversal result\\n\"\n                                + boundsLog, visibleChildren.lastFullyVisiblePosition,\n                        mLayoutManager.findLastCompletelyVisibleItemPosition()\n                );\n            }\n        };\n        runTestOnUiThread(viewInBoundsTest);\n        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching\n        // case\n        final int scrollPosition = config.mStackFromEnd ? 0 : mTestAdapter.getItemCount();\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                mRecyclerView.smoothScrollToPosition(scrollPosition);\n            }\n        });\n        while (mLayoutManager.isSmoothScrolling() ||\n                mRecyclerView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {\n            runTestOnUiThread(viewInBoundsTest);\n            Thread.sleep(400);\n        }\n        // delete all items\n        mLayoutManager.expectLayouts(2);\n        mTestAdapter.deleteAndNotify(0, mTestAdapter.getItemCount());\n        mLayoutManager.waitForLayout(2);\n        // test empty case\n        runTestOnUiThread(viewInBoundsTest);\n        // set a new adapter with huge items to test full bounds check\n        mLayoutManager.expectLayouts(1);\n        final int totalSpace = mLayoutManager.mOrientationHelper.getTotalSpace();\n        final TestAdapter newAdapter = new TestAdapter(100) {\n            @Override\n            public void onBindViewHolder(TestViewHolder holder,\n                                         int position) {\n                super.onBindViewHolder(holder, position);\n                if (config.mOrientation == HORIZONTAL) {\n                    holder.itemView.setMinimumWidth(totalSpace + 5);\n                } else {\n                    holder.itemView.setMinimumHeight(totalSpace + 5);\n                }\n            }\n        };\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                mRecyclerView.setAdapter(newAdapter);\n            }\n        });\n        mLayoutManager.waitForLayout(2);\n        runTestOnUiThread(viewInBoundsTest);\n    }\n\n\n    void setupByConfig(Config config, boolean waitForFirstLayout) throws Throwable {\n        mRecyclerView = new RecyclerView(getActivity());\n        mRecyclerView.setHasFixedSize(true);\n        mTestAdapter = config.mTestAdapter == null ? new TestAdapter(config.mItemCount)\n                : config.mTestAdapter;\n        mRecyclerView.setAdapter(mTestAdapter);\n        mLayoutManager = new WrappedLinearLayoutManager(getActivity(), config.mOrientation,\n                config.mReverseLayout);\n        mLayoutManager.setStackFromEnd(config.mStackFromEnd);\n        mLayoutManager.setRecycleChildrenOnDetach(config.mRecycleChildrenOnDetach);\n        mRecyclerView.setLayoutManager(mLayoutManager);\n        if (waitForFirstLayout) {\n            waitForFirstLayout();\n        }\n    }\n\n\n    void postExceptionToInstrumentation(Throwable t) {\n        if (DEBUG) {\n            Log.e(TAG, \"captured exception on main thread\", t);\n        }\n        if (mainThreadException != null) {\n            Log.e(TAG, \"receiving another main thread exception. dropping.\", t);\n        } else {\n            mainThreadException = t;\n        }\n\n        if (mRecyclerView != null && mRecyclerView\n                .getLayoutManager() instanceof WrappedLinearLayoutManager) {\n            WrappedLinearLayoutManager lm = (WrappedLinearLayoutManager) mRecyclerView.getLayoutManager();\n            // finish all layouts so that we get the correct exception\n            while (lm.layoutLatch.getCount() > 0) {\n                lm.layoutLatch.countDown();\n            }\n        }\n    }\n\n\n    private void waitForFirstLayout() throws Throwable {\n        mLayoutManager.expectLayouts(1);\n        setRecyclerView(mRecyclerView);\n        mLayoutManager.waitForLayout(2);\n    }\n\n\n    public void setRecyclerView(final RecyclerView recyclerView) throws Throwable {\n        setRecyclerView(recyclerView, true);\n    }\n\n    public void setRecyclerView(final RecyclerView recyclerView, boolean assignDummyPool)\n            throws Throwable {\n        mRecyclerView = recyclerView;\n        if (assignDummyPool) {\n            RecyclerView.RecycledViewPool pool = new RecyclerView.RecycledViewPool() {\n                @Override\n                public RecyclerView.ViewHolder getRecycledView(int viewType) {\n                    RecyclerView.ViewHolder viewHolder = super.getRecycledView(viewType);\n                    if (viewHolder == null) {\n                        return null;\n                    }\n\n                    ViewHolderHelper.addViewHolderFlag(viewHolder, 1); //  RecyclerView.ViewHolder.FLAG_BOUND\n                    ViewHolderHelper.setField(viewHolder, \"mPosition\", 200);\n                    ViewHolderHelper.setField(viewHolder, \"mOldPosition\", 300);\n                    ViewHolderHelper.setField(viewHolder, \"mPreLayoutPosition\", 500);\n                    return viewHolder;\n                }\n\n                @Override\n                public void putRecycledView(RecyclerView.ViewHolder scrap) {\n                    super.putRecycledView(scrap);\n                }\n            };\n            mRecyclerView.setRecycledViewPool(pool);\n        }\n\n        // mAdapterHelper = recyclerView.mAdapterHelper;\n        runTestOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                ((ViewGroup) getActivity().findViewById(android.R.id.content)).addView(recyclerView);\n            }\n        });\n    }\n\n    class TestViewHolder extends RecyclerView.ViewHolder {\n\n        Item mBindedItem;\n\n        public TestViewHolder(View itemView) {\n            super(itemView);\n            itemView.setFocusable(true);\n        }\n\n        @Override\n        public String toString() {\n            return super.toString() + \" item:\" + mBindedItem;\n        }\n    }\n\n    static class Item {\n        final static AtomicInteger idCounter = new AtomicInteger(0);\n        final public int mId = idCounter.incrementAndGet();\n\n        int mAdapterIndex;\n\n        final String mText;\n\n        Item(int adapterIndex, String text) {\n            mAdapterIndex = adapterIndex;\n            mText = text;\n        }\n\n        @Override\n        public String toString() {\n            return \"Item{\" +\n                    \"mId=\" + mId +\n                    \", originalIndex=\" + mAdapterIndex +\n                    \", text='\" + mText + '\\'' +\n                    '}';\n        }\n    }\n\n\n    class WrappedLinearLayoutManager extends VirtualLayoutManager {\n\n        CountDownLatch layoutLatch;\n\n        OnLayoutListener mOnLayoutListener;\n\n        public WrappedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {\n            super(context, orientation, reverseLayout);\n        }\n\n        public void expectLayouts(int count) {\n            layoutLatch = new CountDownLatch(count);\n        }\n\n        public void waitForLayout(long timeout) throws InterruptedException {\n            waitForLayout(timeout, TimeUnit.SECONDS);\n        }\n\n        @Override\n        public void removeAndRecycleView(View child, RecyclerView.Recycler recycler) {\n            if (DEBUG) {\n                Log.d(TAG, \"recycling view \" + mRecyclerView.getChildViewHolder(child));\n            }\n            super.removeAndRecycleView(child, recycler);\n        }\n\n        @Override\n        public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) {\n            if (DEBUG) {\n                Log.d(TAG, \"recycling view at\" + mRecyclerView.getChildViewHolder(getChildAt(index)));\n            }\n            super.removeAndRecycleViewAt(index, recycler);\n        }\n\n        private void waitForLayout(long timeout, TimeUnit timeUnit) throws InterruptedException {\n            layoutLatch.await(timeout * (DEBUG ? 100 : 1), timeUnit);\n            assertEquals(\"all expected layouts should be executed at the expected time\",\n                    0, layoutLatch.getCount());\n            getInstrumentation().waitForIdleSync();\n        }\n\n        public String getBoundsLog() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(\"view bounds:[start:\").append(mOrientationHelper.getStartAfterPadding())\n                    .append(\",\").append(\" end\").append(mOrientationHelper.getEndAfterPadding());\n            sb.append(\"\\nchildren bounds\\n\");\n            final int childCount = getChildCount();\n            for (int i = 0; i < childCount; i++) {\n                View child = getChildAt(i);\n                sb.append(\"child (ind:\").append(i).append(\", pos:\").append(getPosition(child))\n                        .append(\"[\").append(\"start:\").append(\n                        mOrientationHelper.getDecoratedStart(child)).append(\", end:\")\n                        .append(mOrientationHelper.getDecoratedEnd(child)).append(\"]\\n\");\n            }\n            return sb.toString();\n        }\n\n        public void waitForAnimationsToEnd(int timeoutInSeconds) throws InterruptedException {\n            RecyclerView.ItemAnimator itemAnimator = mRecyclerView.getItemAnimator();\n            if (itemAnimator == null) {\n                return;\n            }\n            final CountDownLatch latch = new CountDownLatch(1);\n            final boolean running = itemAnimator.isRunning(\n                    new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() {\n                        @Override\n                        public void onAnimationsFinished() {\n                            latch.countDown();\n                        }\n                    }\n            );\n            if (running) {\n                latch.await(timeoutInSeconds, TimeUnit.SECONDS);\n            }\n        }\n\n        public VisibleChildren traverseAndFindVisibleChildren() {\n            int childCount = getChildCount();\n            final VisibleChildren visibleChildren = new VisibleChildren();\n            final int start = mOrientationHelper.getStartAfterPadding();\n            final int end = mOrientationHelper.getEndAfterPadding();\n            for (int i = 0; i < childCount; i++) {\n                View child = getChildAt(i);\n                final int childStart = mOrientationHelper.getDecoratedStart(child);\n                final int childEnd = mOrientationHelper.getDecoratedEnd(child);\n                final boolean fullyVisible = childStart >= start && childEnd <= end;\n                final boolean hidden = childEnd <= start || childStart >= end;\n                if (hidden) {\n                    continue;\n                }\n                final int position = getPosition(child);\n                if (fullyVisible) {\n                    if (position < visibleChildren.firstFullyVisiblePosition ||\n                            visibleChildren.firstFullyVisiblePosition == RecyclerView.NO_POSITION) {\n                        visibleChildren.firstFullyVisiblePosition = position;\n                    }\n\n                    if (position > visibleChildren.lastFullyVisiblePosition) {\n                        visibleChildren.lastFullyVisiblePosition = position;\n                    }\n                }\n\n                if (position < visibleChildren.firstVisiblePosition ||\n                        visibleChildren.firstVisiblePosition == RecyclerView.NO_POSITION) {\n                    visibleChildren.firstVisiblePosition = position;\n                }\n\n                if (position > visibleChildren.lastVisiblePosition) {\n                    visibleChildren.lastVisiblePosition = position;\n                }\n\n            }\n            return visibleChildren;\n        }\n\n        Rect getViewBounds(View view) {\n            if (getOrientation() == HORIZONTAL) {\n                return new Rect(\n                        mOrientationHelper.getDecoratedStart(view),\n                        mSecondaryOrientationHelper.getDecoratedStart(view),\n                        mOrientationHelper.getDecoratedEnd(view),\n                        mSecondaryOrientationHelper.getDecoratedEnd(view));\n            } else {\n                return new Rect(\n                        mSecondaryOrientationHelper.getDecoratedStart(view),\n                        mOrientationHelper.getDecoratedStart(view),\n                        mSecondaryOrientationHelper.getDecoratedEnd(view),\n                        mOrientationHelper.getDecoratedEnd(view));\n            }\n\n        }\n\n        Map<Item, Rect> collectChildCoordinates() throws Throwable {\n            final Map<Item, Rect> items = new LinkedHashMap<Item, Rect>();\n            runTestOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    final int childCount = getChildCount();\n                    for (int i = 0; i < childCount; i++) {\n                        View child = getChildAt(i);\n                        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child\n                                .getLayoutParams();\n                        TestViewHolder vh = (TestViewHolder) ViewHolderHelper.getViewHolder(lp);\n                        items.put(vh.mBindedItem, getViewBounds(child));\n                    }\n                }\n            });\n            return items;\n        }\n\n        @Override\n        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {\n            try {\n                if (mOnLayoutListener != null) {\n                    mOnLayoutListener.before(recycler, state);\n                }\n                super.onLayoutChildren(recycler, state);\n                if (mOnLayoutListener != null) {\n                    mOnLayoutListener.after(recycler, state);\n                }\n            } catch (Throwable t) {\n                postExceptionToInstrumentation(t);\n            }\n            layoutLatch.countDown();\n        }\n\n\n    }\n\n\n    static class OnLayoutListener {\n        void before(RecyclerView.Recycler recycler, RecyclerView.State state) {\n        }\n\n        void after(RecyclerView.Recycler recycler, RecyclerView.State state) {\n        }\n    }\n\n\n    static class VisibleChildren {\n\n        int firstVisiblePosition = RecyclerView.NO_POSITION;\n\n        int firstFullyVisiblePosition = RecyclerView.NO_POSITION;\n\n        int lastVisiblePosition = RecyclerView.NO_POSITION;\n\n        int lastFullyVisiblePosition = RecyclerView.NO_POSITION;\n\n        @Override\n        public String toString() {\n            return \"VisibleChildren{\" +\n                    \"firstVisiblePosition=\" + firstVisiblePosition +\n                    \", firstFullyVisiblePosition=\" + firstFullyVisiblePosition +\n                    \", lastVisiblePosition=\" + lastVisiblePosition +\n                    \", lastFullyVisiblePosition=\" + lastFullyVisiblePosition +\n                    '}';\n        }\n    }\n\n\n    static class Config implements Cloneable {\n\n        private static final int DEFAULT_ITEM_COUNT = 100;\n\n        private boolean mStackFromEnd;\n\n        TestAdapter mTestAdapter = null;\n\n        int mOrientation = VERTICAL;\n\n        boolean mReverseLayout = false;\n\n        boolean mRecycleChildrenOnDetach = false;\n\n        int mItemCount = DEFAULT_ITEM_COUNT;\n\n        // TestAdapter mTestAdapter;\n\n        Config(int orientation, boolean reverseLayout, boolean stackFromEnd) {\n            mOrientation = orientation;\n            mReverseLayout = reverseLayout;\n            mStackFromEnd = stackFromEnd;\n        }\n\n        public Config() {\n\n        }\n\n\n        Config adapter(TestAdapter adapter) {\n            mTestAdapter = adapter;\n            return this;\n        }\n\n\n        Config recycleChildrenOnDetach(boolean recycleChildrenOnDetach) {\n            mRecycleChildrenOnDetach = recycleChildrenOnDetach;\n            return this;\n        }\n\n        Config orientation(int orientation) {\n            mOrientation = orientation;\n            return this;\n        }\n\n        Config stackFromBottom(boolean stackFromBottom) {\n            mStackFromEnd = stackFromBottom;\n            return this;\n        }\n\n        Config reverseLayout(boolean reverseLayout) {\n            mReverseLayout = reverseLayout;\n            return this;\n        }\n\n        public Config itemCount(int itemCount) {\n            mItemCount = itemCount;\n            return this;\n        }\n\n        // required by convention\n        @Override\n        public Object clone() throws CloneNotSupportedException {\n            return super.clone();\n        }\n\n        @Override\n        public String toString() {\n            return \"Config{\" +\n                    \"mStackFromEnd=\" + mStackFromEnd +\n                    \", mOrientation=\" + mOrientation +\n                    \", mReverseLayout=\" + mReverseLayout +\n                    \", mRecycleChildrenOnDetach=\" + mRecycleChildrenOnDetach +\n                    \", mItemCount=\" + mItemCount +\n                    '}';\n        }\n    }\n\n\n    class TestAdapter extends RecyclerView.Adapter<TestViewHolder> {\n\n        List<Item> mItems;\n\n        TestAdapter(int count) {\n            mItems = new ArrayList<Item>(count);\n            for (int i = 0; i < count; i++) {\n                mItems.add(new Item(i, \"Item \" + i));\n            }\n        }\n\n        @Override\n        public TestViewHolder onCreateViewHolder(ViewGroup parent,\n                                                 int viewType) {\n            return new TestViewHolder(new TextView(parent.getContext()));\n        }\n\n        @Override\n        public void onBindViewHolder(TestViewHolder holder, int position) {\n            final Item item = mItems.get(position);\n            ((TextView) (holder.itemView)).setText(item.mText + \"(\" + item.mAdapterIndex + \")\");\n            holder.mBindedItem = item;\n        }\n\n        public void deleteAndNotify(final int start, final int count) throws Throwable {\n            deleteAndNotify(new int[]{start, count});\n        }\n\n        /**\n         * Deletes items in the given ranges.\n         * <p>\n         * Note that each operation affects the one after so you should offset them properly.\n         * <p>\n         * For example, if adapter has 5 items (A,B,C,D,E), and then you call this method with\n         * <code>[1, 2],[2, 1]</code>, it will first delete items B,C and the new adapter will be\n         * A D E. Then it will delete 2,1 which means it will delete E.\n         */\n        public void deleteAndNotify(final int[]... startCountTuples) throws Throwable {\n            for (int[] tuple : startCountTuples) {\n                tuple[1] = -tuple[1];\n            }\n            new AddRemoveRunnable(startCountTuples).runOnMainThread();\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return hasStableIds() ? mItems.get(position).mId : super.getItemId(position);\n        }\n\n        public void offsetOriginalIndices(int start, int offset) {\n            for (int i = start; i < mItems.size(); i++) {\n                mItems.get(i).mAdapterIndex += offset;\n            }\n        }\n\n        /**\n         * @param start  inclusive\n         * @param end    exclusive\n         * @param offset\n         */\n        public void offsetOriginalIndicesBetween(int start, int end, int offset) {\n            for (int i = start; i < end && i < mItems.size(); i++) {\n                mItems.get(i).mAdapterIndex += offset;\n            }\n        }\n\n        public void addAndNotify(final int start, final int count) throws Throwable {\n            addAndNotify(new int[]{start, count});\n        }\n\n        public void addAndNotify(final int[]... startCountTuples) throws Throwable {\n            new AddRemoveRunnable(startCountTuples).runOnMainThread();\n        }\n\n        public void dispatchDataSetChanged() throws Throwable {\n            runTestOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    notifyDataSetChanged();\n                }\n            });\n        }\n\n        public void changeAndNotify(final int start, final int count) throws Throwable {\n            runTestOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    notifyItemRangeChanged(start, count);\n                }\n            });\n        }\n\n        public void changePositionsAndNotify(final int... positions) throws Throwable {\n            runTestOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    for (int i = 0; i < positions.length; i += 1) {\n                        TestAdapter.super.notifyItemRangeChanged(positions[i], 1);\n                    }\n                }\n            });\n        }\n\n        /**\n         * Similar to other methods but negative count means delete and position count means add.\n         * <p>\n         * For instance, calling this method with <code>[1,1], [2,-1]</code> it will first add an\n         * item to index 1, then remove an item from index 2 (updated index 2)\n         */\n        public void addDeleteAndNotify(final int[]... startCountTuples) throws Throwable {\n            new AddRemoveRunnable(startCountTuples).runOnMainThread();\n        }\n\n        @Override\n        public int getItemCount() {\n            return mItems.size();\n        }\n\n        public void moveItems(boolean notifyChange, int[]... fromToTuples) throws Throwable {\n            for (int i = 0; i < fromToTuples.length; i += 1) {\n                int[] tuple = fromToTuples[i];\n                moveItem(tuple[0], tuple[1], false);\n            }\n            if (notifyChange) {\n                dispatchDataSetChanged();\n            }\n        }\n\n        public void moveItem(final int from, final int to, final boolean notifyChange)\n                throws Throwable {\n            runTestOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    Item item = mItems.remove(from);\n                    mItems.add(to, item);\n                    offsetOriginalIndices(from, to - 1);\n                    item.mAdapterIndex = to;\n                    if (notifyChange) {\n                        notifyDataSetChanged();\n                    }\n                }\n            });\n        }\n\n\n        private class AddRemoveRunnable implements Runnable {\n            final int[][] mStartCountTuples;\n\n            public AddRemoveRunnable(int[][] startCountTuples) {\n                mStartCountTuples = startCountTuples;\n            }\n\n            public void runOnMainThread() throws Throwable {\n                if (Looper.myLooper() == Looper.getMainLooper()) {\n                    run();\n                } else {\n                    runTestOnUiThread(this);\n                }\n            }\n\n            @Override\n            public void run() {\n                for (int[] tuple : mStartCountTuples) {\n                    if (tuple[1] < 0) {\n                        delete(tuple);\n                    } else {\n                        add(tuple);\n                    }\n                }\n            }\n\n            private void add(int[] tuple) {\n                // offset others\n                offsetOriginalIndices(tuple[0], tuple[1]);\n                for (int i = 0; i < tuple[1]; i++) {\n                    mItems.add(tuple[0], new Item(i, \"new item \" + i));\n                }\n                notifyItemRangeInserted(tuple[0], tuple[1]);\n            }\n\n            private void delete(int[] tuple) {\n                final int count = -tuple[1];\n                offsetOriginalIndices(tuple[0] + count, tuple[1]);\n                for (int i = 0; i < count; i++) {\n                    mItems.remove(tuple[0]);\n                }\n                notifyItemRangeRemoved(tuple[0], count);\n            }\n        }\n    }\n\n}\n\n\n"
  },
  {
    "path": "vlayout/src/main/AndroidManifest.xml",
    "content": "<!--\n  ~ MIT License\n  ~\n  ~ Copyright (c) 2016 Alibaba Group\n  ~\n  ~ Permission is hereby granted, free of charge, to any person obtaining a copy\n  ~ of this software and associated documentation files (the \"Software\"), to deal\n  ~ in the Software without restriction, including without limitation the rights\n  ~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n  ~ copies of the Software, and to permit persons to whom the Software is\n  ~ furnished to do so, subject to the following conditions:\n  ~\n  ~ The above copyright notice and this permission notice shall be included in all\n  ~ copies or substantial portions of the Software.\n  ~\n  ~ THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n  ~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n  ~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n  ~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n  ~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n  ~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n  ~ SOFTWARE.\n  -->\n\n<manifest package=\"com.alibaba.android.vlayout\">\n\n</manifest>\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/Cantor.java",
    "content": "package com.alibaba.android.vlayout;\n\n/**\n * Created by longerian on 2017/12/10.\n *\n * @author longerian\n * @date 2017/12/10\n */\n\npublic class Cantor {\n\n    /**\n     * @param k1\n     * @param k2\n     * @return cantor pair for k1 and k2\n     */\n    public static long getCantor(long k1, long k2) {\n        return (k1 + k2) * (k1 + k2 + 1) / 2 + k2;\n    }\n\n    /**\n     * reverse cantor pair to origin number k1 and k2, k1 is stored in result[0], and k2 is stored in result[1]\n     * @param cantor a computed cantor number\n     * @param result the array to store output values\n     */\n    public static void reverseCantor(long cantor, long[] result) {\n        if (result == null || result.length < 2) {\n            result = new long[2];\n        }\n        // reverse Cantor Function\n        long w = (long) (Math.floor(Math.sqrt(8 * cantor + 1) - 1) / 2);\n        long t = (w * w + w) / 2;\n\n        long k2 = cantor - t;\n        long k1 = w - k2;\n        result[0] = k1;\n        result[1] = k2;\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/DelegateAdapter.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Pair;\nimport android.util.SparseArray;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.alibaba.android.vlayout.layout.SingleLayoutHelper;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static android.support.v7.widget.RecyclerView.NO_ID;\n\n/**\n * Adapter delegates its responsibility to sub adapters\n *\n * @author villadora\n * @since 1.0.0\n */\npublic class DelegateAdapter extends VirtualLayoutAdapter<RecyclerView.ViewHolder> {\n\n    @Nullable\n    private AtomicInteger mIndexGen;\n\n    private int mIndex = 0;\n\n    private final boolean mHasConsistItemType;\n\n    private SparseArray<Adapter> mItemTypeAry = new SparseArray<>();\n\n    @NonNull\n    private final List<Pair<AdapterDataObserver, Adapter>> mAdapters = new ArrayList<>();\n\n    private int mTotal = 0;\n\n    private final SparseArray<Pair<AdapterDataObserver, Adapter>> mIndexAry = new SparseArray<>();\n\n    private long[] cantorReverse = new long[2];\n\n    /**\n     * Delegate Adapter merge multi sub adapters, default is thread-unsafe\n     *\n     * @param layoutManager layoutManager\n     */\n    public DelegateAdapter(VirtualLayoutManager layoutManager) {\n        this(layoutManager, false, false);\n    }\n\n    /**\n     * @param layoutManager      layoutManager\n     * @param hasConsistItemType whether sub adapters itemTypes are consistent\n     */\n    public DelegateAdapter(VirtualLayoutManager layoutManager, boolean hasConsistItemType) {\n        this(layoutManager, hasConsistItemType, false);\n    }\n\n    /**\n     * @param layoutManager      layoutManager\n     * @param hasConsistItemType whether sub adapters itemTypes are consistent\n     * @param threadSafe         tell whether your adapter is thread-safe or not\n     */\n    DelegateAdapter(VirtualLayoutManager layoutManager, boolean hasConsistItemType, boolean threadSafe) {\n        super(layoutManager);\n        if (threadSafe) {\n            mIndexGen = new AtomicInteger(0);\n        }\n\n        mHasConsistItemType = hasConsistItemType;\n    }\n\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n\n        if (mHasConsistItemType) {\n            Adapter adapter = mItemTypeAry.get(viewType);\n            if (adapter != null) {\n                return adapter.onCreateViewHolder(parent, viewType);\n            }\n\n            return null;\n        }\n\n\n        // reverse Cantor Function\n        Cantor.reverseCantor(viewType, cantorReverse);\n\n        int index = (int)cantorReverse[1];\n        int subItemType = (int)cantorReverse[0];\n\n        Adapter adapter = findAdapterByIndex(index);\n        if (adapter == null) {\n            return null;\n        }\n\n        return adapter.onCreateViewHolder(parent, subItemType);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n        Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);\n        if (pair == null) {\n            return;\n        }\n\n        pair.second.onBindViewHolder(holder, position - pair.first.mStartPosition);\n        pair.second.onBindViewHolderWithOffset(holder, position - pair.first.mStartPosition, position);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {\n        Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);\n        if (pair == null) {\n            return;\n        }\n        pair.second.onBindViewHolder(holder, position - pair.first.mStartPosition, payloads);\n        pair.second.onBindViewHolderWithOffset(holder, position - pair.first.mStartPosition, position, payloads);\n\n    }\n\n    @Override\n    public int getItemCount() {\n        return mTotal;\n    }\n\n    /**\n     * Big integer of itemType returned by delegated adapter may lead to failed\n     *\n     * @param position item position\n     * @return integer represent item view type\n     */\n    @Override\n    public int getItemViewType(int position) {\n        Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);\n        if (p == null) {\n            return RecyclerView.INVALID_TYPE;\n        }\n\n        int subItemType = p.second.getItemViewType(position - p.first.mStartPosition);\n\n        if (subItemType < 0) {\n            // negative integer, invalid, just return\n            return subItemType;\n        }\n\n        if (mHasConsistItemType) {\n            mItemTypeAry.put(subItemType, p.second);\n            return subItemType;\n        }\n\n\n        int index = p.first.mIndex;\n\n        return (int) Cantor.getCantor(subItemType, index);\n    }\n\n\n    @Override\n    public long getItemId(int position) {\n        Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(position);\n\n        if (p == null) {\n            return NO_ID;\n        }\n\n        long itemId = p.second.getItemId(position - p.first.mStartPosition);\n\n        if (itemId < 0) {\n            return NO_ID;\n        }\n\n        int index = p.first.mIndex;\n        /*\n         * Now we have a pairing function problem, we use cantor pairing function for itemId.\n         */\n        return Cantor.getCantor(index, itemId);\n    }\n\n    @Override\n    public void setHasStableIds(boolean hasStableIds) {\n        // do nothing\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void onViewRecycled(RecyclerView.ViewHolder holder) {\n        super.onViewRecycled(holder);\n\n        int position = holder.getPosition();\n        if (position >= 0) {\n            Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);\n            if (pair != null) {\n                pair.second.onViewRecycled(holder);\n            }\n        }\n    }\n\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {\n        super.onViewAttachedToWindow(holder);\n        int position = holder.getPosition();\n        if (position >= 0) {\n            Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);\n            if (pair != null) {\n                pair.second.onViewAttachedToWindow(holder);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public void onViewDetachedFromWindow(RecyclerView.ViewHolder holder) {\n        super.onViewDetachedFromWindow(holder);\n        int position = holder.getPosition();\n        if (position >= 0) {\n            Pair<AdapterDataObserver, Adapter> pair = findAdapterByPosition(position);\n            if (pair != null) {\n                pair.second.onViewDetachedFromWindow(holder);\n            }\n        }\n    }\n\n\n    /**\n     * You can not set layoutHelpers to delegate adapter\n     */\n    @Deprecated\n    @Override\n    public void setLayoutHelpers(List<LayoutHelper> helpers) {\n        throw new UnsupportedOperationException(\"DelegateAdapter doesn't support setLayoutHelpers directly\");\n    }\n\n\n    public void setAdapters(@Nullable List<Adapter> adapters) {\n        clear();\n\n        if (adapters == null) {\n            adapters = Collections.emptyList();\n        }\n\n        List<LayoutHelper> helpers = new LinkedList<>();\n\n        boolean hasStableIds = true;\n        mTotal = 0;\n\n        Pair<AdapterDataObserver, Adapter> pair;\n        for (Adapter adapter : adapters) {\n            // every adapter has an unique index id\n            AdapterDataObserver observer = new AdapterDataObserver(mTotal, mIndexGen == null ? mIndex++ : mIndexGen.incrementAndGet());\n            adapter.registerAdapterDataObserver(observer);\n            hasStableIds = hasStableIds && adapter.hasStableIds();\n            LayoutHelper helper = adapter.onCreateLayoutHelper();\n\n            helper.setItemCount(adapter.getItemCount());\n            mTotal += helper.getItemCount();\n            helpers.add(helper);\n            pair = Pair.create(observer, adapter);\n            mIndexAry.put(observer.mIndex, pair);\n            mAdapters.add(pair);\n        }\n\n        if (!hasObservers()) {\n            super.setHasStableIds(hasStableIds);\n        }\n        super.setLayoutHelpers(helpers);\n    }\n\n    /**\n     * Add adapters in <code>position</code>\n     *\n     * @param position the index where adapters added\n     * @param adapters adapters\n     */\n    public void addAdapters(int position, @Nullable List<Adapter> adapters) {\n        if (adapters == null || adapters.size() == 0) {\n            return;\n        }\n        if (position < 0) {\n            position = 0;\n        }\n\n        if (position > mAdapters.size()) {\n            position = mAdapters.size();\n        }\n\n        List<Adapter> newAdapter = new ArrayList<>();\n        Iterator<Pair<AdapterDataObserver, Adapter>> itr = mAdapters.iterator();\n        while (itr.hasNext()) {\n            Pair<AdapterDataObserver, Adapter> pair = itr.next();\n            Adapter theOrigin = pair.second;\n            newAdapter.add(theOrigin);\n        }\n        for (Adapter adapter : adapters) {\n            newAdapter.add(position, adapter);\n            position++;\n        }\n        setAdapters(newAdapter);\n    }\n\n    /**\n     * Append adapters to the end\n     *\n     * @param adapters adapters will be appended\n     */\n    public void addAdapters(@Nullable List<Adapter> adapters) {\n        addAdapters(mAdapters.size(), adapters);\n    }\n\n    public void addAdapter(int position, @Nullable Adapter adapter) {\n        addAdapters(position, Collections.singletonList(adapter));\n    }\n\n    public void addAdapter(@Nullable Adapter adapter) {\n        addAdapters(Collections.singletonList(adapter));\n    }\n\n    public void removeFirstAdapter() {\n        if (mAdapters != null && !mAdapters.isEmpty()) {\n            Adapter targetAdatper = mAdapters.get(0).second;\n            removeAdapter(targetAdatper);\n        }\n    }\n\n    public void removeLastAdapter() {\n        if (mAdapters != null && !mAdapters.isEmpty()) {\n            Adapter targetAdatper = mAdapters.get(mAdapters.size() - 1).second;\n            removeAdapter(targetAdatper);\n        }\n    }\n\n    public void removeAdapter(int adapterIndex) {\n        if (adapterIndex >= 0 && adapterIndex < mAdapters.size()) {\n            Adapter targetAdatper = mAdapters.get(adapterIndex).second;\n            removeAdapter(targetAdatper);\n        }\n    }\n\n    public void removeAdapter(@Nullable Adapter targetAdapter) {\n        if (targetAdapter == null) {\n            return;\n        }\n        removeAdapters(Collections.singletonList(targetAdapter));\n    }\n\n    public void removeAdapters(@Nullable List<Adapter> targetAdapters) {\n        if (targetAdapters == null || targetAdapters.isEmpty()) {\n            return;\n        }\n        List<LayoutHelper> helpers = new LinkedList<>(super.getLayoutHelpers());\n        for (int i = 0, size = targetAdapters.size(); i < size; i++) {\n            Adapter one = targetAdapters.get(i);\n            Iterator<Pair<AdapterDataObserver, Adapter>> itr = mAdapters.iterator();\n            while (itr.hasNext()) {\n                Pair<AdapterDataObserver, Adapter> pair = itr.next();\n                Adapter theOther = pair.second;\n                if (theOther.equals(one)) {\n                    theOther.unregisterAdapterDataObserver(pair.first);\n                    final int position = findAdapterPositionByIndex(pair.first.mIndex);\n                    if (position >= 0 && position < helpers.size()) {\n                        helpers.remove(position);\n                    }\n                    itr.remove();\n                    break;\n                }\n            }\n        }\n\n        List<Adapter> newAdapter = new ArrayList<>();\n        Iterator<Pair<AdapterDataObserver, Adapter>> itr = mAdapters.iterator();\n        while (itr.hasNext()) {\n            newAdapter.add(itr.next().second);\n        }\n        setAdapters(newAdapter);\n    }\n\n    public void clear() {\n        mTotal = 0;\n        mIndex = 0;\n        if (mIndexGen != null) {\n            mIndexGen.set(0);\n        }\n        mLayoutManager.setLayoutHelpers(null);\n\n        for (Pair<AdapterDataObserver, Adapter> p : mAdapters) {\n            p.second.unregisterAdapterDataObserver(p.first);\n        }\n\n\n        mItemTypeAry.clear();\n        mAdapters.clear();\n        mIndexAry.clear();\n    }\n\n    public int getAdaptersCount() {\n        return mAdapters == null ? 0 : mAdapters.size();\n    }\n\n    /**\n     * @param absoultePosition\n     * @return the relative position in sub adapter by the absoulte position in DelegaterAdapter. Return -1 if no sub adapter founded.\n     */\n    public int findOffsetPosition(int absoultePosition) {\n        Pair<AdapterDataObserver, Adapter> p = findAdapterByPosition(absoultePosition);\n        if (p == null) {\n            return -1;\n        }\n        int subAdapterPosition = absoultePosition - p.first.mStartPosition;\n        return subAdapterPosition;\n    }\n\n    @Nullable\n    public Pair<AdapterDataObserver, Adapter> findAdapterByPosition(int position) {\n        final int count = mAdapters.size();\n        if (count == 0) {\n            return null;\n        }\n\n        int s = 0, e = count - 1, m;\n        Pair<AdapterDataObserver, Adapter> rs = null;\n\n        // binary search range\n        while (s <= e) {\n            m = (s + e) / 2;\n            rs = mAdapters.get(m);\n            int endPosition = rs.first.mStartPosition + rs.second.getItemCount() - 1;\n\n            if (rs.first.mStartPosition > position) {\n                e = m - 1;\n            } else if (endPosition < position) {\n                s = m + 1;\n            } else if (rs.first.mStartPosition <= position && endPosition >= position) {\n                break;\n            }\n\n            rs = null;\n        }\n\n        return rs;\n    }\n\n\n    public int findAdapterPositionByIndex(int index) {\n        Pair<AdapterDataObserver, Adapter> rs = mIndexAry.get(index);\n        return rs == null ? -1 : mAdapters.indexOf(rs);\n    }\n\n    public Adapter findAdapterByIndex(int index) {\n        Pair<AdapterDataObserver, Adapter> rs = mIndexAry.get(index);\n        return rs.second;\n    }\n\n    protected class AdapterDataObserver extends RecyclerView.AdapterDataObserver {\n        int mStartPosition;\n\n        int mIndex = -1;\n\n        public AdapterDataObserver(int startPosition, int index) {\n            this.mStartPosition = startPosition;\n            this.mIndex = index;\n        }\n\n        public void updateStartPositionAndIndex(int startPosition, int index) {\n            this.mStartPosition = startPosition;\n            this.mIndex = index;\n        }\n\n        public int getStartPosition() {\n            return mStartPosition;\n        }\n\n        public int getIndex() {\n            return mIndex;\n        }\n\n        private boolean updateLayoutHelper() {\n            if (mIndex < 0) {\n                return false;\n            }\n\n            final int idx = findAdapterPositionByIndex(mIndex);\n            if (idx < 0) {\n                return false;\n            }\n\n            Pair<AdapterDataObserver, Adapter> p = mAdapters.get(idx);\n            List<LayoutHelper> helpers = new LinkedList<>(getLayoutHelpers());\n            LayoutHelper helper = helpers.get(idx);\n\n            if (helper.getItemCount() != p.second.getItemCount()) {\n                // if itemCount changed;\n                helper.setItemCount(p.second.getItemCount());\n\n                mTotal = mStartPosition + p.second.getItemCount();\n\n                for (int i = idx + 1; i < mAdapters.size(); i++) {\n                    Pair<AdapterDataObserver, Adapter> pair = mAdapters.get(i);\n                    // update startPosition for adapters in following\n                    pair.first.mStartPosition = mTotal;\n                    mTotal += pair.second.getItemCount();\n                }\n\n                // set helpers to refresh range\n                DelegateAdapter.super.setLayoutHelpers(helpers);\n            }\n            return true;\n        }\n\n        @Override\n        public void onChanged() {\n            if (!updateLayoutHelper()) {\n                return;\n            }\n            notifyDataSetChanged();\n        }\n\n        @Override\n        public void onItemRangeRemoved(int positionStart, int itemCount) {\n            if (!updateLayoutHelper()) {\n                return;\n            }\n            notifyItemRangeRemoved(mStartPosition + positionStart, itemCount);\n        }\n\n        @Override\n        public void onItemRangeInserted(int positionStart, int itemCount) {\n            if (!updateLayoutHelper()) {\n                return;\n            }\n            notifyItemRangeInserted(mStartPosition + positionStart, itemCount);\n        }\n\n        @Override\n        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {\n            if (!updateLayoutHelper()) {\n                return;\n            }\n            notifyItemMoved(mStartPosition + fromPosition, mStartPosition + toPosition);\n        }\n\n        @Override\n        public void onItemRangeChanged(int positionStart, int itemCount) {\n            if (!updateLayoutHelper()) {\n                return;\n            }\n            notifyItemRangeChanged(mStartPosition + positionStart, itemCount);\n        }\n\n        @Override\n        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {\n            if (!updateLayoutHelper()) {\n                return;\n            }\n            notifyItemRangeChanged(mStartPosition + positionStart, itemCount, payload);\n        }\n    }\n\n    /**\n     * return an adapter that only contains one item, and using SimpleLayoutHelper\n     *\n     * @param view the only view, no binding is required\n     * @return adapter\n     */\n    public static Adapter<? extends RecyclerView.ViewHolder> simpleAdapter(@NonNull View view) {\n        return new SimpleViewAdapter(view);\n    }\n\n    /**\n     * Return an adapter that only contains on item and using given layoutHelper\n     *\n     * @param view         the only view, no binding is required\n     * @param layoutHelper layoutHelper that adapter used\n     * @return adapter\n     */\n    public static Adapter<? extends RecyclerView.ViewHolder> simpleAdapter(@NonNull View view, @NonNull LayoutHelper layoutHelper) {\n        return new SimpleViewAdapter(view, layoutHelper);\n    }\n\n\n    static class SimpleViewHolder extends RecyclerView.ViewHolder {\n\n        public SimpleViewHolder(View view) {\n            super(view);\n        }\n    }\n\n    static class SimpleViewAdapter extends Adapter<RecyclerView.ViewHolder> {\n\n        private View mView;\n\n        private LayoutHelper mLayoutHelper;\n\n        public SimpleViewAdapter(@NonNull View view, @NonNull LayoutHelper layoutHelper) {\n            this.mView = view;\n            this.mLayoutHelper = layoutHelper;\n        }\n\n        public SimpleViewAdapter(@NonNull View view) {\n            this(view, new SingleLayoutHelper());\n        }\n\n        @Override\n        public LayoutHelper onCreateLayoutHelper() {\n            return mLayoutHelper;\n        }\n\n        @Override\n        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return new SimpleViewHolder(mView);\n        }\n\n        @Override\n        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n\n        }\n\n        @Override\n        public int getItemCount() {\n            return 1;\n        }\n    }\n\n\n    public static abstract class Adapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {\n        public abstract LayoutHelper onCreateLayoutHelper();\n\n        protected void onBindViewHolderWithOffset(VH holder, int position, int offsetTotal) {\n\n        }\n\n        protected void onBindViewHolderWithOffset(VH holder, int position, int offsetTotal, List<Object> payloads) {\n            onBindViewHolderWithOffset(holder, position, offsetTotal);\n        }\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/ExposeLinearLayoutManagerEx.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.PointF;\nimport android.os.Bundle;\nimport android.os.Parcelable;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\n\n/**\n * This class is used to expose layoutChunk method, should not be used in anywhere else\n * It's only a valid class technically and with no features/functions in it\n *\n * @author villadora\n * @since 1.0.0\n */\n\n\n/**\n * A {@link RecyclerView.LayoutManager} implementation which provides\n * similar functionality to {@link android.widget.ListView}.\n */\nclass ExposeLinearLayoutManagerEx extends LinearLayoutManager {\n\n    private static final String TAG = \"ExposeLLManagerEx\";\n\n    private static final boolean DEBUG = false;\n\n    public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;\n\n    public static final int VERTICAL = OrientationHelper.VERTICAL;\n\n    public static final int INVALID_OFFSET = Integer.MIN_VALUE;\n\n\n    /**\n     * While trying to find next view to focus, LayoutManager will not try to scroll more\n     * than this factor times the total space of the list. If layout is vertical, total space is the\n     * height minus padding, if layout is horizontal, total space is the width minus padding.\n     */\n    private static final float MAX_SCROLL_FACTOR = 0.33f;\n\n    /**\n     * Helper class that keeps temporary layout state.\n     * It does not keep state after layout is complete but we still keep a reference to re-use\n     * the same object.\n     */\n    protected LayoutState mLayoutState;\n\n    /**\n     * Many calculations are made depending on orientation. To keep it clean, this interface\n     * helps {@link LinearLayoutManager} make those decisions.\n     * Based on {@link #mOrientation}, an implementation is lazily created in\n     * {@link #ensureLayoutStateExpose} method.\n     */\n    private OrientationHelperEx mOrientationHelper;\n\n    /**\n     * We need to track this so that we can ignore current position when it changes.\n     */\n    private boolean mLastStackFromEnd;\n\n\n    /**\n     * This keeps the final value for how LayoutManager should start laying out views.\n     * It is calculated by checking {@link #getReverseLayout()} and View's layout direction.\n     * {@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} is run.\n     */\n    private boolean mShouldReverseLayoutExpose = false;\n\n    /**\n     * When LayoutManager needs to scroll to a position, it sets this variable and requests a\n     * layout which will check this variable and re-layout accordingly.\n     */\n    private int mCurrentPendingScrollPosition = RecyclerView.NO_POSITION;\n\n    /**\n     * Used to keep the offset value when {@link #scrollToPositionWithOffset(int, int)} is\n     * called.\n     */\n    private int mPendingScrollPositionOffset = INVALID_OFFSET;\n\n\n    protected Bundle mCurrentPendingSavedState = null;\n\n    /**\n     * Re-used variable to keep anchor information on re-layout.\n     * Anchor position and coordinate defines the reference point for LLM while doing a layout.\n     */\n    private final AnchorInfo mAnchorInfo;\n\n    private final ChildHelperWrapper mChildHelperWrapper;\n\n    private final Method mEnsureLayoutStateMethod;\n\n    protected int recycleOffset;\n\n    /**\n     * Creates a vertical LinearLayoutManager\n     *\n     * @param context Current context, will be used to access resources.\n     */\n    public ExposeLinearLayoutManagerEx(Context context) {\n        this(context, VERTICAL, false);\n    }\n\n\n    /**\n     * @param context       Current context, will be used to access resources.\n     * @param orientation   Layout orientation. Should be {@link #HORIZONTAL} or {@link\n     *                      #VERTICAL}.\n     * @param reverseLayout When set to true, layouts from end to start.\n     */\n    public ExposeLinearLayoutManagerEx(Context context, int orientation, boolean reverseLayout) {\n        super(context, orientation, reverseLayout);\n        mAnchorInfo = new AnchorInfo();\n        setOrientation(orientation);\n        setReverseLayout(reverseLayout);\n        mChildHelperWrapper = new ChildHelperWrapper(this);\n\n\n        try {\n            mEnsureLayoutStateMethod = LinearLayoutManager.class.getDeclaredMethod(\"ensureLayoutState\");\n            mEnsureLayoutStateMethod.setAccessible(true);\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n            throw new RuntimeException(e);\n        }\n        try {\n            // FIXME in the future\n            Method setItemPrefetchEnabledMethod = RecyclerView.LayoutManager.class\n                    .getDeclaredMethod(\"setItemPrefetchEnabled\", boolean.class);\n            if (setItemPrefetchEnabledMethod != null) {\n                setItemPrefetchEnabledMethod.invoke(this, false);\n            }\n        } catch (NoSuchMethodException e) {\n            /* this method is added in 25.1.0, official release still has bug, see\n             * https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened&groupby=&sort=&id=230295\n             **/\n        } catch (InvocationTargetException e) {\n        } catch (IllegalAccessException e) {\n        }\n//        setItemPrefetchEnabled(false);\n    }\n\n\n    @Override\n    public Parcelable onSaveInstanceState() {\n        if (mCurrentPendingSavedState != null) {\n            return new Bundle(mCurrentPendingSavedState);\n        }\n        Bundle state = new Bundle();\n        if (getChildCount() > 0) {\n            boolean didLayoutFromEnd = mLastStackFromEnd ^ mShouldReverseLayoutExpose;\n            state.putBoolean(\"AnchorLayoutFromEnd\", didLayoutFromEnd);\n            if (didLayoutFromEnd) {\n                final View refChild = getChildClosestToEndExpose();\n                state.putInt(\"AnchorOffset\", mOrientationHelper.getEndAfterPadding() -\n                        mOrientationHelper.getDecoratedEnd(refChild));\n                state.putInt(\"AnchorPosition\", getPosition(refChild));\n            } else {\n                final View refChild = getChildClosestToStartExpose();\n                state.putInt(\"AnchorPosition\", getPosition(refChild));\n                state.putInt(\"AnchorOffset\", mOrientationHelper.getDecoratedStart(refChild) -\n                        mOrientationHelper.getStartAfterPadding());\n            }\n        } else {\n            state.putInt(\"AnchorPosition\", RecyclerView.NO_POSITION);\n        }\n        return state;\n    }\n\n    @Override\n    public void onRestoreInstanceState(Parcelable state) {\n        if (state instanceof Bundle) {\n            mCurrentPendingSavedState = (Bundle) state;\n            requestLayout();\n            if (DEBUG) {\n                Log.d(TAG, \"loaded saved state\");\n            }\n        } else if (DEBUG) {\n            Log.d(TAG, \"invalid saved state class\");\n        }\n    }\n\n    /**\n     * Sets the orientation of the layout. {@link LinearLayoutManager}\n     * will do its best to keep scroll position.\n     *\n     * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}\n     */\n    public void setOrientation(int orientation) {\n        super.setOrientation(orientation);\n        mOrientationHelper = null;\n    }\n\n    public void setRecycleOffset(int recycleOffset) {\n        this.recycleOffset = recycleOffset;\n    }\n\n    /**\n     * Calculates the view layout order. (e.g. from end to start or start to end)\n     * RTL layout support is applied automatically. So if layout is RTL and\n     * {@link #getReverseLayout()} is {@code true}, elements will be laid out starting from left.\n     */\n    private void myResolveShouldLayoutReverse() {\n        // A == B is the same result, but we rather keep it readable\n        if (getOrientation() == VERTICAL || !isLayoutRTL()) {\n            mShouldReverseLayoutExpose = getReverseLayout();\n        } else {\n            mShouldReverseLayoutExpose = !getReverseLayout();\n        }\n    }\n\n\n    public PointF computeScrollVectorForPosition(int targetPosition) {\n        if (getChildCount() == 0) {\n            return null;\n        }\n        final int firstChildPos = getPosition(getChildAt(0));\n        final int direction = targetPosition < firstChildPos != mShouldReverseLayoutExpose ? -1 : 1;\n        if (getOrientation() == HORIZONTAL) {\n            return new PointF(direction, 0);\n        } else {\n            return new PointF(0, direction);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {\n\n        // layout algorithm:\n        // 1) by checking children and other variables, find an anchor coordinate and an anchor\n        //  item position.\n        // 2) fill towards start, stacking from bottom\n        // 3) fill towards end, stacking from top\n        // 4) scroll to fulfill requirements like stack from bottom.\n        // create layout state\n        if (DEBUG) {\n            Log.d(TAG, \"is pre layout:\" + state.isPreLayout());\n        }\n        if (mCurrentPendingSavedState != null && mCurrentPendingSavedState.getInt(\"AnchorPosition\") >= 0) {\n            mCurrentPendingScrollPosition = mCurrentPendingSavedState.getInt(\"AnchorPosition\");\n        }\n\n        ensureLayoutStateExpose();\n        mLayoutState.mRecycle = false;\n        // resolve layout direction\n        myResolveShouldLayoutReverse();\n\n        mAnchorInfo.reset();\n        mAnchorInfo.mLayoutFromEnd = mShouldReverseLayoutExpose ^ getStackFromEnd();\n        // calculate anchor position and coordinate\n        updateAnchorInfoForLayoutExpose(state, mAnchorInfo);\n\n\n        if (DEBUG) {\n            Log.d(TAG, \"Anchor info:\" + mAnchorInfo);\n        }\n\n        // LLM may decide to layout items for \"extra\" pixels to account for scrolling target,\n        // caching or predictive animations.\n        int extraForStart;\n        int extraForEnd;\n        final int extra = getExtraLayoutSpace(state);\n        boolean before = state.getTargetScrollPosition() < mAnchorInfo.mPosition;\n        if (before == mShouldReverseLayoutExpose) {\n            extraForEnd = extra;\n            extraForStart = 0;\n        } else {\n            extraForStart = extra;\n            extraForEnd = 0;\n        }\n        extraForStart += mOrientationHelper.getStartAfterPadding();\n        extraForEnd += mOrientationHelper.getEndPadding();\n        if (state.isPreLayout() && mCurrentPendingScrollPosition != RecyclerView.NO_POSITION &&\n                mPendingScrollPositionOffset != INVALID_OFFSET) {\n            // if the child is visible and we are going to move it around, we should layout\n            // extra items in the opposite direction to make sure new items animate nicely\n            // instead of just fading in\n            final View existing = findViewByPosition(mCurrentPendingScrollPosition);\n            if (existing != null) {\n                final int current;\n                final int upcomingOffset;\n                if (mShouldReverseLayoutExpose) {\n                    current = mOrientationHelper.getEndAfterPadding() -\n                            mOrientationHelper.getDecoratedEnd(existing);\n                    upcomingOffset = current - mPendingScrollPositionOffset;\n                } else {\n                    current = mOrientationHelper.getDecoratedStart(existing)\n                            - mOrientationHelper.getStartAfterPadding();\n                    upcomingOffset = mPendingScrollPositionOffset - current;\n                }\n                if (upcomingOffset > 0) {\n                    extraForStart += upcomingOffset;\n                } else {\n                    extraForEnd -= upcomingOffset;\n                }\n            }\n        }\n        int startOffset;\n        int endOffset;\n        onAnchorReady(state, mAnchorInfo);\n        detachAndScrapAttachedViews(recycler);\n        mLayoutState.mIsPreLayout = state.isPreLayout();\n        mLayoutState.mOnRefresLayout = true;\n        if (mAnchorInfo.mLayoutFromEnd) {\n            // fill towards start\n            updateLayoutStateToFillStartExpose(mAnchorInfo);\n            mLayoutState.mExtra = extraForStart;\n            fill(recycler, mLayoutState, state, false);\n            startOffset = mLayoutState.mOffset;\n            if (mLayoutState.mAvailable > 0) {\n                extraForEnd += mLayoutState.mAvailable;\n            }\n            // fill towards end\n            updateLayoutStateToFillEndExpose(mAnchorInfo);\n            mLayoutState.mExtra = extraForEnd;\n            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;\n            fill(recycler, mLayoutState, state, false);\n            endOffset = mLayoutState.mOffset;\n        } else {\n            // fill towards end\n            updateLayoutStateToFillEndExpose(mAnchorInfo);\n            mLayoutState.mExtra = extraForEnd;\n            fill(recycler, mLayoutState, state, false);\n            endOffset = mLayoutState.mOffset;\n            if (mLayoutState.mAvailable > 0) {\n                extraForStart += mLayoutState.mAvailable;\n            }\n            // fill towards start\n            updateLayoutStateToFillStartExpose(mAnchorInfo);\n            mLayoutState.mExtra = extraForStart;\n            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;\n            fill(recycler, mLayoutState, state, false);\n            startOffset = mLayoutState.mOffset;\n        }\n\n        // changes may cause gaps on the UI, try to fix them.\n        // TODO we can probably avoid this if neither stackFromEnd/reverseLayout/RTL values have\n        // changed\n        if (getChildCount() > 0) {\n            // because layout from end may be changed by scroll to position\n            // we re-calculate it.\n            // find which side we should check for gaps.\n            if (mShouldReverseLayoutExpose ^ getStackFromEnd()) {\n                int fixOffset = fixLayoutEndGapExpose(endOffset, recycler, state, true);\n                startOffset += fixOffset;\n                endOffset += fixOffset;\n                fixOffset = fixLayoutStartGapExpose(startOffset, recycler, state, false);\n                startOffset += fixOffset;\n                endOffset += fixOffset;\n            } else {\n                int fixOffset = fixLayoutStartGapExpose(startOffset, recycler, state, true);\n                startOffset += fixOffset;\n                endOffset += fixOffset;\n                fixOffset = fixLayoutEndGapExpose(endOffset, recycler, state, false);\n                startOffset += fixOffset;\n                endOffset += fixOffset;\n            }\n        }\n        layoutForPredictiveAnimationsExpose(recycler, state, startOffset, endOffset);\n        if (!state.isPreLayout()) {\n            mCurrentPendingScrollPosition = RecyclerView.NO_POSITION;\n            mPendingScrollPositionOffset = INVALID_OFFSET;\n            mOrientationHelper.onLayoutComplete();\n        }\n        mLastStackFromEnd = getStackFromEnd();\n        mCurrentPendingSavedState = null; // we don't need this anymore\n        if (DEBUG) {\n            validateChildOrderExpose();\n        }\n    }\n\n    /**\n     * Method called when Anchor position is decided. Extending class can setup accordingly or\n     * even update anchor info if necessary.\n     *\n     * @param state\n     * @param anchorInfo Simple data structure to keep anchor point information for the next layout\n     */\n    public void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) {\n    }\n\n\n    private RecyclerView mRecyclerView;\n\n    @Override\n    public void onAttachedToWindow(RecyclerView view) {\n        super.onAttachedToWindow(view);\n        mRecyclerView = view;\n    }\n\n    @Override\n    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {\n        super.onDetachedFromWindow(view, recycler);\n        mRecyclerView = null;\n    }\n\n    @Override\n    public int findFirstVisibleItemPosition() {\n        ensureLayoutStateExpose();\n        return super.findFirstVisibleItemPosition();\n    }\n\n\n    @Override\n    public int findLastVisibleItemPosition() {\n        ensureLayoutStateExpose();\n        try {\n            return super.findLastVisibleItemPosition();\n        } catch (Exception e) {\n            Log.d(\"LastItem\", \"itemCount: \" + getItemCount());\n            Log.d(\"LastItem\", \"childCount: \" + getChildCount());\n            Log.d(\"LastItem\", \"child: \" + getChildAt(getChildCount() - 1));\n            Log.d(\"LastItem\", \"RV childCount: \" + mRecyclerView.getChildCount());\n            Log.d(\"LastItem\", \"RV child: \" + mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1));\n            throw e;\n        }\n    }\n\n    private View myFindReferenceChildClosestToEnd(RecyclerView.State state) {\n        return this.mShouldReverseLayoutExpose ? this.myFindFirstReferenceChild(state.getItemCount()) : this.myFindLastReferenceChild(state.getItemCount());\n    }\n\n    private View myFindReferenceChildClosestToStart(RecyclerView.State state) {\n        return this.mShouldReverseLayoutExpose ? this.myFindLastReferenceChild(state.getItemCount()) : this.myFindFirstReferenceChild(state.getItemCount());\n    }\n\n\n    private View myFindFirstReferenceChild(int itemCount) {\n        return this.findReferenceChildInternal(0, this.getChildCount(), itemCount);\n    }\n\n    private View myFindLastReferenceChild(int itemCount) {\n        return this.findReferenceChildInternal(this.getChildCount() - 1, -1, itemCount);\n    }\n\n\n    private View findReferenceChildInternal(int start, int end, int itemCount) {\n        this.ensureLayoutStateExpose();\n        View invalidMatch = null;\n        View outOfBoundsMatch = null;\n        int boundsStart = this.mOrientationHelper.getStartAfterPadding();\n        int boundsEnd = this.mOrientationHelper.getEndAfterPadding();\n        int diff = end > start ? 1 : -1;\n\n        for (int i = start; i != end; i += diff) {\n            View view = this.getChildAt(i);\n            int position = this.getPosition(view);\n            if (position >= 0 && position < itemCount) {\n                if (((RecyclerView.LayoutParams) view.getLayoutParams()).isItemRemoved()) {\n                    if (invalidMatch == null) {\n                        invalidMatch = view;\n                    }\n                } else {\n                    if (this.mOrientationHelper.getDecoratedStart(view) < boundsEnd && this.mOrientationHelper.getDecoratedEnd(view) >= boundsStart) {\n                        return view;\n                    }\n\n                    if (outOfBoundsMatch == null) {\n                        outOfBoundsMatch = view;\n                    }\n                }\n            }\n        }\n\n        return outOfBoundsMatch != null ? outOfBoundsMatch : invalidMatch;\n    }\n\n    /**\n     * If necessary, layouts new items for predictive animations\n     */\n    private void layoutForPredictiveAnimationsExpose(RecyclerView.Recycler recycler,\n                                                     RecyclerView.State state, int startOffset, int endOffset) {\n        // If there are scrap children that we did not layout, we need to find where they did go\n        // and layout them accordingly so that animations can work as expected.\n        // This case may happen if new views are added or an existing view expands and pushes\n        // another view out of bounds.\n        if (!state.willRunPredictiveAnimations() || getChildCount() == 0 || state.isPreLayout()\n                || !supportsPredictiveItemAnimations()) {\n            return;\n        }\n\n        // to make the logic simpler, we calculate the size of children and call fill.\n        int scrapExtraStart = 0, scrapExtraEnd = 0;\n        final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();\n        final int scrapSize = scrapList.size();\n        final int firstChildPos = getPosition(getChildAt(0));\n        for (int i = 0; i < scrapSize; i++) {\n            RecyclerView.ViewHolder scrap = scrapList.get(i);\n            final int position = scrap.getPosition();\n            final int direction = position < firstChildPos != mShouldReverseLayoutExpose\n                    ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END;\n            if (direction == LayoutState.LAYOUT_START) {\n                scrapExtraStart += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);\n            } else {\n                scrapExtraEnd += mOrientationHelper.getDecoratedMeasurement(scrap.itemView);\n            }\n        }\n\n        if (DEBUG) {\n            Log.d(TAG, \"for unused scrap, decided to add \" + scrapExtraStart\n                    + \" towards start and \" + scrapExtraEnd + \" towards end\");\n        }\n        mLayoutState.mScrapList = scrapList;\n        if (scrapExtraStart > 0) {\n            View anchor = getChildClosestToStartExpose();\n            updateLayoutStateToFillStartExpose(getPosition(anchor), startOffset);\n            mLayoutState.mExtra = scrapExtraStart;\n            mLayoutState.mAvailable = 0;\n            mLayoutState.mCurrentPosition += mShouldReverseLayoutExpose ? 1 : -1;\n            mLayoutState.mOnRefresLayout = true;\n            fill(recycler, mLayoutState, state, false);\n        }\n\n        if (scrapExtraEnd > 0) {\n            View anchor = getChildClosestToEndExpose();\n            updateLayoutStateToFillEndExpose(getPosition(anchor), endOffset);\n            mLayoutState.mExtra = scrapExtraEnd;\n            mLayoutState.mAvailable = 0;\n            mLayoutState.mCurrentPosition += mShouldReverseLayoutExpose ? -1 : 1;\n            mLayoutState.mOnRefresLayout = true;\n            fill(recycler, mLayoutState, state, false);\n        }\n        mLayoutState.mScrapList = null;\n    }\n\n    private void updateAnchorInfoForLayoutExpose(RecyclerView.State state, AnchorInfo anchorInfo) {\n        if (updateAnchorFromPendingDataExpose(state, anchorInfo)) {\n            if (DEBUG) {\n                Log.d(TAG, \"updated anchor info from pending information\");\n            }\n            return;\n        }\n\n        if (updateAnchorFromChildrenExpose(state, anchorInfo)) {\n            if (DEBUG) {\n                Log.d(TAG, \"updated anchor info from existing children\");\n            }\n            return;\n        }\n        if (DEBUG) {\n            Log.d(TAG, \"deciding anchor info for fresh state\");\n        }\n        anchorInfo.assignCoordinateFromPadding();\n        anchorInfo.mPosition = getStackFromEnd() ? state.getItemCount() - 1 : 0;\n    }\n\n    /**\n     * Finds an anchor child from existing Views. Most of the time, this is the view closest to\n     * start or end that has a valid position (e.g. not removed).\n     * <p/>\n     * If a child has focus, it is given priority.\n     */\n    private boolean updateAnchorFromChildrenExpose(RecyclerView.State state, AnchorInfo anchorInfo) {\n        if (getChildCount() == 0) {\n            return false;\n        }\n        View focused = getFocusedChild();\n        if (focused != null && anchorInfo.assignFromViewIfValid(focused, state)) {\n            if (DEBUG) {\n                Log.d(TAG, \"decided anchor child from focused view\");\n            }\n            return true;\n        }\n\n        if (mLastStackFromEnd != getStackFromEnd()) {\n            return false;\n        }\n\n\n        final View referenceChild = anchorInfo.mLayoutFromEnd ?\n                myFindReferenceChildClosestToEnd(state)\n                : myFindReferenceChildClosestToStart(state);\n\n\n        if (referenceChild != null) {\n            anchorInfo.assignFromView(referenceChild);\n            // If all visible views are removed in 1 pass, reference child might be out of bounds.\n            // If that is the case, offset it back to 0 so that we use these pre-layout children.\n            if (!state.isPreLayout() && supportsPredictiveItemAnimations()) {\n                // validate this child is at least partially visible. if not, offset it to start\n                final boolean notVisible =\n                        mOrientationHelper.getDecoratedStart(referenceChild) >= mOrientationHelper\n                                .getEndAfterPadding()\n                                || mOrientationHelper.getDecoratedEnd(referenceChild)\n                                < mOrientationHelper.getStartAfterPadding();\n                if (notVisible) {\n                    anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd\n                            ? mOrientationHelper.getEndAfterPadding()\n                            : mOrientationHelper.getStartAfterPadding();\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * If there is a pending scroll position or saved states, updates the anchor info from that\n     * data and returns true\n     */\n    private boolean updateAnchorFromPendingDataExpose(RecyclerView.State state, AnchorInfo anchorInfo) {\n        if (state.isPreLayout() || mCurrentPendingScrollPosition == RecyclerView.NO_POSITION) {\n            return false;\n        }\n        // validate scroll position\n        if (mCurrentPendingScrollPosition < 0 || mCurrentPendingScrollPosition >= state.getItemCount()) {\n            mCurrentPendingScrollPosition = RecyclerView.NO_POSITION;\n            mPendingScrollPositionOffset = INVALID_OFFSET;\n            if (DEBUG) {\n                Log.e(TAG, \"ignoring invalid scroll position \" + mCurrentPendingScrollPosition);\n            }\n            return false;\n        }\n\n        // if child is visible, try to make it a reference child and ensure it is fully visible.\n        // if child is not visible, align it depending on its virtual position.\n        anchorInfo.mPosition = mCurrentPendingScrollPosition;\n        if (mCurrentPendingSavedState != null && mCurrentPendingSavedState.getInt(\"AnchorPosition\")>=0) {\n            // Anchor offset depends on how that child was laid out. Here, we update it\n            // according to our current view bounds\n            anchorInfo.mLayoutFromEnd = mCurrentPendingSavedState.getBoolean(\"AnchorLayoutFromEnd\");\n            if (anchorInfo.mLayoutFromEnd) {\n                anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -\n                        mCurrentPendingSavedState.getInt(\"AnchorOffset\");\n            } else {\n                anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +\n                        mCurrentPendingSavedState.getInt(\"AnchorOffset\");\n            }\n            return true;\n        }\n\n        if (mPendingScrollPositionOffset == INVALID_OFFSET) {\n            View child = findViewByPosition(mCurrentPendingScrollPosition);\n            if (child != null) {\n                final int childSize = mOrientationHelper.getDecoratedMeasurement(child);\n                if (childSize > mOrientationHelper.getTotalSpace()) {\n                    // item does not fit. fix depending on layout direction\n                    anchorInfo.assignCoordinateFromPadding();\n                    return true;\n                }\n                final int startGap = mOrientationHelper.getDecoratedStart(child)\n                        - mOrientationHelper.getStartAfterPadding();\n                if (startGap < 0) {\n                    anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding();\n                    anchorInfo.mLayoutFromEnd = false;\n                    return true;\n                }\n                final int endGap = mOrientationHelper.getEndAfterPadding() -\n                        mOrientationHelper.getDecoratedEnd(child);\n                if (endGap < 0) {\n                    anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding();\n                    anchorInfo.mLayoutFromEnd = true;\n                    return true;\n                }\n                anchorInfo.mCoordinate = anchorInfo.mLayoutFromEnd\n                        ? (mOrientationHelper.getDecoratedEnd(child) + mOrientationHelper\n                        .getTotalSpaceChange())\n                        : mOrientationHelper.getDecoratedStart(child);\n            } else { // item is not visible.\n                if (getChildCount() > 0) {\n                    // get position of any child, does not matter\n                    int pos = getPosition(getChildAt(0));\n                    anchorInfo.mLayoutFromEnd = mCurrentPendingScrollPosition < pos\n                            == mShouldReverseLayoutExpose;\n                }\n                anchorInfo.assignCoordinateFromPadding();\n            }\n            return true;\n        }\n        // override layout from end values for consistency\n        anchorInfo.mLayoutFromEnd = mShouldReverseLayoutExpose;\n        if (mShouldReverseLayoutExpose) {\n            anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -\n                    mPendingScrollPositionOffset;\n        } else {\n            anchorInfo.mCoordinate = mOrientationHelper.getStartAfterPadding() +\n                    mPendingScrollPositionOffset;\n        }\n        return true;\n    }\n\n    /**\n     * @return The final offset amount for children\n     */\n    private int fixLayoutEndGapExpose(int endOffset, RecyclerView.Recycler recycler,\n                                      RecyclerView.State state, boolean canOffsetChildren) {\n        int gap = mOrientationHelper.getEndAfterPadding() - endOffset;\n        int fixOffset = 0;\n        if (gap > 0) {\n            fixOffset = -scrollInternalBy(-gap, recycler, state);\n        } else {\n            return 0; // nothing to fix\n        }\n        // move offset according to scroll amount\n        endOffset += fixOffset;\n        if (canOffsetChildren) {\n            // re-calculate gap, see if we could fix it\n            gap = mOrientationHelper.getEndAfterPadding() - endOffset;\n            if (gap > 0) {\n                mOrientationHelper.offsetChildren(gap);\n                return gap + fixOffset;\n            }\n        }\n        return fixOffset;\n    }\n\n    /**\n     * @return The final offset amount for children\n     */\n    private int fixLayoutStartGapExpose(int startOffset, RecyclerView.Recycler recycler,\n                                        RecyclerView.State state, boolean canOffsetChildren) {\n        int gap = startOffset - mOrientationHelper.getStartAfterPadding();\n        int fixOffset = 0;\n        if (gap > 0) {\n            // check if we should fix this gap.\n            fixOffset = -scrollInternalBy(gap, recycler, state);\n        } else {\n            return 0; // nothing to fix\n        }\n        startOffset += fixOffset;\n        if (canOffsetChildren) {\n            // re-calculate gap, see if we could fix it\n            gap = startOffset - mOrientationHelper.getStartAfterPadding();\n            if (gap > 0) {\n                mOrientationHelper.offsetChildren(-gap);\n                return fixOffset - gap;\n            }\n        }\n        return fixOffset;\n    }\n\n    private void updateLayoutStateToFillEndExpose(AnchorInfo anchorInfo) {\n        updateLayoutStateToFillEndExpose(anchorInfo.mPosition, anchorInfo.mCoordinate);\n    }\n\n    private void updateLayoutStateToFillEndExpose(int itemPosition, int offset) {\n        mLayoutState.mAvailable = mOrientationHelper.getEndAfterPadding() - offset;\n        mLayoutState.mItemDirection = mShouldReverseLayoutExpose ? LayoutState.ITEM_DIRECTION_HEAD :\n                LayoutState.ITEM_DIRECTION_TAIL;\n        mLayoutState.mCurrentPosition = itemPosition;\n        mLayoutState.mLayoutDirection = LayoutState.LAYOUT_END;\n        mLayoutState.mOffset = offset;\n        mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;\n    }\n\n    private void updateLayoutStateToFillStartExpose(AnchorInfo anchorInfo) {\n        updateLayoutStateToFillStartExpose(anchorInfo.mPosition, anchorInfo.mCoordinate);\n    }\n\n    private void updateLayoutStateToFillStartExpose(int itemPosition, int offset) {\n        mLayoutState.mAvailable = offset - mOrientationHelper.getStartAfterPadding();\n        mLayoutState.mCurrentPosition = itemPosition;\n        mLayoutState.mItemDirection = mShouldReverseLayoutExpose ? LayoutState.ITEM_DIRECTION_TAIL :\n                LayoutState.ITEM_DIRECTION_HEAD;\n        mLayoutState.mLayoutDirection = LayoutState.LAYOUT_START;\n        mLayoutState.mOffset = offset;\n        mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;\n\n    }\n\n    private Object[] emptyArgs = new Object[0];\n\n    protected void ensureLayoutStateExpose() {\n        if (mLayoutState == null) {\n            mLayoutState = new LayoutState();\n        }\n\n        if (mOrientationHelper == null) {\n            mOrientationHelper = OrientationHelperEx.createOrientationHelper(this, getOrientation());\n        }\n\n        try {\n            mEnsureLayoutStateMethod.invoke(this, emptyArgs);\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * <p>Scroll the RecyclerView to make the position visible.</p>\n     * <p/>\n     * <p>RecyclerView will scroll the minimum amount that is necessary to make the\n     * target position visible. If you are looking for a similar behavior to\n     * {@link android.widget.ListView#setSelection(int)} or\n     * {@link android.widget.ListView#setSelectionFromTop(int, int)}, use\n     * {@link #scrollToPositionWithOffset(int, int)}.</p>\n     * <p/>\n     * <p>Note that scroll position change will not be reflected until the next layout call.</p>\n     *\n     * @param position Scroll to this adapter position\n     * @see #scrollToPositionWithOffset(int, int)\n     */\n    @Override\n    public void scrollToPosition(int position) {\n        mCurrentPendingScrollPosition = position;\n        mPendingScrollPositionOffset = INVALID_OFFSET;\n        if (mCurrentPendingSavedState != null) {\n            mCurrentPendingSavedState.putInt(\"AnchorPosition\", RecyclerView.NO_POSITION);\n        }\n        requestLayout();\n    }\n\n    /**\n     * Scroll to the specified adapter position with the given offset from resolved layout\n     * start. Resolved layout start depends on {@link #getReverseLayout()},\n     * {@link ViewCompat#getLayoutDirection(View)} and {@link #getStackFromEnd()}.\n     * <p/>\n     * For example, if layout is {@link #VERTICAL} and {@link #getStackFromEnd()} is true, calling\n     * <code>scrollToPositionWithOffset(10, 20)</code> will layout such that\n     * <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom.\n     * <p/>\n     * Note that scroll position change will not be reflected until the next layout call.\n     * <p/>\n     * <p/>\n     * If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.\n     *\n     * @param position Index (starting at 0) of the reference item.\n     * @param offset   The distance (in pixels) between the start edge of the item view and\n     *                 start edge of the RecyclerView.\n     * @see #setReverseLayout(boolean)\n     * @see #scrollToPosition(int)\n     */\n    public void scrollToPositionWithOffset(int position, int offset) {\n        mCurrentPendingScrollPosition = position;\n        mPendingScrollPositionOffset = offset;\n        if (mCurrentPendingSavedState != null) {\n            mCurrentPendingSavedState.putInt(\"AnchorPosition\", RecyclerView.NO_POSITION);\n        }\n        requestLayout();\n    }\n\n    protected void updateLayoutStateExpose(int layoutDirection, int requiredSpace,\n                                           boolean canUseExistingSpace, RecyclerView.State state) {\n        mLayoutState.mExtra = getExtraLayoutSpace(state);\n        mLayoutState.mLayoutDirection = layoutDirection;\n        int fastScrollSpace;\n        if (layoutDirection == LayoutState.LAYOUT_END) {\n            mLayoutState.mExtra += mOrientationHelper.getEndPadding();\n            // get the first child in the direction we are going\n            final View child = getChildClosestToEndExpose();\n            // the direction in which we are traversing children\n            mLayoutState.mItemDirection = mShouldReverseLayoutExpose ? LayoutState.ITEM_DIRECTION_HEAD\n                    : LayoutState.ITEM_DIRECTION_TAIL;\n            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;\n            mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child) + computeAlignOffset(child, true, false);\n            // calculate how much we can scroll without adding new children (independent of layout)\n            fastScrollSpace = mLayoutState.mOffset\n                    - mOrientationHelper.getEndAfterPadding();\n\n        } else {\n            final View child = getChildClosestToStartExpose();\n            mLayoutState.mExtra += mOrientationHelper.getStartAfterPadding();\n            mLayoutState.mItemDirection = mShouldReverseLayoutExpose ? LayoutState.ITEM_DIRECTION_TAIL\n                    : LayoutState.ITEM_DIRECTION_HEAD;\n            mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;\n\n            mLayoutState.mOffset = mOrientationHelper.getDecoratedStart(child) + computeAlignOffset(child, false, false);\n            fastScrollSpace = -mLayoutState.mOffset\n                    + mOrientationHelper.getStartAfterPadding();\n        }\n        mLayoutState.mAvailable = requiredSpace;\n        if (canUseExistingSpace) {\n            mLayoutState.mAvailable -= fastScrollSpace;\n        }\n        mLayoutState.mScrollingOffset = fastScrollSpace;\n    }\n\n    /**\n     * adjust align offset when fill view during scrolling or get margins when layout from anchor\n     *\n     * @param child\n     * @param isLayoutEnd\n     * @return\n     */\n    protected int computeAlignOffset(View child, boolean isLayoutEnd, boolean useAnchor) {\n        return 0;\n    }\n\n    /**\n     * adjust align offset when fill view during scrolling or get margins when layout from anchor\n     *\n     * @param position\n     * @param isLayoutEnd\n     * @return\n     */\n    protected int computeAlignOffset(int position, boolean isLayoutEnd, boolean useAnchor) {\n        return 0;\n    }\n\n    public boolean isEnableMarginOverLap() {\n        return false;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,\n                                    RecyclerView.State state) {\n        if (getOrientation() == VERTICAL) {\n            return 0;\n        }\n        return scrollInternalBy(dx, recycler, state);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,\n                                  RecyclerView.State state) {\n        if (getOrientation() == HORIZONTAL) {\n            return 0;\n        }\n        return scrollInternalBy(dy, recycler, state);\n    }\n\n    /**\n     * Handle scroll event internally, cover both horizontal and vertical\n     *\n     * @param dy       Pixel that will be scrolled\n     * @param recycler Recycler hold recycled views\n     * @param state    Current {@link RecyclerView} state, hold whether in preLayout, etc.\n     * @return The actual scrolled pixel, it may not be the same as {@code dy}, like when reaching the edge of view\n     */\n    protected int scrollInternalBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {\n        if (getChildCount() == 0 || dy == 0) {\n            return 0;\n        }\n\n\n        // indicate whether need recycle in this pass, true when scrolling, false when layout\n        mLayoutState.mRecycle = true;\n        ensureLayoutStateExpose();\n        final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;\n        final int absDy = Math.abs(dy);\n        updateLayoutStateExpose(layoutDirection, absDy, true, state);\n        final int freeScroll = mLayoutState.mScrollingOffset;\n\n        mLayoutState.mOnRefresLayout = false;\n\n        final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);\n        if (consumed < 0) {\n            if (DEBUG) {\n                Log.d(TAG, \"Don't have any more elements to scroll\");\n            }\n            return 0;\n        }\n        final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;\n        mOrientationHelper.offsetChildren(-scrolled);\n        if (DEBUG) {\n            Log.d(TAG, \"scroll req: \" + dy + \" scrolled: \" + scrolled);\n        }\n\n        return scrolled;\n    }\n\n    @Override\n    public void assertNotInLayoutOrScroll(String message) {\n        if (mCurrentPendingSavedState == null) {\n            super.assertNotInLayoutOrScroll(message);\n        }\n    }\n\n    /**\n     * Recycles children between given indices.\n     *\n     * @param startIndex inclusive\n     * @param endIndex   exclusive\n     */\n    protected void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {\n        if (startIndex == endIndex) {\n            return;\n        }\n        if (DEBUG) {\n            Log.d(TAG, \"Recycling \" + Math.abs(startIndex - endIndex) + \" items\");\n        }\n        if (endIndex > startIndex) {\n            for (int i = endIndex - 1; i >= startIndex; i--) {\n                removeAndRecycleViewAt(i, recycler);\n            }\n        } else {\n            for (int i = startIndex; i > endIndex; i--) {\n                removeAndRecycleViewAt(i, recycler);\n            }\n        }\n    }\n\n    /**\n     * Recycles views that went out of bounds after scrolling towards the end of the layout.\n     *\n     * @param recycler Recycler instance of {@link RecyclerView}\n     * @param dt       This can be used to add additional padding to the visible area. This is used\n     *                 to\n     *                 detect children that will go out of bounds after scrolling, without actually\n     *                 moving them.\n     */\n    private void recycleViewsFromStartExpose(RecyclerView.Recycler recycler, int dt) {\n        if (dt < 0) {\n            if (DEBUG) {\n                Log.d(TAG, \"Called recycle from start with a negative value. This might happen\"\n                        + \" during layout changes but may be sign of a bug\");\n            }\n            return;\n        }\n        // ignore padding, ViewGroup may not clip children.\n        final int limit = dt;\n        final int childCount = getChildCount();\n        if (mShouldReverseLayoutExpose) {\n            for (int i = childCount - 1; i >= 0; i--) {\n                View child = getChildAt(i);\n                if (mOrientationHelper.getDecoratedEnd(child) + recycleOffset > limit) {// stop here\n                    recycleChildren(recycler, childCount - 1, i);\n                    return;\n                }\n            }\n        } else {\n            for (int i = 0; i < childCount; i++) {\n                View child = getChildAt(i);\n                if (mOrientationHelper.getDecoratedEnd(child) + recycleOffset > limit) {// stop here\n                    recycleChildren(recycler, 0, i);\n                    return;\n                }\n            }\n        }\n    }\n\n\n    /**\n     * Recycles views that went out of bounds after scrolling towards the start of the layout.\n     *\n     * @param recycler Recycler instance of {@link RecyclerView}\n     * @param dt       This can be used to add additional padding to the visible area. This is used\n     *                 to detect children that will go out of bounds after scrolling, without\n     *                 actually moving them.\n     */\n    private void recycleViewsFromEndExpose(RecyclerView.Recycler recycler, int dt) {\n        final int childCount = getChildCount();\n        if (dt < 0) {\n            if (DEBUG) {\n                Log.d(TAG, \"Called recycle from end with a negative value. This might happen\"\n                        + \" during layout changes but may be sign of a bug\");\n            }\n            return;\n        }\n        final int limit = mOrientationHelper.getEnd() - dt;\n        if (mShouldReverseLayoutExpose) {\n            for (int i = 0; i < childCount; i++) {\n                View child = getChildAt(i);\n                if (mOrientationHelper.getDecoratedStart(child) - recycleOffset < limit) {// stop here\n                    recycleChildren(recycler, 0, i);\n                    return;\n                }\n            }\n        } else {\n            for (int i = childCount - 1; i >= 0; i--) {\n                View child = getChildAt(i);\n                if (mOrientationHelper.getDecoratedStart(child) - recycleOffset < limit) {// stop here\n                    recycleChildren(recycler, childCount - 1, i);\n                    return;\n                }\n            }\n        }\n\n    }\n\n    /**\n     * Helper method to call appropriate recycle method depending on current layout direction\n     *\n     * @param recycler    Current recycler that is attached to RecyclerView\n     * @param layoutState Current layout state. Right now, this object does not change but\n     *                    we may consider moving it out of this view so passing around as a\n     *                    parameter for now, rather than accessing {@link #mLayoutState}\n     * @see #recycleViewsFromStartExpose(RecyclerView.Recycler, int)\n     * @see #recycleViewsFromEndExpose(RecyclerView.Recycler, int)\n     * @see LinearLayoutManager.LayoutState#mLayoutDirection\n     */\n    private void recycleByLayoutStateExpose(RecyclerView.Recycler recycler, LayoutState layoutState) {\n        if (!layoutState.mRecycle) {\n            return;\n        }\n        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {\n            recycleViewsFromEndExpose(recycler, layoutState.mScrollingOffset);\n        } else {\n            recycleViewsFromStartExpose(recycler, layoutState.mScrollingOffset);\n        }\n    }\n\n    private com.alibaba.android.vlayout.layout.LayoutChunkResult layoutChunkResultCache\n            = new com.alibaba.android.vlayout.layout.LayoutChunkResult();\n\n    /**\n     * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly\n     * independent from the rest of the {@link LinearLayoutManager}\n     * and with little change, can be made publicly available as a helper class.\n     *\n     * @param recycler        Current recycler that is attached to RecyclerView\n     * @param layoutState     Configuration on how we should fill out the available space.\n     * @param state           Context passed by the RecyclerView to control scroll steps.\n     * @param stopOnFocusable If true, filling stops in the first focusable new child\n     * @return Number of pixels that it added. Useful for scoll functions.\n     */\n    protected int fill(RecyclerView.Recycler recycler, LayoutState layoutState,\n                       RecyclerView.State state, boolean stopOnFocusable) {\n        // max offset we should set is mFastScroll + available\n        final int start = layoutState.mAvailable;\n        if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {\n            // TODO ugly bug fix. should not happen\n            if (layoutState.mAvailable < 0) {\n                layoutState.mScrollingOffset += layoutState.mAvailable;\n            }\n            recycleByLayoutStateExpose(recycler, layoutState);\n        }\n        int remainingSpace = layoutState.mAvailable + layoutState.mExtra + recycleOffset;\n        while (remainingSpace > 0 && layoutState.hasMore(state)) {\n            layoutChunkResultCache.resetInternal();\n            layoutChunk(recycler, state, layoutState, layoutChunkResultCache);\n            if (layoutChunkResultCache.mFinished) {\n                break;\n            }\n            layoutState.mOffset += layoutChunkResultCache.mConsumed * layoutState.mLayoutDirection;\n            /**\n             * Consume the available space if:\n             * * layoutChunk did not request to be ignored\n             * * OR we are laying out scrap children\n             * * OR we are not doing pre-layout\n             */\n            if (!layoutChunkResultCache.mIgnoreConsumed || mLayoutState.mScrapList != null\n                    || !state.isPreLayout()) {\n                layoutState.mAvailable -= layoutChunkResultCache.mConsumed;\n                // we keep a separate remaining space because mAvailable is important for recycling\n                remainingSpace -= layoutChunkResultCache.mConsumed;\n            }\n\n            if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) {\n                layoutState.mScrollingOffset += layoutChunkResultCache.mConsumed;\n                if (layoutState.mAvailable < 0) {\n                    layoutState.mScrollingOffset += layoutState.mAvailable;\n                }\n                recycleByLayoutStateExpose(recycler, layoutState);\n            }\n            if (stopOnFocusable && layoutChunkResultCache.mFocusable) {\n                break;\n            }\n        }\n        if (DEBUG) {\n            validateChildOrderExpose();\n        }\n        return start - layoutState.mAvailable;\n    }\n\n    protected void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,\n                               LayoutState layoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult result) {\n        View view = layoutState.next(recycler);\n        if (view == null) {\n            if (DEBUG && layoutState.mScrapList == null) {\n                throw new RuntimeException(\"received null view when unexpected\");\n            }\n            // if we are laying out views in scrap, this may return null which means there is\n            // no more items to layout.\n            result.mFinished = true;\n            return;\n        }\n        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();\n        if (layoutState.mScrapList == null) {\n            // can not find in scrapList\n            if (mShouldReverseLayoutExpose == (layoutState.mLayoutDirection\n                    == LayoutState.LAYOUT_START)) {\n                addView(view);\n            } else {\n                addView(view, 0);\n            }\n        } else {\n            if (mShouldReverseLayoutExpose == (layoutState.mLayoutDirection\n                    == LayoutState.LAYOUT_START)) {\n                addDisappearingView(view);\n            } else {\n                addDisappearingView(view, 0);\n            }\n        }\n        measureChildWithMargins(view, 0, 0);\n        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);\n        int left, top, right, bottom;\n        if (getOrientation() == VERTICAL) {\n            if (isLayoutRTL()) {\n                right = getWidth() - getPaddingRight();\n                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);\n            } else {\n                left = getPaddingLeft();\n                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);\n            }\n\n            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {\n                bottom = layoutState.mOffset;\n                top = layoutState.mOffset - result.mConsumed;\n            } else {\n                top = layoutState.mOffset;\n                bottom = layoutState.mOffset + result.mConsumed;\n            }\n        } else {\n            top = getPaddingTop();\n            bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);\n\n            // whether this layout pass is layout form start to end or in reverse\n            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {\n                right = layoutState.mOffset;\n                left = layoutState.mOffset - result.mConsumed;\n            } else {\n                left = layoutState.mOffset;\n                right = layoutState.mOffset + result.mConsumed;\n            }\n        }\n        // We calculate everything with View's bounding box (which includes decor and margins)\n        // To calculate correct layout position, we subtract margins.\n        layoutDecorated(view, left + params.leftMargin, top + params.topMargin,\n                right - params.rightMargin, bottom - params.bottomMargin);\n        if (DEBUG) {\n            Log.d(TAG, \"laid out child at position \" + getPosition(view) + \", with l:\"\n                    + (left + params.leftMargin) + \", t:\" + (top + params.topMargin) + \", r:\"\n                    + (right - params.rightMargin) + \", b:\" + (bottom - params.bottomMargin));\n        }\n        // Consume the available space if the view is not removed OR changed\n        if (params.isItemRemoved() || params.isItemChanged()) {\n            result.mIgnoreConsumed = true;\n        }\n        result.mFocusable = view.isFocusable();\n    }\n\n    /**\n     * Converts a focusDirection to orientation.\n     *\n     * @param focusDirection One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},\n     *                       {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},\n     *                       {@link View#FOCUS_BACKWARD}, {@link View#FOCUS_FORWARD}\n     *                       or 0 for not applicable\n     * @return {@link LayoutState#LAYOUT_START} or {@link LayoutState#LAYOUT_END} if focus direction\n     * is applicable to current state, {@link LayoutState#INVALID_LAYOUT} otherwise.\n     */\n    private int convertFocusDirectionToLayoutDirectionExpose(int focusDirection) {\n        int orientation = getOrientation();\n        switch (focusDirection) {\n            case View.FOCUS_BACKWARD:\n                return LayoutState.LAYOUT_START;\n            case View.FOCUS_FORWARD:\n                return LayoutState.LAYOUT_END;\n            case View.FOCUS_UP:\n                return orientation == VERTICAL ? LayoutState.LAYOUT_START\n                        : LayoutState.INVALID_LAYOUT;\n            case View.FOCUS_DOWN:\n                return orientation == VERTICAL ? LayoutState.LAYOUT_END\n                        : LayoutState.INVALID_LAYOUT;\n            case View.FOCUS_LEFT:\n                return orientation == HORIZONTAL ? LayoutState.LAYOUT_START\n                        : LayoutState.INVALID_LAYOUT;\n            case View.FOCUS_RIGHT:\n                return orientation == HORIZONTAL ? LayoutState.LAYOUT_END\n                        : LayoutState.INVALID_LAYOUT;\n            default:\n                if (DEBUG) {\n                    Log.d(TAG, \"Unknown focus request:\" + focusDirection);\n                }\n                return LayoutState.INVALID_LAYOUT;\n        }\n\n    }\n\n    /**\n     * Convenience method to find the child closes to start. Caller should check it has enough\n     * children.\n     *\n     * @return The child closes to start of the layout from user's perspective.\n     */\n    private View getChildClosestToStartExpose() {\n        return getChildAt(mShouldReverseLayoutExpose ? getChildCount() - 1 : 0);\n    }\n\n    /**\n     * Convenience method to find the child closes to end. Caller should check it has enough\n     * children.\n     *\n     * @return The child closes to end of the layout from user's perspective.\n     */\n    private View getChildClosestToEndExpose() {\n        return getChildAt(mShouldReverseLayoutExpose ? 0 : getChildCount() - 1);\n    }\n\n    @Override\n    public View onFocusSearchFailed(View focused, int focusDirection,\n                                    RecyclerView.Recycler recycler, RecyclerView.State state) {\n        myResolveShouldLayoutReverse();\n        if (getChildCount() == 0) {\n            return null;\n        }\n\n        final int layoutDir = convertFocusDirectionToLayoutDirectionExpose(focusDirection);\n        if (layoutDir == LayoutState.INVALID_LAYOUT) {\n            return null;\n        }\n\n        View referenceChild = null;\n        if (layoutDir == LayoutState.LAYOUT_START) {\n            referenceChild = myFindReferenceChildClosestToStart(state);\n        } else {\n            referenceChild = myFindReferenceChildClosestToEnd(state);\n\n        }\n\n        if (referenceChild == null) {\n            if (DEBUG) {\n                Log.d(TAG,\n                        \"Cannot find a child with a valid position to be used for focus search.\");\n            }\n            return null;\n        }\n        ensureLayoutStateExpose();\n        final int maxScroll = (int) (MAX_SCROLL_FACTOR * mOrientationHelper.getTotalSpace());\n        updateLayoutStateExpose(layoutDir, maxScroll, false, state);\n        mLayoutState.mScrollingOffset = LayoutState.SCOLLING_OFFSET_NaN;\n        mLayoutState.mRecycle = false;\n        mLayoutState.mOnRefresLayout = false;\n        fill(recycler, mLayoutState, state, true);\n        final View nextFocus;\n        if (layoutDir == LayoutState.LAYOUT_START) {\n            nextFocus = getChildClosestToStartExpose();\n        } else {\n            nextFocus = getChildClosestToEndExpose();\n        }\n        if (nextFocus == referenceChild || !nextFocus.isFocusable()) {\n            return null;\n        }\n        return nextFocus;\n    }\n\n    /**\n     * Used for debugging.\n     * Logs the internal representation of children to default logger.\n     */\n    private void logChildren() {\n        Log.d(TAG, \"internal representation of views on the screen\");\n        for (int i = 0; i < getChildCount(); i++) {\n            View child = getChildAt(i);\n            Log.d(TAG, \"item \" + getPosition(child) + \", coord:\"\n                    + mOrientationHelper.getDecoratedStart(child));\n        }\n        Log.d(TAG, \"==============\");\n    }\n\n    /**\n     * Used for debugging.\n     * Validates that child views are laid out in correct order. This is important because rest of\n     * the algorithm relies on this constraint.\n     * <p/>\n     * In default layout, child 0 should be closest to screen position 0 and last child should be\n     * closest to position WIDTH or HEIGHT.\n     * In reverse layout, last child should be closes to screen position 0 and first child should\n     * be closest to position WIDTH  or HEIGHT\n     */\n    private void validateChildOrderExpose() {\n        Log.d(TAG, \"validating child count \" + getChildCount());\n        if (getChildCount() < 1) {\n            return;\n        }\n        int lastPos = getPosition(getChildAt(0));\n        int lastScreenLoc = mOrientationHelper.getDecoratedStart(getChildAt(0));\n        if (mShouldReverseLayoutExpose) {\n            for (int i = 1; i < getChildCount(); i++) {\n                View child = getChildAt(i);\n                int pos = getPosition(child);\n                int screenLoc = mOrientationHelper.getDecoratedStart(child);\n                if (pos < lastPos) {\n                    logChildren();\n                    throw new RuntimeException(\"detected invalid position. loc invalid? \" +\n                            (screenLoc < lastScreenLoc));\n                }\n                if (screenLoc > lastScreenLoc) {\n                    logChildren();\n                    throw new RuntimeException(\"detected invalid location\");\n                }\n            }\n        } else {\n            for (int i = 1; i < getChildCount(); i++) {\n                View child = getChildAt(i);\n                int pos = getPosition(child);\n                int screenLoc = mOrientationHelper.getDecoratedStart(child);\n                if (pos < lastPos) {\n                    logChildren();\n                    throw new RuntimeException(\"detected invalid position. loc invalid? \" +\n                            (screenLoc < lastScreenLoc));\n                }\n                if (screenLoc < lastScreenLoc) {\n                    logChildren();\n                    throw new RuntimeException(\"detected invalid location\");\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean supportsPredictiveItemAnimations() {\n        return mCurrentPendingSavedState == null && mLastStackFromEnd == getStackFromEnd();\n    }\n\n    //==================================\n    // Extends method\n    //==================================\n    protected void addHiddenView(View view, boolean head) {\n        int index = head ? 0 : -1;\n        addView(view, index);\n        mChildHelperWrapper.hide(view);\n    }\n\n    protected void hideView(View view) {\n        mChildHelperWrapper.hide(view);\n    }\n\n    protected void showView(View view) {\n        mChildHelperWrapper.show(view);\n    }\n\n    protected View findHiddenView(int position) {\n        return mChildHelperWrapper.findHiddenNonRemovedView(position, RecyclerView.INVALID_TYPE);\n    }\n\n    protected boolean isHidden(View view) {\n        return mChildHelperWrapper.isHidden(view);\n    }\n\n    static final int FLAG_INVALID = 1 << 2;\n\n    static final int FLAG_UPDATED = 1 << 1;\n\n    private static Field vhField = null;\n    private static Method vhSetFlags = null;\n\n    protected static boolean isViewHolderUpdated(RecyclerView.ViewHolder holder) {\n        return new ViewHolderWrapper(holder).requireUpdated();\n    }\n\n    protected static void attachViewHolder(RecyclerView.LayoutParams params, RecyclerView.ViewHolder holder) {\n        try {\n\n            if (vhField == null) {\n                vhField = RecyclerView.LayoutParams.class.getDeclaredField(\"mViewHolder\");\n            }\n\n            vhField.setAccessible(true);\n            vhField.set(params, holder);\n\n            if (vhSetFlags == null) {\n                vhSetFlags = RecyclerView.ViewHolder.class.getDeclaredMethod(\"setFlags\", int.class, int.class);\n                vhSetFlags.setAccessible(true);\n            }\n\n            vhSetFlags.invoke(holder, FLAG_INVALID, FLAG_INVALID);\n        } catch (NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n        }\n    }\n\n\n    /**\n     * Helper class that keeps temporary state while {LayoutManager} is filling out the empty\n     * space.\n     */\n    public static class LayoutState {\n\n        private Method vhIsRemoved = null;\n\n        final static String TAG = \"_ExposeLLayoutManager#LayoutState\";\n\n        public final static int LAYOUT_START = -1;\n\n        public final static int LAYOUT_END = 1;\n\n        final static int INVALID_LAYOUT = Integer.MIN_VALUE;\n\n        public final static int ITEM_DIRECTION_HEAD = -1;\n\n        public final static int ITEM_DIRECTION_TAIL = 1;\n\n        final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE;\n\n        public boolean mOnRefresLayout = false;\n\n        /**\n         * We may not want to recycle children in some cases (e.g. layout)\n         */\n        public boolean mRecycle = true;\n\n        /**\n         * Pixel offset where layout should start\n         */\n        public int mOffset;\n\n        /**\n         * Number of pixels that we should fill, in the layout direction.\n         */\n        public int mAvailable;\n\n        /**\n         * Current position on the adapter to get the next item.\n         */\n        public int mCurrentPosition;\n\n        /**\n         * Defines the direction in which the data adapter is traversed.\n         * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}\n         */\n        public int mItemDirection;\n\n        /**\n         * Defines the direction in which the layout is filled.\n         * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}\n         */\n        public int mLayoutDirection;\n\n        /**\n         * Used when LayoutState is constructed in a scrolling state.\n         * It should be set the amount of scrolling we can make without creating a new view.\n         * Settings this is required for efficient view recycling.\n         */\n        public int mScrollingOffset;\n\n        /**\n         * Used if you want to pre-layout items that are not yet visible.\n         * The difference with {@link #mAvailable} is that, when recycling, distance laid out for\n         * {@link #mExtra} is not considered to avoid recycling visible children.\n         */\n        public int mExtra = 0;\n\n\n        /**\n         * Used when Layout is fixed scrolling\n         */\n        public int mFixOffset = 0;\n\n        /**\n         * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value\n         * is set to true, we skip removed views since they should not be laid out in post layout\n         * step.\n         */\n        public boolean mIsPreLayout = false;\n\n        /**\n         * When LLM needs to layout particular views, it sets this list in which case, LayoutState\n         * will only return views from this list and return null if it cannot find an item.\n         */\n        public List<RecyclerView.ViewHolder> mScrapList = null;\n\n        public LayoutState() {\n            try {\n                vhIsRemoved = RecyclerView.ViewHolder.class.getDeclaredMethod(\"isRemoved\");\n                vhIsRemoved.setAccessible(true);\n            } catch (NoSuchMethodException e) {\n                e.printStackTrace();\n                throw new RuntimeException(e);\n            }\n        }\n\n\n        /**\n         * @return true if there are more items in the data adapter\n         */\n        public boolean hasMore(RecyclerView.State state) {\n            return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();\n        }\n\n        /**\n         * Gets the view for the next element that we should layout.\n         * Also updates current item index to the next item, based on {@link #mItemDirection}\n         *\n         * @return The next element that we should layout.\n         */\n        public View next(RecyclerView.Recycler recycler) {\n            if (mScrapList != null) {\n                return nextFromLimitedList();\n            }\n            final View view = recycler.getViewForPosition(mCurrentPosition);\n            mCurrentPosition += mItemDirection;\n            return view;\n        }\n\n        /**\n         * Returns next item from limited list.\n         * <p/>\n         * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection\n         *\n         * @return View if an item in the current position or direction exists if not null.\n         */\n        @SuppressLint(\"LongLogTag\")\n        private View nextFromLimitedList() {\n            int size = mScrapList.size();\n            RecyclerView.ViewHolder closest = null;\n            int closestDistance = Integer.MAX_VALUE;\n            for (int i = 0; i < size; i++) {\n                RecyclerView.ViewHolder viewHolder = mScrapList.get(i);\n                if (!mIsPreLayout) {\n                    boolean isRemoved = false;\n                    try {\n                        isRemoved = (boolean) vhIsRemoved.invoke(viewHolder);\n                    } catch (IllegalAccessException e) {\n                        e.printStackTrace();\n                    } catch (InvocationTargetException e) {\n                        e.printStackTrace();\n                    }\n\n                    if (!mIsPreLayout && isRemoved) {\n                        continue;\n                    }\n                }\n                final int distance = (viewHolder.getPosition() - mCurrentPosition) * mItemDirection;\n                if (distance < 0) {\n                    continue; // item is not in current direction\n                }\n                if (distance < closestDistance) {\n                    closest = viewHolder;\n                    closestDistance = distance;\n                    if (distance == 0) {\n                        break;\n                    }\n                }\n            }\n            if (DEBUG) {\n                Log.d(TAG, \"layout from scrap. found view:?\" + (closest != null));\n            }\n            if (closest != null) {\n                mCurrentPosition = closest.getPosition() + mItemDirection;\n                return closest.itemView;\n            }\n            return null;\n        }\n\n        @SuppressLint(\"LongLogTag\")\n        void log() {\n            Log.d(TAG, \"avail:\" + mAvailable + \", ind:\" + mCurrentPosition + \", dir:\" +\n                    mItemDirection + \", offset:\" + mOffset + \", layoutDir:\" + mLayoutDirection);\n        }\n    }\n\n    /**\n     * Simple data class to keep Anchor information\n     */\n    protected class AnchorInfo {\n        public int mPosition;\n        public int mCoordinate;\n        public boolean mLayoutFromEnd;\n\n        void reset() {\n            mPosition = RecyclerView.NO_POSITION;\n            mCoordinate = INVALID_OFFSET;\n            mLayoutFromEnd = false;\n        }\n\n        /**\n         * assigns anchor coordinate from the RecyclerView's padding depending on current\n         * layoutFromEnd value\n         */\n        void assignCoordinateFromPadding() {\n            mCoordinate = mLayoutFromEnd\n                    ? mOrientationHelper.getEndAfterPadding()\n                    : mOrientationHelper.getStartAfterPadding();\n        }\n\n        @Override\n        public String toString() {\n            return \"AnchorInfo{\" +\n                    \"mPosition=\" + mPosition +\n                    \", mCoordinate=\" + mCoordinate +\n                    \", mLayoutFromEnd=\" + mLayoutFromEnd +\n                    '}';\n        }\n\n        /**\n         * Assign anchor position information from the provided view if it is valid as a reference\n         * child.\n         */\n        public boolean assignFromViewIfValid(View child, RecyclerView.State state) {\n            RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();\n            if (!lp.isItemRemoved() && lp.getViewPosition() >= 0\n                    && lp.getViewPosition() < state.getItemCount()) {\n                assignFromView(child);\n                return true;\n            }\n            return false;\n        }\n\n        public void assignFromView(View child) {\n            if (mLayoutFromEnd) {\n                mCoordinate = mOrientationHelper.getDecoratedEnd(child) + computeAlignOffset(child, mLayoutFromEnd, true) +\n                        mOrientationHelper.getTotalSpaceChange();\n                if (DEBUG) {\n                    Log.d(TAG, \"1 mLayoutFromEnd \" + mLayoutFromEnd + \" mOrientationHelper.getDecoratedEnd(child) \"\n                        + mOrientationHelper.getDecoratedEnd(child) + \" computeAlignOffset(child, mLayoutFromEnd, true) \" + computeAlignOffset(child, mLayoutFromEnd, true));\n                }\n            } else {\n                mCoordinate = mOrientationHelper.getDecoratedStart(child) + computeAlignOffset(child, mLayoutFromEnd, true);\n                if (DEBUG) {\n                    Log.d(TAG, \"2 mLayoutFromEnd \" + mLayoutFromEnd + \" mOrientationHelper.getDecoratedStart(child) \"\n                        + mOrientationHelper.getDecoratedStart(child) + \" computeAlignOffset(child, mLayoutFromEnd, true) \" + computeAlignOffset(child, mLayoutFromEnd, true));\n                }\n            }\n\n            mPosition = getPosition(child);\n            if (DEBUG) {\n                Log.d(TAG, \"position \" + mPosition + \" mCoordinate \" + mCoordinate);\n            }\n        }\n    }\n\n\n    static class ViewHolderWrapper {\n        private RecyclerView.ViewHolder mHolder;\n\n        private static Method mShouldIgnore;\n        private static Method mIsInvalid;\n        private static Method mIsRemoved;\n        private static Method mIsChanged;\n        private static Method mSetFlags;\n\n\n        static {\n            try {\n                mShouldIgnore = RecyclerView.ViewHolder.class.getDeclaredMethod(\"shouldIgnore\");\n                mShouldIgnore.setAccessible(true);\n                mIsInvalid = RecyclerView.ViewHolder.class.getDeclaredMethod(\"isInvalid\");\n                mIsInvalid.setAccessible(true);\n                mIsRemoved = RecyclerView.ViewHolder.class.getDeclaredMethod(\"isRemoved\");\n                mIsRemoved.setAccessible(true);\n\n                mSetFlags = RecyclerView.ViewHolder.class.getDeclaredMethod(\"setFlags\", int.class, int.class);\n                mSetFlags.setAccessible(true);\n\n                try {\n                    mIsChanged = RecyclerView.ViewHolder.class.getDeclaredMethod(\"isChanged\");\n                } catch (NoSuchMethodException e) {\n                    mIsChanged = RecyclerView.ViewHolder.class.getDeclaredMethod(\"isUpdated\");\n                }\n\n                mIsChanged.setAccessible(true);\n            } catch (NoSuchMethodException e) {\n                e.printStackTrace();\n            }\n        }\n\n\n        public static void setFlags(RecyclerView.ViewHolder viewHolder, int flags, int mask) {\n            try {\n                mSetFlags.invoke(viewHolder, flags, mask);\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n        }\n\n        public ViewHolderWrapper(RecyclerView.ViewHolder holder) {\n            this.mHolder = holder;\n\n        }\n\n        boolean isInvalid() {\n            if (mIsInvalid == null) return true;\n            try {\n                return (boolean) mIsInvalid.invoke(mHolder);\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n            return true;\n        }\n\n        boolean isRemoved() {\n            if (mIsRemoved == null) return true;\n            try {\n                return (boolean) mIsRemoved.invoke(mHolder);\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n            return true;\n        }\n\n        boolean isChanged() {\n            if (mIsChanged == null) return true;\n            try {\n                return (boolean) mIsChanged.invoke(mHolder);\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n            return true;\n        }\n\n        void setFlags(int flags, int mask) {\n            try {\n                mSetFlags.invoke(mHolder, flags, mask);\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n        }\n\n\n        public boolean requireUpdated() {\n            return isInvalid() || isRemoved() || isChanged();\n        }\n\n\n    }\n\n    class ChildHelperWrapper {\n        private Object mInnerChildHelper;\n\n        private Method mHideMethod;\n\n        private Method mFindHiddenNonRemovedViewMethod;\n\n        /** start from 25.2.0, maybe earlier, this method reduce parameters from two to one */\n        private Method mFindHiddenNonRemovedViewMethod25;\n\n        private Method mIsHideMethod;\n\n        private Field mHiddenViewField;\n\n        private Object mInnerBucket;\n\n        private Method mClearMethod;\n\n        private Field mChildHelperField;\n\n        private List mInnerHiddenView;\n\n        private RecyclerView.LayoutManager mLayoutManager;\n\n        private Object[] args = new Object[1];\n\n        void ensureChildHelper() {\n            try {\n                if (mInnerChildHelper == null) {\n                    mInnerChildHelper = mChildHelperField.get(mLayoutManager);\n                    if (mInnerChildHelper == null) return;\n\n                    Class<?> helperClz = mInnerChildHelper.getClass();\n                    mHideMethod = helperClz.getDeclaredMethod(\"hide\", View.class);\n                    mHideMethod.setAccessible(true);\n                    try {\n                        mFindHiddenNonRemovedViewMethod = helperClz.getDeclaredMethod(\"findHiddenNonRemovedView\", int.class, int.class);\n                        mFindHiddenNonRemovedViewMethod.setAccessible(true);\n                    } catch (NoSuchMethodException nsme) {\n                        mFindHiddenNonRemovedViewMethod25 = helperClz.getDeclaredMethod(\"findHiddenNonRemovedView\", int.class);\n                        mFindHiddenNonRemovedViewMethod25.setAccessible(true);\n                    }\n                    mIsHideMethod = helperClz.getDeclaredMethod(\"isHidden\", View.class);\n                    mIsHideMethod.setAccessible(true);\n\n                    Field bucketField = helperClz.getDeclaredField(\"mBucket\");\n                    bucketField.setAccessible(true);\n\n                    mInnerBucket = bucketField.get(mInnerChildHelper);\n                    mClearMethod = mInnerBucket.getClass().getDeclaredMethod(\"clear\", int.class);\n                    mClearMethod.setAccessible(true);\n\n                    mHiddenViewField = helperClz.getDeclaredField(\"mHiddenViews\");\n                    mHiddenViewField.setAccessible(true);\n                    mInnerHiddenView = (List) mHiddenViewField.get(mInnerChildHelper);\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n\n        }\n\n        ChildHelperWrapper(RecyclerView.LayoutManager layoutManager) {\n            this.mLayoutManager = layoutManager;\n            try {\n                mChildHelperField = RecyclerView.LayoutManager.class.getDeclaredField(\"mChildHelper\");\n                mChildHelperField.setAccessible(true);\n                ensureChildHelper();\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n\n        void hide(View view) {\n            try {\n                ensureChildHelper();\n                if (mInnerHiddenView.indexOf(view) < 0) {\n                    args[0] = view;\n                    mHideMethod.invoke(mInnerChildHelper, args);\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n\n        void show(View view) {\n            try {\n                ensureChildHelper();\n                int index = mRecyclerView.indexOfChild(view);\n                args[0] = Integer.valueOf(index);\n                mClearMethod.invoke(mInnerBucket, args);\n                if (mInnerHiddenView != null)\n                    mInnerHiddenView.remove(view);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n\n        View findHiddenNonRemovedView(int position, int type) {\n            try {\n                ensureChildHelper();\n                if (mFindHiddenNonRemovedViewMethod != null) {\n                    return (View) mFindHiddenNonRemovedViewMethod.invoke(mInnerChildHelper, position, RecyclerView.INVALID_TYPE);\n                } else if (mFindHiddenNonRemovedViewMethod25 != null) {\n                    return (View) mFindHiddenNonRemovedViewMethod25.invoke(mInnerChildHelper, position);\n                }\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            return null;\n        }\n\n\n        boolean isHidden(View view) {\n            try {\n                ensureChildHelper();\n                args[0] = view;\n                return (boolean) mIsHideMethod.invoke(mInnerChildHelper, args);\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            return false;\n        }\n\n    }\n\n}\n\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/LayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.graphics.Rect;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\n\nimport com.alibaba.android.vlayout.layout.LayoutChunkResult;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\n/**\n * Helper class to handle different layouts in {@link VirtualLayoutManager}\n *\n * @author villadora\n * @date 2015-8-14\n * @since 1.0.0\n */\n\npublic abstract class LayoutHelper {\n\n    public static final Range<Integer> RANGE_ALL = Range.create(Integer.MIN_VALUE, Integer.MAX_VALUE);\n    public static final Range<Integer> RANGE_EMPTY = Range.create(-1, -1);\n\n    /**\n     * Range for this layoutHelper, intialize with EMPTY\n     */\n    @NonNull\n    Range<Integer> mRange = RANGE_EMPTY;\n\n    int mZIndex = 0;\n\n\n    /**\n     * Is the position should be handle by this {@link LayoutHelper}\n     *\n     * @param position position without offset, which is the true index in {@link VirtualLayoutManager}\n     * @return true if position in range returned by {@link #getRange()}\n     */\n    public boolean isOutOfRange(int position) {\n        return !mRange.contains(position);\n    }\n\n\n    /**\n     * Set range of items, which will be handled by this layoutHelper\n     * start position must be greater than end position, otherwise {@link IllegalArgumentException}\n     * will be thrown\n     *\n     * @param start start position of items handled by this layoutHelper\n     * @param end   end position of items handled by this layoutHelper, if end < start, it will throw {@link IllegalArgumentException}\n     * @throws MismatchChildCountException when the (start - end) doesn't equal to itemCount\n     */\n    public void setRange(int start, int end) {\n        if (end < start) {\n            throw new IllegalArgumentException(\"end should be larger or equeal then start position\");\n        }\n\n        if (start == -1 && end == -1) {\n            this.mRange = RANGE_EMPTY;\n            onRangeChange(start, end);\n            return;\n        }\n\n        if ((end - start + 1) != getItemCount()) {\n            throw new MismatchChildCountException(\"ItemCount mismatch when range: \" + mRange.toString() + \" childCount: \" + getItemCount());\n        }\n\n        if (start == mRange.getUpper() && end == mRange.getLower()) {\n            // no change\n            return;\n        }\n\n        this.mRange = Range.create(start, end);\n        onRangeChange(start, end);\n    }\n\n    /**\n     * This method will be called when range changes\n     *\n     * @param start start position of items handled by this layoutHelper\n     * @param end   end position of items handled by this layoutHelper, if end < start, it will throw {@link IllegalArgumentException}\n     */\n    public void onRangeChange(final int start, final int end) {\n\n    }\n\n    /**\n     * Return current range\n     *\n     * @return Range of integer\n     */\n    @NonNull\n    public final Range<Integer> getRange() {\n        return mRange;\n    }\n\n\n    /**\n     * Given a chance to check and change the chosen anchorInfo\n     *\n     * @param state      current {@link }RecyclerView} 's state\n     * @param anchorInfo the chosen anchorInfo\n     * @param helper\n     */\n    public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n\n    }\n\n    /**\n     * This method is called when scroll state is changed\n     *\n     * @param state         The new scroll state for RecyclerView\n     * @param startPosition\n     * @param endPosition\n     */\n    public void onScrollStateChanged(int state, int startPosition, int endPosition, LayoutManagerHelper helper) {\n\n    }\n\n\n    /**\n     * Offset all child views attached to the parent RecyclerView by dx pixels along\n     * the horizontal axis.\n     *\n     * @param dx Pixels to offset by\n     */\n    public void onOffsetChildrenHorizontal(int dx, LayoutManagerHelper helper) {\n\n    }\n\n    /**\n     * Offset all child views attached to the parent RecyclerView by dy pixels along\n     * the vertical axis.\n     *\n     * @param dy Pixels to offset by\n     */\n    public void onOffsetChildrenVertical(int dy, LayoutManagerHelper helper) {\n\n    }\n\n    /**\n     * Get zIndex of this {@link LayoutHelper}\n     *\n     * @return zIndex of current layoutHelper\n     */\n    public int getZIndex() {\n        return mZIndex;\n    }\n\n    /**\n     * Experimental attribute, set zIndex of this {@link LayoutHelper}，it does not mean the z-index of view. It just reorder the layoutHelpers in linear flow.\n     * Do not use it currently.\n     * @param zIndex\n     */\n    public void setZIndex(int zIndex) {\n        this.mZIndex = zIndex;\n    }\n\n    /**\n     * Get View that fixed in some position\n     *\n     * @return\n     */\n    @Nullable\n    public View getFixedView() {\n        return null;\n    }\n\n\n    @NonNull\n    protected final List<View> mOffFlowViews = new LinkedList<>();\n\n    /**\n     * Get Views that out of normal flow layout\n     *\n     * @return list of views\n     */\n    @NonNull\n    public List<View> getOffFlowViews() {\n        return mOffFlowViews;\n    }\n\n    /**\n     * Tell LayoutManager whether the child can be recycled, the recycleChild range is (startIndex, endIndex)\n     *\n     * @param childPos   recycled child index\n     * @param startIndex start index of child will be recycled\n     * @param endIndex   end index of child will be recycled\n     * @param helper     a helper of type {@link LayoutManagerHelper}\n     * @param fromStart  whether is recycleChildren from start\n     * @return whether the child in <code>childPos</code> can be recycled\n     */\n    public boolean isRecyclable(int childPos, int startIndex, int endIndex, LayoutManagerHelper helper, boolean fromStart) {\n        return true;\n    }\n\n    /**\n     * Return children count\n     *\n     * @return the number of children\n     */\n    public abstract int getItemCount();\n\n    /**\n     * Set items' count\n     *\n     * @param itemCount how many children in this layoutHelper\n     */\n    public abstract void setItemCount(int itemCount);\n\n    public abstract void doLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n                                  LayoutStateWrapper layoutState, LayoutChunkResult result,\n                                  LayoutManagerHelper helper);\n\n    public void onRefreshLayout(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n\n    }\n\n    /**\n     * Called before <code>doLayout</code>\n     *\n     * @param recycler recycler\n     * @param state    RecyclerView's State\n     * @param helper   LayoutManagerHelper to handle views\n     */\n    public abstract void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n                                      LayoutManagerHelper helper);\n\n    /**\n     * Called after <code>doLayout</code>\n     *\n     * @param recycler      recycler\n     * @param state         RecyclerView's State\n     * @param startPosition firstVisiblePosition in {@link RecyclerView}\n     * @param endPosition   lastVisiblePosition in {@link RecyclerView}\n     * @param scrolled      how many offset scrolled if layout is happened in a scrolling process\n     * @param helper        LayoutManagerHelper\n     */\n    public abstract void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n                                     int startPosition, int endPosition, int scrolled,\n                                     LayoutManagerHelper helper);\n\n    /**\n     * Run to adjust layoutHelper's background area\n     * @param startPosition\n     * @param endPosition\n     * @param helper\n     */\n    public abstract void adjustLayout(int startPosition, int endPosition, LayoutManagerHelper helper);\n\n    public void onItemsChanged(LayoutManagerHelper helper) {\n\n    }\n\n    /**\n     * Called when this layoutHelper will be removed from LayoutManager, please release views and other resources here\n     *\n     * @param helper LayoutManagerHelper\n     */\n    public abstract void clear(LayoutManagerHelper helper);\n\n    /**\n     * Whether a background layoutView is required\n     *\n     * @return true if require a layoutView\n     */\n    public abstract boolean requireLayoutView();\n\n    /**\n     * Bind properties to <code>layoutView</code>\n     *\n     * @param layoutView generated layoutView as backgroundView\n     */\n    public abstract void bindLayoutView(View layoutView);\n\n    public abstract boolean isFixLayout();\n\n    /**\n     * Get margins between layout when layout child at <code>offset</code>\n     * Or compute offset for align line during scrolling\n     *\n     * @param offset      anchor child's offset in current layoutHelper, for example, 0 means first item\n     * @param isLayoutEnd is the layout process will do to end or start, true means it will lay views from start to end\n     * @param useAnchor   whether offset is computed for scrolling or for anchor reset\n     * @param helper      view layout helper\n     * @return extra offset must be calculated in {@link VirtualLayoutManager}\n     */\n    public abstract int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor,\n        LayoutManagerHelper helper);\n\n    public abstract int computeMarginStart(int offset, boolean isLayoutEnd, boolean useAnchor,\n        LayoutManagerHelper helper);\n\n    public abstract int computeMarginEnd(int offset, boolean isLayoutEnd, boolean useAnchor,\n        LayoutManagerHelper helper);\n\n    public abstract int computePaddingStart(int offset, boolean isLayoutEnd, boolean useAnchor,\n        LayoutManagerHelper helper);\n\n    public abstract int computePaddingEnd(int offset, boolean isLayoutEnd, boolean useAnchor,\n        LayoutManagerHelper helper);\n\n    public void onSaveState(final Bundle bundle) {\n\n    }\n\n    public void onRestoreInstanceState(final Bundle bundle) {\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/LayoutHelperFinder.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\n\nimport java.util.List;\n\n/**\n * LayoutHelperFinder provides as repository of LayoutHelpers\n */\npublic abstract class LayoutHelperFinder {\n\n    /**\n     * Put layouts into the finder\n     */\n    abstract void setLayouts(@Nullable List<LayoutHelper> layouts);\n\n    /**\n     * Get layoutHelper at given position\n     *\n     * @param position\n     * @return\n     */\n    @Nullable\n    public abstract LayoutHelper getLayoutHelper(int position);\n\n    /**\n     * Get all layoutHelpers\n     *\n     * @return\n     */\n    @NonNull\n    protected abstract List<LayoutHelper> getLayoutHelpers();\n\n    /**\n     * Get layoutHelpers that in reverse order\n     *\n     * @return\n     */\n    protected abstract List<LayoutHelper> reverse();\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/LayoutManagerHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\n\n/**\n * LayoutManagerHelper that provides methods for {@link LayoutHelper}\n */\npublic interface LayoutManagerHelper {\n\n\n    /*\n     * View operation helpers\n     */\n\n    /**\n     * It may be cached and reused, it's layoutHelper's responsibility to make sure the properties are correct\n     *\n     * @return default LayoutView\n     */\n    View generateLayoutView();\n\n    /**\n     * Get current child count, without hidden and fixed children\n     *\n     * @return Total number of children in normal flow, not include fixed and hidden children\n     */\n    int getChildCount();\n\n    /**\n     * Get visible child\n     *\n     * @param index Index to get child at\n     * @return Child view, null if none.\n     */\n    @Nullable\n    View getChildAt(int index);\n\n    /**\n     * Add view to defined index\n     *\n     * @param view  View that added\n     * @param index Index to add child at\n     */\n    void addChildView(View view, int index);\n\n    /**\n     * Add view to specified index, which when animation are required\n     *\n     * @param layoutState Current layoutState to perform animation check\n     * @param view        View will be added\n     * @param index       Index to add child at\n     */\n    void addChildView(VirtualLayoutManager.LayoutStateWrapper layoutState, View view, int index);\n\n    /**\n     * Add view to head/tail according to layoutState state\n     *\n     * @param layoutState current layoutState to perform animation check\n     * @param view        View will be added\n     */\n    void addChildView(VirtualLayoutManager.LayoutStateWrapper layoutState, View view);\n\n    /**\n     * Remove view from container\n     *\n     * @param view View will be removed\n     */\n    void removeChildView(View view);\n\n    /**\n     * Tell whether the data bind to the view has updated, if true means it need rebinding\n     *\n     * @param view\n     * @return\n     */\n    boolean isViewHolderUpdated(View view);\n\n    /**\n     * Add view out of normal flow, which means it won't be ignored in getChildAt, but still be able to scrolled with content\n     * But it's can be find by position via {@link #findViewByPosition(int)}\n     *\n     * @param view View will be added\n     * @param head Whether added to the head or tail\n     */\n    void addOffFlowView(View view, boolean head);\n\n    /**\n     * Add view out of normal flow, which means it won't be ignored in getChildAt, but still be able to scrolled with content\n     * But it's can be find by position via {@link #findViewByPosition(int)}.\n     * The differece between with {@link #addOffFlowView(View, boolean)} is that this method does not hide the view, it is used to add background view with overlapping.\n     * @param view View will be added\n     * @param head Whether added to the head or tail\n     */\n    void addBackgroundView(View view, boolean head);\n\n    /**\n     * Add view to fixed layer, which overlays on the normal layer.\n     * It won't be found by getChildAt and also scrolled with content.\n     * Can only be get by position via {@link #findViewByPosition(int)}\n     *\n     * @param view Fixed view\n     */\n    void addFixedView(View view);\n\n    /**\n     * Mark a view as hidden, it will show on the screen,\n     * but can not be access via {@link RecyclerView.LayoutManager#getChildCount()} and {@link android.support.v7.widget.RecyclerView.LayoutManager#getChildAt(int)}\n     *\n     * @param view\n     */\n    void hideView(View view);\n\n    /**\n     * Mark a hidden view re-shown, so you can get it from {@link android.support.v7.widget.RecyclerView.LayoutManager#getChildAt(int)}\n     *\n     * @param view\n     */\n    void showView(View view);\n\n    /**\n     * Get {@link android.support.v7.widget.RecyclerView.ViewHolder} for a view in RecyclerView\n     *\n     * @param child\n     * @return\n     */\n    RecyclerView.ViewHolder getChildViewHolder(View child);\n\n    /**\n     * Get current container recyclerView\n     *\n     * @return\n     */\n    RecyclerView getRecyclerView();\n\n    /**\n     * Find view via item position {@param position}\n     *\n     * @param position Position of the item that view associated with\n     * @return View that found, null if not.\n     */\n    @Nullable\n    View findViewByPosition(int position);\n\n\n    /*\n     * Measure and layout helpers\n     */\n\n    /**\n     * MainOrientationHelper\n     *\n     * @return\n     */\n    OrientationHelperEx getMainOrientationHelper();\n\n    /**\n     * OrientationHelper in secondary direction\n     *\n     * @return\n     */\n    OrientationHelperEx getSecondaryOrientationHelper();\n\n    /**\n     * Measure children views with decorations, use this to measure children\n     *\n     * @param view\n     * @param widthSpec\n     * @param heightSpec\n     */\n    void measureChild(View view, int widthSpec, int heightSpec);\n\n    /**\n     * Measure children views with margins and decorations, use this to measure children\n     *\n     * @param child\n     * @param widthUsed\n     * @param heightUsed\n     */\n    void measureChildWithMargins(View child, int widthUsed, int heightUsed);\n\n\n        /**\n         * Layout children views with margins and decorations.\n         *\n         * @param view\n         * @param left\n         * @param top\n         * @param right\n         * @param bottom\n         */\n    void layoutChildWithMargins(View view, int left, int top, int right, int bottom);\n\n    /**\n     * Layout children views with decorations but without margins.\n     *\n     * @param view\n     * @param left\n     * @param top\n     * @param right\n     * @param bottom\n     */\n    void layoutChild(View view, int left, int top, int right, int bottom);\n\n    /**\n     * Quick helper to get measureSize\n     *\n     * @param parentSize\n     * @param size\n     * @param canScroll  whehter can scroll in this direction\n     * @return\n     */\n    int getChildMeasureSpec(int parentSize, int size, boolean canScroll);\n\n    /*\n     * Properties helpers\n     */\n\n    /**\n     * Find item position of a view, not the index of view in RecyclerView\n     *\n     * @param view\n     * @return\n     */\n    int getPosition(View view);\n\n    int getOrientation();\n\n    int getPaddingTop();\n\n    int getPaddingBottom();\n\n    int getPaddingRight();\n\n    int getPaddingLeft();\n\n    int getContentWidth();\n\n    int getContentHeight();\n\n    boolean isDoLayoutRTL();\n\n    boolean getReverseLayout();\n\n    /**\n     * Recycle child back to recycledPool\n     *\n     * @param child View will be recycled\n     */\n    void recycleView(View child);\n\n    /**\n     * return layout helper for specific position\n     * @param position\n     * @return\n     */\n    LayoutHelper findLayoutHelperByPosition(int position);\n\n    /**\n     * return first visibile item position in layoutManager\n     * @return\n     */\n    int findFirstVisibleItemPosition();\n\n    /**\n     *\n     * return last visibile item position in layoutManager\n     * @return\n     */\n    int findLastVisibleItemPosition();\n\n    /**\n     * @return true to make margin between items or layout helpers overlapping, in vlayout, we support both vertical and horizontal margins overlapping between the siblings\n     */\n    boolean isEnableMarginOverLap();\n\n    int getDecoratedLeft(View child);\n\n    int getDecoratedTop(View child);\n\n    int getDecoratedRight(View child);\n\n    int getDecoratedBottom(View child);\n}\n\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/LayoutView.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.View;\n\n/**\n * A flat LayoutView which used to render VirtualLayout in VirtualLayoutManager\n *\n * @since 1.0.0\n */\npublic final class LayoutView extends View {\n\n    public LayoutView(Context context) {\n        super(context);\n    }\n\n    public LayoutView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public LayoutView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    public LayoutView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/LayoutViewFactory.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.content.Context;\nimport android.support.annotation.NonNull;\nimport android.view.View;\n\n/**\n * Created by villadora on 16/1/9.\n */\npublic interface LayoutViewFactory {\n\n    View generateLayoutView(@NonNull final Context context);\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/MismatchChildCountException.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\n/**\n * Throws when a layoutHelper's range is not match its itemCount\n */\npublic class MismatchChildCountException extends RuntimeException {\n\n\n    public MismatchChildCountException(String msg) {\n        super(msg);\n    }\n\n    public MismatchChildCountException(String msg, Throwable cause) {\n        super(msg, cause);\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/OrientationHelperEx.java",
    "content": "package com.alibaba.android.vlayout;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\n/**\n * Created by longerian on 2017/6/6.\n *\n * @author longerian\n * @date 2017/06/06\n */\n\nabstract public class OrientationHelperEx {\n\n    private static final int INVALID_SIZE = Integer.MIN_VALUE;\n\n    protected final ExposeLinearLayoutManagerEx mLayoutManager;\n\n    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;\n\n    public static final int VERTICAL = LinearLayout.VERTICAL;\n\n    private int mLastTotalSpace = INVALID_SIZE;\n\n    private OrientationHelperEx(ExposeLinearLayoutManagerEx layoutManager) {\n        mLayoutManager = layoutManager;\n    }\n\n    /**\n     * Call this method after onLayout method is complete if state is NOT pre-layout.\n     * This method records information like layout bounds that might be useful in the next layout\n     * calculations.\n     */\n    public void onLayoutComplete() {\n        mLastTotalSpace = getTotalSpace();\n    }\n\n    /**\n     * Returns the layout space change between the previous layout pass and current layout pass.\n     * <p>\n     * Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's\n     * {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,\n     * RecyclerView.State)} method.\n     *\n     * @return The difference between the current total space and previous layout's total space.\n     * @see #onLayoutComplete()\n     */\n    public int getTotalSpaceChange() {\n        return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;\n    }\n\n    /**\n     * Returns the start of the view including its decoration and margin.\n     * <p>\n     * For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left\n     * decoration and 3px left margin, returned value will be 15px.\n     *\n     * @param view The view element to check\n     * @return The first pixel of the element\n     * @see #getDecoratedEnd(android.view.View)\n     */\n    public abstract int getDecoratedStart(View view);\n\n    /**\n     * Returns the end of the view including its decoration and margin.\n     * <p>\n     * For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right\n     * decoration and 3px right margin, returned value will be 205.\n     *\n     * @param view The view element to check\n     * @return The last pixel of the element\n     * @see #getDecoratedStart(android.view.View)\n     */\n    public abstract int getDecoratedEnd(View view);\n\n    /**\n     * Returns the space occupied by this View in the current orientation including decorations and\n     * margins.\n     *\n     * @param view The view element to check\n     * @return Total space occupied by this view\n     * @see #getDecoratedMeasurementInOther(View)\n     */\n    public abstract int getDecoratedMeasurement(View view);\n\n    /**\n     * Returns the space occupied by this View in the perpendicular orientation including\n     * decorations and margins.\n     *\n     * @param view The view element to check\n     * @return Total space occupied by this view in the perpendicular orientation to current one\n     * @see #getDecoratedMeasurement(View)\n     */\n    public abstract int getDecoratedMeasurementInOther(View view);\n\n    /**\n     * Returns the start position of the layout after the start padding is added.\n     *\n     * @return The very first pixel we can draw.\n     */\n    public abstract int getStartAfterPadding();\n\n    /**\n     * Returns the end position of the layout after the end padding is removed.\n     *\n     * @return The end boundary for this layout.\n     */\n    public abstract int getEndAfterPadding();\n\n    /**\n     * Returns the end position of the layout without taking padding into account.\n     *\n     * @return The end boundary for this layout without considering padding.\n     */\n    public abstract int getEnd();\n\n    /**\n     * Offsets all children's positions by the given amount.\n     *\n     * @param amount Value to add to each child's layout parameters\n     */\n    public abstract void offsetChildren(int amount);\n\n    /**\n     * Returns the total space to layout. This number is the difference between\n     * {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.\n     *\n     * @return Total space to layout children\n     */\n    public abstract int getTotalSpace();\n\n    /**\n     * Offsets the child in this orientation.\n     *\n     * @param view   View to offset\n     * @param offset offset amount\n     */\n    public abstract void offsetChild(View view, int offset);\n\n    /**\n     * Returns the padding at the end of the layout. For horizontal helper, this is the right\n     * padding and for vertical helper, this is the bottom padding. This method does not check\n     * whether the layout is RTL or not.\n     *\n     * @return The padding at the end of the layout.\n     */\n    public abstract int getEndPadding();\n\n    /**\n     * Creates an OrientationHelper for the given LayoutManager and orientation.\n     *\n     * @param layoutManager LayoutManager to attach to\n     * @param orientation   Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}\n     * @return A new OrientationHelper\n     */\n    public static OrientationHelperEx createOrientationHelper(\n        ExposeLinearLayoutManagerEx layoutManager, int orientation) {\n        switch (orientation) {\n            case HORIZONTAL:\n                return createHorizontalHelper(layoutManager);\n            case VERTICAL:\n                return createVerticalHelper(layoutManager);\n        }\n        throw new IllegalArgumentException(\"invalid orientation\");\n    }\n\n    /**\n     * Creates a horizontal OrientationHelper for the given LayoutManager.\n     *\n     * @param layoutManager The LayoutManager to attach to.\n     * @return A new OrientationHelper\n     */\n    public static OrientationHelperEx createHorizontalHelper(\n        ExposeLinearLayoutManagerEx layoutManager) {\n        return new OrientationHelperEx(layoutManager) {\n            @Override\n            public int getEndAfterPadding() {\n                return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();\n            }\n\n            @Override\n            public int getEnd() {\n                return mLayoutManager.getWidth();\n            }\n\n            @Override\n            public void offsetChildren(int amount) {\n                mLayoutManager.offsetChildrenHorizontal(amount);\n            }\n\n            @Override\n            public int getStartAfterPadding() {\n                return mLayoutManager.getPaddingLeft();\n            }\n\n            @Override\n            public int getDecoratedMeasurement(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return !mLayoutManager.isEnableMarginOverLap() ? mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin\n                    + params.rightMargin : mLayoutManager.getDecoratedMeasuredWidth(view);\n            }\n\n            @Override\n            public int getDecoratedMeasurementInOther(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin + params.bottomMargin;\n            }\n\n            @Override\n            public int getDecoratedEnd(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return !mLayoutManager.isEnableMarginOverLap() ? mLayoutManager.getDecoratedRight(view)\n                    + params.rightMargin : mLayoutManager.getDecoratedRight(view);\n            }\n\n            @Override\n            public int getDecoratedStart(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return !mLayoutManager.isEnableMarginOverLap() ? mLayoutManager.getDecoratedLeft(view)\n                    - params.leftMargin : mLayoutManager.getDecoratedLeft(view);\n            }\n\n            @Override\n            public int getTotalSpace() {\n                return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()\n                    - mLayoutManager.getPaddingRight();\n            }\n\n            @Override\n            public void offsetChild(View view, int offset) {\n                view.offsetLeftAndRight(offset);\n            }\n\n            @Override\n            public int getEndPadding() {\n                return mLayoutManager.getPaddingRight();\n            }\n        };\n    }\n\n    /**\n     * Creates a vertical OrientationHelper for the given LayoutManager.\n     *\n     * @param layoutManager The LayoutManager to attach to.\n     * @return A new OrientationHelper\n     */\n    public static OrientationHelperEx createVerticalHelper(ExposeLinearLayoutManagerEx layoutManager) {\n        return new OrientationHelperEx(layoutManager) {\n            @Override\n            public int getEndAfterPadding() {\n                return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();\n            }\n\n            @Override\n            public int getEnd() {\n                return mLayoutManager.getHeight();\n            }\n\n            @Override\n            public void offsetChildren(int amount) {\n                mLayoutManager.offsetChildrenVertical(amount);\n            }\n\n            @Override\n            public int getStartAfterPadding() {\n                return mLayoutManager.getPaddingTop();\n            }\n\n            @Override\n            public int getDecoratedMeasurement(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return !mLayoutManager.isEnableMarginOverLap() ? mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin\n                    + params.bottomMargin : mLayoutManager.getDecoratedMeasuredHeight(view);\n            }\n\n            @Override\n            public int getDecoratedMeasurementInOther(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin + params.rightMargin ;\n            }\n\n            @Override\n            public int getDecoratedEnd(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return !mLayoutManager.isEnableMarginOverLap() ? mLayoutManager.getDecoratedBottom(view)\n                    + params.bottomMargin : mLayoutManager.getDecoratedBottom(view);\n            }\n\n            @Override\n            public int getDecoratedStart(View view) {\n                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                    view.getLayoutParams();\n                return !mLayoutManager.isEnableMarginOverLap() ? mLayoutManager.getDecoratedTop(view) - params.topMargin\n                    : mLayoutManager.getDecoratedTop(view);\n            }\n\n            @Override\n            public int getTotalSpace() {\n                return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()\n                    - mLayoutManager.getPaddingBottom();\n            }\n\n            @Override\n            public void offsetChild(View view, int offset) {\n                view.offsetTopAndBottom(offset);\n            }\n\n            @Override\n            public int getEndPadding() {\n                return mLayoutManager.getPaddingBottom();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/Range.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.os.Build;\nimport android.support.annotation.NonNull;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * Range object\n */\npublic final class Range<T extends Comparable<? super T>> {\n\n    /**\n     * Create a new immutable range.\n     * <p/>\n     * <p>\n     * The endpoints are {@code [lower, upper]}; that\n     * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}\n     * to {@code upper}.\n     * </p>\n     *\n     * @param lower The lower endpoint (inclusive)\n     * @param upper The upper endpoint (inclusive)\n     * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}\n     */\n    public Range(@NonNull final T lower, @NonNull final T upper) {\n        if (lower == null)\n            throw new IllegalArgumentException(\"lower must not be null\");\n\n        if (upper == null)\n            throw new IllegalArgumentException(\"upper must not be null\");\n\n        mLower = lower;\n        mUpper = upper;\n\n        if (lower.compareTo(upper) > 0) {\n            throw new IllegalArgumentException(\"lower must be less than or equal to upper\");\n        }\n    }\n\n    /**\n     * Create a new immutable range, with the argument types inferred.\n     * <p/>\n     * <p>\n     * The endpoints are {@code [lower, upper]}; that\n     * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}\n     * to {@code upper}.\n     * </p>\n     *\n     * @param lower The lower endpoint (inclusive)\n     * @param upper The upper endpoint (inclusive)\n     * @throws NullPointerException if {@code lower} or {@code upper} is {@code null}\n     */\n    public static <T extends Comparable<? super T>> Range<T> create(final T lower, final T upper) {\n        return new Range<T>(lower, upper);\n    }\n\n    /**\n     * Get the lower endpoint.\n     *\n     * @return a non-{@code null} {@code T} reference\n     */\n    public T getLower() {\n        return mLower;\n    }\n\n    /**\n     * Get the upper endpoint.\n     *\n     * @return a non-{@code null} {@code T} reference\n     */\n    public T getUpper() {\n        return mUpper;\n    }\n\n    /**\n     * Checks if the {@code value} is within the bounds of this range.\n     * <p/>\n     * <p>A value is considered to be within this range if it's {@code >=}\n     * the lower endpoint <i>and</i> {@code <=} the upper endpoint (using the {@link Comparable}\n     * interface.)</p>\n     *\n     * @param value a non-{@code null} {@code T} reference\n     * @return {@code true} if the value is within this inclusive range, {@code false} otherwise\n     * @throws NullPointerException if {@code value} was {@code null}\n     */\n    public boolean contains(@NonNull T value) {\n        if (value == null)\n            throw new IllegalArgumentException(\"value must not be null\");\n\n        boolean gteLower = value.compareTo(mLower) >= 0;\n        boolean lteUpper = value.compareTo(mUpper) <= 0;\n\n        return gteLower && lteUpper;\n    }\n\n    /**\n     * Checks if another {@code range} is within the bounds of this range.\n     * <p/>\n     * <p>A range is considered to be within this range if both of its endpoints\n     * are within this range.</p>\n     *\n     * @param range a non-{@code null} {@code T} reference\n     * @return {@code true} if the range is within this inclusive range, {@code false} otherwise\n     * @throws NullPointerException if {@code range} was {@code null}\n     */\n    public boolean contains(@NonNull Range<T> range) {\n        if (range == null)\n            throw new IllegalArgumentException(\"value must not be null\");\n\n        boolean gteLower = range.mLower.compareTo(mLower) >= 0;\n        boolean lteUpper = range.mUpper.compareTo(mUpper) <= 0;\n\n        return gteLower && lteUpper;\n    }\n\n    /**\n     * Compare two ranges for equality.\n     * <p/>\n     * <p>A range is considered equal if and only if both the lower and upper endpoints\n     * are also equal.</p>\n     *\n     * @return {@code true} if the ranges are equal, {@code false} otherwise\n     */\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == null) {\n            return false;\n        } else if (this == obj) {\n            return true;\n        } else if (obj instanceof Range) {\n            @SuppressWarnings(\"rawtypes\")\n            Range other = (Range) obj;\n            return mLower.equals(other.mLower) && mUpper.equals(other.mUpper);\n        }\n        return false;\n    }\n\n    /**\n     * Clamps {@code value} to this range.\n     * <p/>\n     * <p>If the value is within this range, it is returned.  Otherwise, if it\n     * is {@code <} than the lower endpoint, the lower endpoint is returned,\n     * else the upper endpoint is returned. Comparisons are performed using the\n     * {@link Comparable} interface.</p>\n     *\n     * @param value a non-{@code null} {@code T} reference\n     * @return {@code value} clamped to this range.\n     */\n    public T clamp(T value) {\n        if (value == null)\n            throw new IllegalArgumentException(\"value must not be null\");\n\n        if (value.compareTo(mLower) < 0) {\n            return mLower;\n        } else if (value.compareTo(mUpper) > 0) {\n            return mUpper;\n        } else {\n            return value;\n        }\n    }\n\n    /**\n     * Returns the intersection of this range and another {@code range}.\n     * <p>\n     * E.g. if a {@code <} b {@code <} c {@code <} d, the\n     * intersection of [a, c] and [b, d] ranges is [b, c].\n     * As the endpoints are object references, there is no guarantee\n     * which specific endpoint reference is used from the input ranges:</p>\n     * <p>\n     * E.g. if a {@code ==} a' {@code <} b {@code <} c, the\n     * intersection of [a, b] and [a', c] ranges could be either\n     * [a, b] or ['a, b], where [a, b] could be either the exact\n     * input range, or a newly created range with the same endpoints.</p>\n     *\n     * @param range a non-{@code null} {@code Range<T>} reference\n     * @return the intersection of this range and the other range.\n     * @throws NullPointerException     if {@code range} was {@code null}\n     * @throws IllegalArgumentException if the ranges are disjoint.\n     */\n    public Range<T> intersect(Range<T> range) {\n        if (range == null)\n            throw new IllegalArgumentException(\"range must not be null\");\n\n        int cmpLower = range.mLower.compareTo(mLower);\n        int cmpUpper = range.mUpper.compareTo(mUpper);\n\n        if (cmpLower <= 0 && cmpUpper >= 0) {\n            // range includes this\n            return this;\n        } else if (cmpLower >= 0 && cmpUpper <= 0) {\n            // this inludes range\n            return range;\n        } else {\n            return Range.create(\n                    cmpLower <= 0 ? mLower : range.mLower,\n                    cmpUpper >= 0 ? mUpper : range.mUpper);\n        }\n    }\n\n    /**\n     * Returns the intersection of this range and the inclusive range\n     * specified by {@code [lower, upper]}.\n     * <p>\n     * See {@link #intersect(Range)} for more details.</p>\n     *\n     * @param lower a non-{@code null} {@code T} reference\n     * @param upper a non-{@code null} {@code T} reference\n     * @return the intersection of this range and the other range\n     * @throws NullPointerException     if {@code lower} or {@code upper} was {@code null}\n     * @throws IllegalArgumentException if the ranges are disjoint.\n     */\n    public Range<T> intersect(T lower, T upper) {\n        if (lower == null)\n            throw new IllegalArgumentException(\"lower must not be null\");\n\n        if (upper == null)\n            throw new IllegalArgumentException(\"upper must not be null\");\n\n        int cmpLower = lower.compareTo(mLower);\n        int cmpUpper = upper.compareTo(mUpper);\n\n        if (cmpLower <= 0 && cmpUpper >= 0) {\n            // [lower, upper] includes this\n            return this;\n        } else {\n            return Range.create(\n                    cmpLower <= 0 ? mLower : lower,\n                    cmpUpper >= 0 ? mUpper : upper);\n        }\n    }\n\n    /**\n     * Returns the smallest range that includes this range and\n     * another {@code range}.\n     * <p>\n     * E.g. if a {@code <} b {@code <} c {@code <} d, the\n     * extension of [a, c] and [b, d] ranges is [a, d].\n     * As the endpoints are object references, there is no guarantee\n     * which specific endpoint reference is used from the input ranges:</p>\n     * <p>\n     * E.g. if a {@code ==} a' {@code <} b {@code <} c, the\n     * extension of [a, b] and [a', c] ranges could be either\n     * [a, c] or ['a, c], where ['a, c] could be either the exact\n     * input range, or a newly created range with the same endpoints.</p>\n     *\n     * @param range a non-{@code null} {@code Range<T>} reference\n     * @return the extension of this range and the other range.\n     * @throws NullPointerException if {@code range} was {@code null}\n     */\n    public Range<T> extend(Range<T> range) {\n        if (range == null)\n            throw new IllegalArgumentException(\"range must not be null\");\n\n        int cmpLower = range.mLower.compareTo(mLower);\n        int cmpUpper = range.mUpper.compareTo(mUpper);\n\n        if (cmpLower <= 0 && cmpUpper >= 0) {\n            // other includes this\n            return range;\n        } else if (cmpLower >= 0 && cmpUpper <= 0) {\n            // this inludes other\n            return this;\n        } else {\n            return Range.create(\n                    cmpLower >= 0 ? mLower : range.mLower,\n                    cmpUpper <= 0 ? mUpper : range.mUpper);\n        }\n    }\n\n    /**\n     * Returns the smallest range that includes this range and\n     * the inclusive range specified by {@code [lower, upper]}.\n     * <p>\n     * See {@link #extend(Range)} for more details.</p>\n     *\n     * @param lower a non-{@code null} {@code T} reference\n     * @param upper a non-{@code null} {@code T} reference\n     * @return the extension of this range and the other range.\n     * @throws NullPointerException if {@code lower} or {@code\n     *                              upper} was {@code null}\n     */\n    public Range<T> extend(T lower, T upper) {\n        if (lower == null)\n            throw new IllegalArgumentException(\"lower must not be null\");\n\n        if (upper == null)\n            throw new IllegalArgumentException(\"upper must not be null\");\n\n        int cmpLower = lower.compareTo(mLower);\n        int cmpUpper = upper.compareTo(mUpper);\n\n        if (cmpLower >= 0 && cmpUpper <= 0) {\n            // this inludes other\n            return this;\n        } else {\n            return Range.create(\n                    cmpLower >= 0 ? mLower : lower,\n                    cmpUpper <= 0 ? mUpper : upper);\n        }\n    }\n\n    /**\n     * Returns the smallest range that includes this range and\n     * the {@code value}.\n     * <p>\n     * See {@link #extend(Range)} for more details, as this method is\n     * equivalent to {@code extend(Range.create(value, value))}.</p>\n     *\n     * @param value a non-{@code null} {@code T} reference\n     * @return the extension of this range and the value.\n     * @throws NullPointerException if {@code value} was {@code null}\n     */\n    public Range<T> extend(T value) {\n        if (value == null)\n            throw new IllegalArgumentException(\"value must not be null\");\n        return extend(value, value);\n    }\n\n    /**\n     * Return the range as a string representation {@code \"[lower, upper]\"}.\n     *\n     * @return string representation of the range\n     */\n    @Override\n    public String toString() {\n        return String.format(\"[%s, %s]\", mLower, mUpper);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public int hashCode() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            return Objects.hash(mLower, mUpper);\n        }\n\n        return Arrays.hashCode(new Object[]{mLower, mUpper});\n    }\n\n    private final T mLower;\n    private final T mUpper;\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/RangeLayoutHelperFinder.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.ListIterator;\n\n/**\n * An implement of {@link LayoutHelperFinder} which finds layoutHelpers by position\n */\npublic class RangeLayoutHelperFinder extends LayoutHelperFinder {\n\n    @NonNull\n    private List<LayoutHelperItem> mLayoutHelperItems = new LinkedList<>();\n\n    @NonNull\n    private List<LayoutHelper> mLayoutHelpers = new LinkedList<>();\n\n    @NonNull\n    private List<LayoutHelper> mReverseLayoutHelpers =new LinkedList<>();\n\n    private LayoutHelperItem[] mSortedLayoutHelpers = null;\n\n    @NonNull\n    private Comparator<LayoutHelperItem> mLayoutHelperItemComparator = new Comparator<LayoutHelperItem>() {\n        @Override\n        public int compare(LayoutHelperItem lhs, LayoutHelperItem rhs) {\n            return lhs.getStartPosition() - rhs.getStartPosition();\n        }\n    };\n\n    @Override\n    protected List<LayoutHelper> reverse() {\n        return mReverseLayoutHelpers;\n    }\n\n    /**\n     * @param layouts layoutHelpers that handled\n     */\n    @Override\n    public void setLayouts(@Nullable List<LayoutHelper> layouts) {\n        mLayoutHelpers.clear();\n        mReverseLayoutHelpers.clear();\n        mLayoutHelperItems.clear();\n        if (layouts != null) {\n            ListIterator<LayoutHelper> iterator = layouts.listIterator();\n            LayoutHelper helper = null;\n            while (iterator.hasNext()) {\n                helper = iterator.next();\n                mLayoutHelpers.add(helper);\n                mLayoutHelperItems.add(new LayoutHelperItem(helper));\n            }\n\n            while (iterator.hasPrevious()) {\n                mReverseLayoutHelpers.add(iterator.previous());\n            }\n\n            // Collections.sort(mLayoutHelperItems, mLayoutHelperItemComparator);\n            mSortedLayoutHelpers = mLayoutHelperItems.toArray(new LayoutHelperItem[mLayoutHelperItems.size()]);\n            Arrays.sort(mSortedLayoutHelpers, mLayoutHelperItemComparator);\n        }\n    }\n\n    @NonNull\n    @Override\n    protected List<LayoutHelper> getLayoutHelpers() {\n        return mLayoutHelpers;\n    }\n\n    @Nullable\n    @Override\n    public LayoutHelper getLayoutHelper(int position) {\n        if (mSortedLayoutHelpers == null || mSortedLayoutHelpers.length == 0) {\n            return null;\n        }\n        final int count = mSortedLayoutHelpers.length;\n\n        int s = 0, e = count - 1, m;\n        LayoutHelperItem rs = null;\n        // binary search range\n        while (s <= e) {\n            m = (s + e) / 2;\n            rs = mSortedLayoutHelpers[m];\n            if (rs.getStartPosition() > position) {\n                e = m - 1;\n            } else if (rs.getEndPosition() < position) {\n                s = m + 1;\n            } else if (rs.getStartPosition() <= position && rs.getEndPosition() >= position) {\n                break;\n            }\n\n            rs = null;\n        }\n\n        return rs == null ? null : rs.layoutHelper;\n    }\n\n    static class LayoutHelperItem {\n\n        LayoutHelperItem(LayoutHelper helper) {\n            this.layoutHelper = helper;\n        }\n\n        LayoutHelper layoutHelper;\n\n        public int getStartPosition() {\n            return layoutHelper.getRange().getLower();\n        }\n\n        public int getEndPosition() {\n            return layoutHelper.getRange().getUpper();\n        }\n\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/RecyclablePagerAdapter.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.alibaba.android.vlayout.extend.InnerRecycledViewPool;\n\n/**\n * PagerAdapter which use RecycledPool, used for nested ViewPager.\n */\npublic abstract class RecyclablePagerAdapter<VH extends RecyclerView.ViewHolder> extends PagerAdapter {\n\n    private RecyclerView.Adapter<VH> mAdapter;\n\n    private InnerRecycledViewPool mRecycledViewPool;\n\n\n    public RecyclablePagerAdapter(RecyclerView.Adapter<VH> adapter, RecyclerView.RecycledViewPool pool) {\n        this.mAdapter = adapter;\n        if (pool instanceof InnerRecycledViewPool) {\n            this.mRecycledViewPool = (InnerRecycledViewPool) pool;\n        } else {\n            this.mRecycledViewPool = new InnerRecycledViewPool(pool);\n        }\n    }\n\n    @Override\n    public abstract int getCount();\n\n    @Override\n    public boolean isViewFromObject(View view, Object o) {\n        return o instanceof RecyclerView.ViewHolder && (((RecyclerView.ViewHolder) o).itemView == view);\n    }\n\n    /**\n     * Get view from position\n     *\n     * @param container\n     * @param position\n     * @return\n     */\n    @Override\n    public Object instantiateItem(ViewGroup container, int position) {\n        int itemViewType = getItemViewType(position);\n        RecyclerView.ViewHolder holder = mRecycledViewPool.getRecycledView(itemViewType);\n\n        if (holder == null) {\n            holder = mAdapter.createViewHolder(container, itemViewType);\n        }\n\n\n        onBindViewHolder((VH) holder, position);\n        //itemViews' layoutParam will be reused when there are more than one nested ViewPager in one page,\n        //so the attributes of layoutParam such as widthFactor and position will also be reused,\n        //while these attributes should be reset to default value during reused.\n        //Considering ViewPager.LayoutParams has a few inner attributes which could not be modify outside, we provide a new instance here\n\n        ViewPager.LayoutParams layoutParams = new ViewPager.LayoutParams();\n        if (holder.itemView.getLayoutParams() != null) {\n            layoutParams.width = holder.itemView.getLayoutParams().width;\n            layoutParams.height = holder.itemView.getLayoutParams().height;\n        }\n\n        container.addView(holder.itemView, layoutParams);\n\n        return holder;\n    }\n\n    @Override\n    public void destroyItem(ViewGroup container, int position, Object object) {\n        if (object instanceof RecyclerView.ViewHolder) {\n            RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) object;\n            container.removeView(holder.itemView);\n            mRecycledViewPool.putRecycledView(holder);\n        }\n    }\n\n\n    public abstract void onBindViewHolder(VH viewHolder, int position);\n\n    public abstract int getItemViewType(int position);\n}\n\n\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/SortedList.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport java.lang.reflect.Array;\n\n\n/**\n * A Sorted list implementation that can keep items in order and also notify for changes in the\n * list\n * such that it can be bound to a {@link android.support.v7.widget.RecyclerView.Adapter\n * RecyclerView.Adapter}.\n * <p>\n * It keeps items ordered using the {@link Callback#compare(Object, Object)} method and uses\n * binary search to retrieve items. If the sorting criteria of your items may change, make sure you\n * call appropriate methods while editing them to avoid data inconsistencies.\n * <p>\n * You can control the order of items and change notifications via the {@link Callback} parameter.\n */\n@SuppressWarnings(\"unchecked\")\npublic class SortedList<T> {\n\n    /**\n     * Used by {@link #indexOf(Object)} when he item cannot be found in the list.\n     */\n    public static final int INVALID_POSITION = -1;\n\n    private static final int MIN_CAPACITY = 10;\n    private static final int CAPACITY_GROWTH = MIN_CAPACITY;\n    private static final int INSERTION = 1;\n    private static final int DELETION = 1 << 1;\n    private static final int LOOKUP = 1 << 2;\n    T[] mData;\n\n    /**\n     * The callback instance that controls the behavior of the SortedList and get notified when\n     * changes happen.\n     */\n    private Callback mCallback;\n\n    private BatchedCallback mBatchedCallback;\n\n    private int mSize;\n    private final Class<T> mTClass;\n\n    /**\n     * Creates a new SortedList of type T.\n     *\n     * @param klass    The class of the contents of the SortedList.\n     * @param callback The callback that controls the behavior of SortedList.\n     */\n    public SortedList(Class<T> klass, Callback<T> callback) {\n        this(klass, callback, MIN_CAPACITY);\n    }\n\n    /**\n     * Creates a new SortedList of type T.\n     *\n     * @param klass           The class of the contents of the SortedList.\n     * @param callback        The callback that controls the behavior of SortedList.\n     * @param initialCapacity The initial capacity to hold items.\n     */\n    public SortedList(Class<T> klass, Callback<T> callback, int initialCapacity) {\n        mTClass = klass;\n        mData = (T[]) Array.newInstance(klass, initialCapacity);\n        mCallback = callback;\n        mSize = 0;\n    }\n\n    /**\n     * The number of items in the list.\n     *\n     * @return The number of items in the list.\n     */\n    public int size() {\n        return mSize;\n    }\n\n    /**\n     * Adds the given item to the list. If this is a new item, SortedList calls\n     * {@link Callback#onInserted(int, int)}.\n     * <p>\n     * If the item already exists in the list and its sorting criteria is not changed, it is\n     * replaced with the existing Item. SortedList uses\n     * {@link Callback#areItemsTheSame(Object, Object)} to check if two items are the same item\n     * and uses {@link Callback#areContentsTheSame(Object, Object)} to decide whether it should\n     * call {@link Callback#onChanged(int, int)} or not. In both cases, it always removes the\n     * reference to the old item and puts the new item into the backing array even if\n     * {@link Callback#areContentsTheSame(Object, Object)} returns false.\n     * <p>\n     * If the sorting criteria of the item is changed, SortedList won't be able to find\n     * its duplicate in the list which will result in having a duplicate of the Item in the list.\n     * If you need to update sorting criteria of an item that already exists in the list,\n     * use {@link #updateItemAt(int, Object)}. You can find the index of the item using\n     * {@link #indexOf(Object)} before you update the object.\n     *\n     * @param item The item to be added into the list.\n     * @return The index of the newly added item.\n     * @see {@link Callback#compare(Object, Object)}\n     * @see {@link Callback#areItemsTheSame(Object, Object)}\n     * @see {@link Callback#areContentsTheSame(Object, Object)}}\n     */\n    public int add(T item) {\n        return add(item, true);\n    }\n\n    /**\n     * Batches adapter updates that happen between calling this method until calling\n     * {@link #endBatchedUpdates()}. For example, if you add multiple items in a loop\n     * and they are placed into consecutive indices, SortedList calls\n     * {@link Callback#onInserted(int, int)} only once with the proper item count. If an event\n     * cannot be merged with the previous event, the previous event is dispatched\n     * to the callback instantly.\n     * <p>\n     * After running your data updates, you <b>must</b> call {@link #endBatchedUpdates()}\n     * which will dispatch any deferred data change event to the current callback.\n     * <p>\n     * A sample implementation may look like this:\n     * <pre>\n     *     mSortedList.beginBatchedUpdates();\n     *     try {\n     *         mSortedList.add(item1)\n     *         mSortedList.add(item2)\n     *         mSortedList.remove(item3)\n     *         ...\n     *     } finally {\n     *         mSortedList.endBatchedUpdates();\n     *     }\n     * </pre>\n     * <p>\n     * Instead of using this method to batch calls, you can use a Callback that extends\n     * {@link BatchedCallback}. In that case, you must make sure that you are manually calling\n     * {@link BatchedCallback#dispatchLastEvent()} right after you complete your data changes.\n     * Failing to do so may create data inconsistencies with the Callback.\n     * <p>\n     * If the current Callback in an instance of {@link BatchedCallback}, calling this method\n     * has no effect.\n     */\n    public void beginBatchedUpdates() {\n        if (mCallback instanceof BatchedCallback) {\n            return;\n        }\n        if (mBatchedCallback == null) {\n            mBatchedCallback = new BatchedCallback(mCallback);\n        }\n        mCallback = mBatchedCallback;\n    }\n\n    /**\n     * Ends the update transaction and dispatches any remaining event to the callback.\n     */\n    public void endBatchedUpdates() {\n        if (mCallback instanceof BatchedCallback) {\n            ((BatchedCallback) mCallback).dispatchLastEvent();\n        }\n        if (mCallback == mBatchedCallback) {\n            mCallback = mBatchedCallback.mWrappedCallback;\n        }\n    }\n\n    private int add(T item, boolean notify) {\n        int index = findIndexOf(item, INSERTION);\n        if (index == INVALID_POSITION) {\n            index = 0;\n        } else if (index < mSize) {\n            T existing = mData[index];\n            if (mCallback.areItemsTheSame(existing, item)) {\n                if (mCallback.areContentsTheSame(existing, item)) {\n                    //no change but still replace the item\n                    mData[index] = item;\n                    return index;\n                } else {\n                    mData[index] = item;\n                    mCallback.onChanged(index, 1);\n                    return index;\n                }\n            }\n        }\n        addToData(index, item);\n        if (notify) {\n            mCallback.onInserted(index, 1);\n        }\n        return index;\n    }\n\n    /**\n     * Removes the provided item from the list and calls {@link Callback#onRemoved(int, int)}.\n     *\n     * @param item The item to be removed from the list.\n     * @return True if item is removed, false if item cannot be found in the list.\n     */\n    public boolean remove(T item) {\n        return remove(item, true);\n    }\n\n    /**\n     * Removes the item at the given index and calls {@link Callback#onRemoved(int, int)}.\n     *\n     * @param index The index of the item to be removed.\n     * @return The removed item.\n     */\n    public T removeItemAt(int index) {\n        T item = get(index);\n        removeItemAtIndex(index, true);\n        return item;\n    }\n\n    private boolean remove(T item, boolean notify) {\n        int index = findIndexOf(item, DELETION);\n        if (index == INVALID_POSITION) {\n            return false;\n        }\n        removeItemAtIndex(index, notify);\n        return true;\n    }\n\n    private void removeItemAtIndex(int index, boolean notify) {\n        System.arraycopy(mData, index + 1, mData, index, mSize - index - 1);\n        mSize--;\n        mData[mSize] = null;\n        if (notify) {\n            mCallback.onRemoved(index, 1);\n        }\n    }\n\n    /**\n     * Updates the item at the given index and calls {@link Callback#onChanged(int, int)} and/or\n     * {@link Callback#onMoved(int, int)} if necessary.\n     * <p>\n     * You can use this method if you need to change an existing Item such that its position in the\n     * list may change.\n     * <p>\n     * If the new object is a different object (<code>get(index) != item</code>) and\n     * {@link Callback#areContentsTheSame(Object, Object)} returns <code>true</code>, SortedList\n     * avoids calling {@link Callback#onChanged(int, int)} otherwise it calls\n     * {@link Callback#onChanged(int, int)}.\n     * <p>\n     * If the new position of the item is different than the provided <code>index</code>,\n     * SortedList\n     * calls {@link Callback#onMoved(int, int)}.\n     *\n     * @param index The index of the item to replace\n     * @param item  The item to replace the item at the given Index.\n     * @see #add(Object)\n     */\n    public void updateItemAt(int index, T item) {\n        final T existing = get(index);\n        // assume changed if the same object is given back\n        boolean contentsChanged = existing == item || !mCallback.areContentsTheSame(existing, item);\n        if (existing != item) {\n            // different items, we can use comparison and may avoid lookup\n            final int cmp = mCallback.compare(existing, item);\n            if (cmp == 0) {\n                mData[index] = item;\n                if (contentsChanged) {\n                    mCallback.onChanged(index, 1);\n                }\n                return;\n            }\n        }\n        if (contentsChanged) {\n            mCallback.onChanged(index, 1);\n        }\n        // TODO this done in 1 pass to avoid shifting twice.\n        removeItemAtIndex(index, false);\n        int newIndex = add(item, false);\n        if (index != newIndex) {\n            mCallback.onMoved(index, newIndex);\n        }\n    }\n\n    /**\n     * This method can be used to recalculate the position of the item at the given index, without\n     * triggering an {@link Callback#onChanged(int, int)} callback.\n     * <p>\n     * If you are editing objects in the list such that their position in the list may change but\n     * you don't want to trigger an onChange animation, you can use this method to re-position it.\n     * If the item changes position, SortedList will call {@link Callback#onMoved(int, int)}\n     * without\n     * calling {@link Callback#onChanged(int, int)}.\n     * <p>\n     * A sample usage may look like:\n     *\n     * <pre>\n     *     final int position = mSortedList.indexOf(item);\n     *     item.incrementPriority(); // assume items are sorted by priority\n     *     mSortedList.recalculatePositionOfItemAt(position);\n     * </pre>\n     * In the example above, because the sorting criteria of the item has been changed,\n     * mSortedList.indexOf(item) will not be able to find the item. This is why the code above\n     * first\n     * gets the position before editing the item, edits it and informs the SortedList that item\n     * should be repositioned.\n     *\n     * @param index The current index of the Item whose position should be re-calculated.\n     * @see #updateItemAt(int, Object)\n     * @see #add(Object)\n     */\n    public void recalculatePositionOfItemAt(int index) {\n        // TODO can be improved\n        final T item = get(index);\n        removeItemAtIndex(index, false);\n        int newIndex = add(item, false);\n        if (index != newIndex) {\n            mCallback.onMoved(index, newIndex);\n        }\n    }\n\n    /**\n     * Returns the item at the given index.\n     *\n     * @param index The index of the item to retrieve.\n     * @return The item at the given index.\n     * @throws java.lang.IndexOutOfBoundsException if provided index is negative or larger than the\n     *                                             size of the list.\n     */\n    public T get(int index) throws IndexOutOfBoundsException {\n        if (index >= mSize || index < 0) {\n            throw new IndexOutOfBoundsException(\"Asked to get item at \" + index + \" but size is \"\n                    + mSize);\n        }\n        return mData[index];\n    }\n\n    /**\n     * Returns the position of the provided item.\n     *\n     * @param item The item to query for position.\n     * @return The position of the provided item or {@link #INVALID_POSITION} if item is not in the\n     * list.\n     */\n    public int indexOf(T item) {\n        return findIndexOf(item, LOOKUP);\n    }\n\n    private int findIndexOf(T item, int reason) {\n        int left = 0;\n        int right = mSize;\n        while (left < right) {\n            final int middle = (left + right) / 2;\n            T myItem = mData[middle];\n            final int cmp = mCallback.compare(myItem, item);\n            if (cmp < 0) {\n                left = middle + 1;\n            } else if (cmp == 0) {\n                if (mCallback.areItemsTheSame(myItem, item)) {\n                    return middle;\n                } else {\n                    int exact = linearEqualitySearch(item, middle, left, right);\n                    if (reason == INSERTION) {\n                        return exact == INVALID_POSITION ? middle : exact;\n                    } else {\n                        return exact;\n                    }\n                }\n            } else {\n                right = middle;\n            }\n        }\n        return reason == INSERTION ? left : INVALID_POSITION;\n    }\n\n    private int linearEqualitySearch(T item, int middle, int left, int right) {\n        // go left\n        for (int next = middle - 1; next >= left; next--) {\n            T nextItem = mData[next];\n            int cmp = mCallback.compare(nextItem, item);\n            if (cmp != 0) {\n                break;\n            }\n            if (mCallback.areItemsTheSame(nextItem, item)) {\n                return next;\n            }\n        }\n        for (int next = middle + 1; next < right; next++) {\n            T nextItem = mData[next];\n            int cmp = mCallback.compare(nextItem, item);\n            if (cmp != 0) {\n                break;\n            }\n            if (mCallback.areItemsTheSame(nextItem, item)) {\n                return next;\n            }\n        }\n        return INVALID_POSITION;\n    }\n\n    private void addToData(int index, T item) {\n        if (index > mSize) {\n            throw new IndexOutOfBoundsException(\n                    \"cannot add item to \" + index + \" because size is \" + mSize);\n        }\n        if (mSize == mData.length) {\n            // we are at the limit enlarge\n            T[] newData = (T[]) Array.newInstance(mTClass, mData.length + CAPACITY_GROWTH);\n            System.arraycopy(mData, 0, newData, 0, index);\n            newData[index] = item;\n            System.arraycopy(mData, index, newData, index + 1, mSize - index);\n            mData = newData;\n        } else {\n            // just shift, we fit\n            System.arraycopy(mData, index, mData, index + 1, mSize - index);\n            mData[index] = item;\n        }\n        mSize++;\n    }\n\n    /**\n     * The class that controls the behavior of the {@link SortedList}.\n     * <p>\n     * It defines how items should be sorted and how duplicates should be handled.\n     * <p>\n     * SortedList calls the callback methods on this class to notify changes about the underlying\n     * data.\n     */\n    public static abstract class Callback<T2> {\n\n        /**\n         * Similar to {@link java.util.Comparator#compare(Object, Object)}, should compare two and\n         * return how they should be ordered.\n         *\n         * @param o1 The first object to compare.\n         * @param o2 The second object to compare.\n         * @return a negative integer, zero, or a positive integer as the\n         * first argument is less than, equal to, or greater than the\n         * second.\n         */\n        abstract public int compare(T2 o1, T2 o2);\n\n        /**\n         * Called by the SortedList when an item is inserted at the given position.\n         *\n         * @param position The position of the new item.\n         * @param count    The number of items that have been added.\n         */\n        abstract public void onInserted(int position, int count);\n\n        /**\n         * Called by the SortedList when an item is removed from the given position.\n         *\n         * @param position The position of the item which has been removed.\n         * @param count    The number of items which have been removed.\n         */\n        abstract public void onRemoved(int position, int count);\n\n        /**\n         * Called by the SortedList when an item changes its position in the list.\n         *\n         * @param fromPosition The previous position of the item before the move.\n         * @param toPosition   The new position of the item.\n         */\n        abstract public void onMoved(int fromPosition, int toPosition);\n\n        /**\n         * Called by the SortedList when the item at the given position is updated.\n         *\n         * @param position The position of the item which has been updated.\n         * @param count    The number of items which has changed.\n         */\n        abstract public void onChanged(int position, int count);\n\n        /**\n         * Called by the SortedList when it wants to check whether two items have the same data\n         * or not. SortedList uses this information to decide whether it should call\n         * {@link #onChanged(int, int)} or not.\n         * <p>\n         * SortedList uses this method to check equality instead of {@link Object#equals(Object)}\n         * so\n         * that you can change its behavior depending on your UI.\n         * <p>\n         * For example, if you are using SortedList with a {@link android.support.v7.widget.RecyclerView.Adapter\n         * RecyclerView.Adapter}, you should\n         * return whether the items' visual representations are the same or not.\n         *\n         * @param oldItem The previous representation of the object.\n         * @param newItem The new object that replaces the previous one.\n         * @return True if the contents of the items are the same or false if they are different.\n         */\n        abstract public boolean areContentsTheSame(T2 oldItem, T2 newItem);\n\n        /**\n         * Called by the SortedList to decide whether two object represent the same Item or not.\n         * <p>\n         * For example, if your items have unique ids, this method should check their equality.\n         *\n         * @param item1 The first item to check.\n         * @param item2 The second item to check.\n         * @return True if the two items represent the same object or false if they are different.\n         */\n        abstract public boolean areItemsTheSame(T2 item1, T2 item2);\n    }\n\n    /**\n     * A callback implementation that can batch notify events dispatched by the SortedList.\n     * <p>\n     * This class can be useful if you want to do multiple operations on a SortedList but don't\n     * want to dispatch each event one by one, which may result in a performance issue.\n     * <p>\n     * For example, if you are going to add multiple items to a SortedList, BatchedCallback call\n     * convert individual <code>onInserted(index, 1)</code> calls into one\n     * <code>onInserted(index, N)</code> if items are added into consecutive indices. This change\n     * can help RecyclerView resolve changes much more easily.\n     * <p>\n     * If consecutive changes in the SortedList are not suitable for batching, BatchingCallback\n     * dispatches them as soon as such case is detected. After your edits on the SortedList is\n     * complete, you <b>must</b> always call {@link BatchedCallback#dispatchLastEvent()} to flush\n     * all changes to the Callback.\n     */\n    public static class BatchedCallback<T2> extends Callback<T2> {\n\n        private final Callback<T2> mWrappedCallback;\n        static final int TYPE_NONE = 0;\n        static final int TYPE_ADD = 1;\n        static final int TYPE_REMOVE = 2;\n        static final int TYPE_CHANGE = 3;\n        static final int TYPE_MOVE = 4;\n\n        int mLastEventType = TYPE_NONE;\n        int mLastEventPosition = -1;\n        int mLastEventCount = -1;\n\n        /**\n         * Creates a new BatchedCallback that wraps the provided Callback.\n         *\n         * @param wrappedCallback The Callback which should received the data change callbacks.\n         *                        Other method calls (e.g. {@link #compare(Object, Object)} from\n         *                        the SortedList are directly forwarded to this Callback.\n         */\n        public BatchedCallback(Callback<T2> wrappedCallback) {\n            mWrappedCallback = wrappedCallback;\n        }\n\n        @Override\n        public int compare(T2 o1, T2 o2) {\n            return mWrappedCallback.compare(o1, o2);\n        }\n\n        @Override\n        public void onInserted(int position, int count) {\n            if (mLastEventType == TYPE_ADD && position >= mLastEventPosition\n                    && position <= mLastEventPosition + mLastEventCount) {\n                mLastEventCount += count;\n                mLastEventPosition = Math.min(position, mLastEventPosition);\n                return;\n            }\n            dispatchLastEvent();\n            mLastEventPosition = position;\n            mLastEventCount = count;\n            mLastEventType = TYPE_ADD;\n        }\n\n        @Override\n        public void onRemoved(int position, int count) {\n            if (mLastEventType == TYPE_REMOVE && mLastEventPosition == position) {\n                mLastEventCount += count;\n                return;\n            }\n            dispatchLastEvent();\n            mLastEventPosition = position;\n            mLastEventCount = count;\n            mLastEventType = TYPE_REMOVE;\n        }\n\n        @Override\n        public void onMoved(int fromPosition, int toPosition) {\n            dispatchLastEvent();//moves are not merged\n            mWrappedCallback.onMoved(fromPosition, toPosition);\n        }\n\n        @Override\n        public void onChanged(int position, int count) {\n            if (mLastEventType == TYPE_CHANGE &&\n                    !(position > mLastEventPosition + mLastEventCount\n                            || position + count < mLastEventPosition)) {\n                // take potential overlap into account\n                int previousEnd = mLastEventPosition + mLastEventCount;\n                mLastEventPosition = Math.min(position, mLastEventPosition);\n                mLastEventCount = Math.max(previousEnd, position + count) - mLastEventPosition;\n                return;\n            }\n            dispatchLastEvent();\n            mLastEventPosition = position;\n            mLastEventCount = count;\n            mLastEventType = TYPE_CHANGE;\n        }\n\n        @Override\n        public boolean areContentsTheSame(T2 oldItem, T2 newItem) {\n            return mWrappedCallback.areContentsTheSame(oldItem, newItem);\n        }\n\n        @Override\n        public boolean areItemsTheSame(T2 item1, T2 item2) {\n            return mWrappedCallback.areItemsTheSame(item1, item2);\n        }\n\n\n        /**\n         * This method dispatches any pending event notifications to the wrapped Callback.\n         * You <b>must</b> always call this method after you are done with editing the SortedList.\n         */\n        public void dispatchLastEvent() {\n            if (mLastEventType == TYPE_NONE) {\n                return;\n            }\n            switch (mLastEventType) {\n                case TYPE_ADD:\n                    mWrappedCallback.onInserted(mLastEventPosition, mLastEventCount);\n                    break;\n                case TYPE_REMOVE:\n                    mWrappedCallback.onRemoved(mLastEventPosition, mLastEventCount);\n                    break;\n                case TYPE_CHANGE:\n                    mWrappedCallback.onChanged(mLastEventPosition, mLastEventCount);\n                    break;\n            }\n            mLastEventType = TYPE_NONE;\n        }\n    }\n}\n\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/VirtualLayoutAdapter.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.support.annotation.NonNull;\nimport android.support.v7.widget.RecyclerView;\n\nimport java.util.List;\n\n/**\n * Adapter used in VirtualLayoutManager\n */\npublic abstract class VirtualLayoutAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {\n\n    @NonNull\n    protected VirtualLayoutManager mLayoutManager;\n\n    public VirtualLayoutAdapter(@NonNull VirtualLayoutManager layoutManager) {\n        this.mLayoutManager = layoutManager;\n    }\n\n    public void setLayoutHelpers(List<LayoutHelper> helpers) {\n        this.mLayoutManager.setLayoutHelpers(helpers);\n    }\n\n    @NonNull\n    public List<LayoutHelper> getLayoutHelpers() {\n        return this.mLayoutManager.getLayoutHelpers();\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/VirtualLayoutManager.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout;\n\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.os.Trace;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\n\nimport com.alibaba.android.vlayout.extend.LayoutManagerCanScrollListener;\nimport com.alibaba.android.vlayout.extend.PerformanceMonitor;\nimport com.alibaba.android.vlayout.extend.ViewLifeCycleHelper;\nimport com.alibaba.android.vlayout.extend.ViewLifeCycleListener;\nimport com.alibaba.android.vlayout.layout.BaseLayoutHelper;\nimport com.alibaba.android.vlayout.layout.DefaultLayoutHelper;\nimport com.alibaba.android.vlayout.layout.FixAreaAdjuster;\nimport com.alibaba.android.vlayout.layout.FixAreaLayoutHelper;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\n\n/**\n * A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides\n * a virtual layout for actual views.\n * <p>\n * NOTE: it will change {@link android.support.v7.widget.RecyclerView.RecycledViewPool}\n * for RecyclerView.\n *\n * @author villadora\n * @since 1.0.0\n */\n\npublic class VirtualLayoutManager extends ExposeLinearLayoutManagerEx implements LayoutManagerHelper {\n    protected static final String TAG = \"VirtualLayoutManager\";\n\n    private static final String PHASE_MEASURE = \"measure\";\n    private static final String PHASE_LAYOUT = \"layout\";\n    private static final String TRACE_LAYOUT = \"VLM onLayoutChildren\";\n    private static final String TRACE_SCROLL = \"VLM scroll\";\n\n    public static boolean sDebuggable = false;\n\n    public static void enableDebugging(boolean isDebug) {\n        sDebuggable = isDebug;\n    }\n\n    public static final int HORIZONTAL = OrientationHelper.HORIZONTAL;\n\n    public static final int VERTICAL = OrientationHelper.VERTICAL;\n\n\n    protected OrientationHelperEx mOrientationHelper;\n    protected OrientationHelperEx mSecondaryOrientationHelper;\n\n    private RecyclerView mRecyclerView;\n\n    private boolean mNoScrolling = false;\n\n    private boolean mNestedScrolling = false;\n\n    private boolean mCanScrollHorizontally;\n\n    private boolean mCanScrollVertically;\n\n    private LayoutManagerCanScrollListener layoutManagerCanScrollListener;\n\n    private boolean mEnableMarginOverlapping = false;\n\n    private int mMaxMeasureSize = -1;\n\n    private PerformanceMonitor mPerformanceMonitor;\n\n    private ViewLifeCycleHelper mViewLifeCycleHelper;\n\n    private Comparator<Pair<Range<Integer>, Integer>> mRangeComparator = new Comparator<Pair<Range<Integer>, Integer>>() {\n        @Override\n        public int compare(Pair<Range<Integer>, Integer> a, Pair<Range<Integer>, Integer> b) {\n            if (a == null && b == null) return 0;\n            if (a == null) return -1;\n            if (b == null) return 1;\n\n            Range<Integer> lr = a.first;\n            Range<Integer> rr = b.first;\n\n            return lr.getLower() - rr.getLower();\n        }\n    };\n\n    public VirtualLayoutManager(@NonNull final Context context) {\n        this(context, VERTICAL);\n    }\n\n    /**\n     * @param context     Context\n     * @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link\n     *                    #VERTICAL}.\n     */\n    public VirtualLayoutManager(@NonNull final Context context, int orientation) {\n        this(context, orientation, false);\n    }\n\n    /**\n     * @param context       Current context, will be used to access resources.\n     * @param orientation   Layout orientation. Should be {@link #HORIZONTAL} or {@link\n     *                      #VERTICAL}.\n     * @param reverseLayout whether should reverse data\n     */\n    public VirtualLayoutManager(@NonNull final Context context, int orientation, boolean reverseLayout) {\n        super(context, orientation, reverseLayout);\n        this.mOrientationHelper = OrientationHelperEx.createOrientationHelper(this, orientation);\n        this.mSecondaryOrientationHelper = OrientationHelperEx.createOrientationHelper(this, orientation == VERTICAL ? HORIZONTAL : VERTICAL);\n        this.mCanScrollVertically = super.canScrollVertically();\n        this.mCanScrollHorizontally = super.canScrollHorizontally();\n        setHelperFinder(new RangeLayoutHelperFinder());\n    }\n\n    public void setPerformanceMonitor(PerformanceMonitor performanceMonitor) {\n        mPerformanceMonitor = performanceMonitor;\n    }\n\n    public void setNoScrolling(boolean noScrolling) {\n        this.mNoScrolling = noScrolling;\n        mSpaceMeasured = false;\n        mMeasuredFullSpace = 0;\n        mSpaceMeasuring = false;\n    }\n\n    public void setCanScrollVertically(boolean canScrollVertically) {\n        this.mCanScrollVertically = canScrollVertically;\n    }\n\n    public void setCanScrollHorizontally(boolean canScrollHorizontally) {\n        this.mCanScrollHorizontally = canScrollHorizontally;\n    }\n\n    public void setLayoutManagerCanScrollListener(LayoutManagerCanScrollListener layoutManagerCanScrollListener) {\n        this.layoutManagerCanScrollListener = layoutManagerCanScrollListener;\n    }\n\n    public void setNestedScrolling(boolean nestedScrolling) {\n        setNestedScrolling(nestedScrolling, -1);\n    }\n\n    public void setNestedScrolling(boolean nestedScrolling, int maxMeasureSize) {\n        this.mNestedScrolling = nestedScrolling;\n        mSpaceMeasuring = mSpaceMeasured = false;\n        mMeasuredFullSpace = 0;\n    }\n\n    private LayoutHelperFinder mHelperFinder;\n\n    public void setHelperFinder(@NonNull final LayoutHelperFinder finder) {\n        //noinspection ConstantConditions\n        if (finder == null) {\n            throw new IllegalArgumentException(\"finder is null\");\n        }\n\n        List<LayoutHelper> helpers = new LinkedList<>();\n        if (this.mHelperFinder != null) {\n            List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n            Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n            LayoutHelper layoutHelper = null;\n            while (iterator.hasNext()) {\n                layoutHelper = iterator.next();\n                helpers.add(layoutHelper);\n\n            }\n        }\n\n        this.mHelperFinder = finder;\n        if (helpers.size() > 0)\n            this.mHelperFinder.setLayouts(helpers);\n\n        mSpaceMeasured = false;\n        requestLayout();\n    }\n\n    private FixAreaAdjuster mFixAreaAdjustor = FixAreaAdjuster.mDefaultAdjuster;\n\n    public void setFixOffset(int left, int top, int right, int bottom) {\n        mFixAreaAdjustor = new FixAreaAdjuster(left, top, right, bottom);\n    }\n\n\n    /*\n     * Temp hashMap\n     */\n    private HashMap<Integer, LayoutHelper> newHelpersSet = new HashMap<>();\n    private HashMap<Integer, LayoutHelper> oldHelpersSet = new HashMap<>();\n\n    private BaseLayoutHelper.LayoutViewBindListener mLayoutViewBindListener;\n\n    /**\n     * Update layoutHelpers, data changes will cause layoutHelpers change\n     *\n     * @param helpers group of layoutHelpers\n     */\n    public void setLayoutHelpers(@Nullable List<LayoutHelper> helpers) {\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> it0 = layoutHelpers.iterator();\n        while (it0.hasNext()) {\n            LayoutHelper helper = it0.next();\n            oldHelpersSet.put(System.identityHashCode(helper), helper);\n        }\n\n        // set ranges\n        if (helpers != null) {\n            int start = 0;\n            Iterator<LayoutHelper> it1 = helpers.iterator();\n            while (it1.hasNext()) {\n                LayoutHelper helper = it1.next();\n                if (helper instanceof FixAreaLayoutHelper) {\n                    ((FixAreaLayoutHelper) helper).setAdjuster(mFixAreaAdjustor);\n                }\n\n                if (helper instanceof BaseLayoutHelper && mLayoutViewBindListener != null) {\n                    ((BaseLayoutHelper) helper).setLayoutViewBindListener(mLayoutViewBindListener);\n                }\n\n\n                if (helper.getItemCount() > 0) {\n                    helper.setRange(start, start + helper.getItemCount() - 1);\n                } else {\n                    helper.setRange(-1, -1);\n                }\n\n                start += helper.getItemCount();\n            }\n        }\n\n        this.mHelperFinder.setLayouts(helpers);\n\n        layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        while (iterator.hasNext()) {\n            LayoutHelper layoutHelper = iterator.next();\n            newHelpersSet.put(System.identityHashCode(layoutHelper), layoutHelper);\n        }\n\n        for (Iterator<Map.Entry<Integer, LayoutHelper>> it = oldHelpersSet.entrySet().iterator(); it.hasNext(); ) {\n            Map.Entry<Integer, LayoutHelper> entry = it.next();\n            Integer key = entry.getKey();\n            if (newHelpersSet.containsKey(key)) {\n                newHelpersSet.remove(key);\n                it.remove();\n            }\n        }\n\n\n        for (LayoutHelper helper : oldHelpersSet.values()) {\n            helper.clear(this);\n        }\n\n        if (!oldHelpersSet.isEmpty() || !newHelpersSet.isEmpty()) {\n            mSpaceMeasured = false;\n        }\n\n        oldHelpersSet.clear();\n        newHelpersSet.clear();\n        requestLayout();\n    }\n\n\n    @NonNull\n    public List<LayoutHelper> getLayoutHelpers() {\n        return this.mHelperFinder.getLayoutHelpers();\n    }\n\n    public void setEnableMarginOverlapping(boolean enableMarginOverlapping) {\n        mEnableMarginOverlapping = enableMarginOverlapping;\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public boolean isEnableMarginOverLap() {\n        return mEnableMarginOverlapping;\n    }\n\n    /**\n     * Either be {@link #HORIZONTAL} or {@link #VERTICAL}\n     *\n     * @return orientation of this layout manager\n     */\n    @Override\n    public int getOrientation() {\n        return super.getOrientation();\n    }\n\n    @Override\n    public void setOrientation(int orientation) {\n        this.mOrientationHelper = OrientationHelperEx.createOrientationHelper(this, orientation);\n        super.setOrientation(orientation);\n    }\n\n    /**\n     * reverseLayout is not supported by VirtualLayoutManager. It's get disabled until all the LayoutHelpers support it.\n     */\n    @Override\n    public void setReverseLayout(boolean reverseLayout) {\n        if (reverseLayout) {\n            throw new UnsupportedOperationException(\n                    \"VirtualLayoutManager does not support reverse layout in current version.\");\n        }\n\n        super.setReverseLayout(false);\n    }\n\n    /**\n     * stackFromEnd is not supported by VirtualLayoutManager. It's get disabled util all the layoutHelpers support it.\n     * {@link #setReverseLayout(boolean)}.\n     */\n    @Override\n    public void setStackFromEnd(boolean stackFromEnd) {\n        if (stackFromEnd) {\n            throw new UnsupportedOperationException(\n                    \"VirtualLayoutManager does not support stack from end.\");\n        }\n        super.setStackFromEnd(false);\n    }\n\n\n    private AnchorInfoWrapper mTempAnchorInfoWrapper = new AnchorInfoWrapper();\n\n    @Override\n    public void onAnchorReady(RecyclerView.State state, ExposeLinearLayoutManagerEx.AnchorInfo anchorInfo) {\n        super.onAnchorReady(state, anchorInfo);\n\n        boolean changed = true;\n        while (changed) {\n            mTempAnchorInfoWrapper.position = anchorInfo.mPosition;\n            mTempAnchorInfoWrapper.coordinate = anchorInfo.mCoordinate;\n            mTempAnchorInfoWrapper.layoutFromEnd = anchorInfo.mLayoutFromEnd;\n            LayoutHelper layoutHelper = mHelperFinder.getLayoutHelper(anchorInfo.mPosition);\n            if (layoutHelper != null)\n                layoutHelper.checkAnchorInfo(state, mTempAnchorInfoWrapper, this);\n\n            if (mTempAnchorInfoWrapper.position == anchorInfo.mPosition) {\n                changed = false;\n            } else {\n                anchorInfo.mPosition = mTempAnchorInfoWrapper.position;\n            }\n\n            anchorInfo.mCoordinate = mTempAnchorInfoWrapper.coordinate;\n\n            mTempAnchorInfoWrapper.position = -1;\n        }\n\n\n        mTempAnchorInfoWrapper.position = anchorInfo.mPosition;\n        mTempAnchorInfoWrapper.coordinate = anchorInfo.mCoordinate;\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            layoutHelper.onRefreshLayout(state, mTempAnchorInfoWrapper, this);\n        }\n    }\n\n    public LayoutHelper findNeighbourNonfixLayoutHelper(LayoutHelper layoutHelper, boolean isLayoutEnd) {\n        if (layoutHelper == null) {\n            return null;\n        }\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        int index = layoutHelpers.indexOf(layoutHelper);\n        if (index == -1) {\n            return null;\n        }\n        int next = isLayoutEnd ? index - 1 : index + 1;\n        if (next >= 0 && next < layoutHelpers.size()) {\n            LayoutHelper helper = layoutHelpers.get(next);\n            if (helper != null) {\n                if (helper.isFixLayout()) {\n                    return null;\n                } else {\n                    return helper;\n                }\n            } else {\n                return null;\n            }\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    protected int computeAlignOffset(View child, boolean isLayoutEnd, boolean useAnchor) {\n        return computeAlignOffset(getPosition(child), isLayoutEnd, useAnchor);\n    }\n\n    @Override\n    protected int computeAlignOffset(int position, boolean isLayoutEnd, boolean useAnchor) {\n        if (position != RecyclerView.NO_POSITION) {\n            LayoutHelper helper = mHelperFinder.getLayoutHelper(position);\n\n            if (helper != null) {\n                return helper.computeAlignOffset(position - helper.getRange().getLower(),\n                        isLayoutEnd, useAnchor, this);\n            }\n        }\n\n        return 0;\n    }\n\n    public int obtainExtraMargin(View child, boolean isLayoutEnd) {\n        return obtainExtraMargin(child, isLayoutEnd, true);\n    }\n\n    public int obtainExtraMargin(View child, boolean isLayoutEnd, boolean useAnchor) {\n        if (child != null) {\n            return computeAlignOffset(child, isLayoutEnd, useAnchor);\n        }\n\n        return 0;\n    }\n\n    private int mNested = 0;\n\n\n    private void runPreLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {\n\n        if (mNested == 0) {\n            List<LayoutHelper> reverseLayoutHelpers = mHelperFinder.reverse();\n            Iterator<LayoutHelper> iterator = reverseLayoutHelpers.iterator();\n            LayoutHelper layoutHelper = null;\n            while (iterator.hasNext()) {\n                layoutHelper = iterator.next();\n                layoutHelper.beforeLayout(recycler, state, this);\n            }\n        }\n\n        mNested++;\n    }\n\n    private void runPostLayout(RecyclerView.Recycler recycler, RecyclerView.State state, int scrolled) {\n        mNested--;\n        if (mNested <= 0) {\n            mNested = 0;\n            final int startPosition = findFirstVisibleItemPosition();\n            final int endPosition = findLastVisibleItemPosition();\n            List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n            Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n            LayoutHelper layoutHelper = null;\n            while (iterator.hasNext()) {\n                layoutHelper = iterator.next();\n                try {\n                    layoutHelper.afterLayout(recycler, state, startPosition, endPosition, scrolled, this);\n                } catch (Exception e) {\n                    if (VirtualLayoutManager.sDebuggable) {\n                        throw e;\n                    }\n                }\n            }\n\n            if (null != mViewLifeCycleHelper) {\n                mViewLifeCycleHelper.checkViewStatusInScreen();\n            }\n        }\n    }\n\n    public void runAdjustLayout() {\n        final int startPosition = findFirstVisibleItemPosition();\n        final LayoutHelper firstLayoutHelper = mHelperFinder.getLayoutHelper(startPosition);\n        final int endPosition = findLastVisibleItemPosition();\n        final LayoutHelper lastLayoutHelper = mHelperFinder.getLayoutHelper(endPosition);\n        List<LayoutHelper> totalLayoutHelpers = mHelperFinder.getLayoutHelpers();\n        final int start = totalLayoutHelpers.indexOf(firstLayoutHelper);\n        final int end = totalLayoutHelpers.indexOf(lastLayoutHelper);\n        for (int i = start; i <= end; i++) {\n            try {\n                totalLayoutHelpers.get(i).adjustLayout(startPosition, endPosition, this);\n            } catch (Exception e) {\n                if (VirtualLayoutManager.sDebuggable) {\n                    throw e;\n                }\n            }\n        }\n    }\n\n    @Override\n    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            Trace.beginSection(TRACE_LAYOUT);\n        }\n\n        if (mNoScrolling && state.didStructureChange()) {\n            mSpaceMeasured = false;\n            mSpaceMeasuring = true;\n        }\n\n\n        runPreLayout(recycler, state);\n\n        try {\n            super.onLayoutChildren(recycler, state);\n        } catch (Exception e) {\n            e.printStackTrace();\n            throw e;\n        } finally {\n            // MaX_VALUE means invalidate scrolling offset - no scroll\n            runPostLayout(recycler, state, Integer.MAX_VALUE); // hack to indicate its an initial layout\n        }\n\n\n        if ((mNestedScrolling || mNoScrolling) && mSpaceMeasuring) {\n            // measure required, so do measure\n            mSpaceMeasured = true;\n            // get last child\n            int childCount = getChildCount();\n            View lastChild = getChildAt(childCount - 1);\n            if (lastChild != null) {\n                RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) lastChild.getLayoutParams();\n                // found the end of last child view\n                mMeasuredFullSpace = getDecoratedBottom(lastChild) + params.bottomMargin + computeAlignOffset(lastChild, true, false);\n\n                if (mRecyclerView != null && mNestedScrolling) {\n                    ViewParent parent = mRecyclerView.getParent();\n                    if (parent instanceof View) {\n                        // make sure the fullspace be the min value of measured space and parent's height\n                        mMeasuredFullSpace = Math.min(mMeasuredFullSpace, ((View) parent).getMeasuredHeight());\n                    }\n                }\n            } else {\n                mSpaceMeasuring = false;\n            }\n            mSpaceMeasuring = false;\n            if (mRecyclerView != null && getItemCount() > 0) {\n                // relayout\n                mRecyclerView.post(new Runnable() {\n                    @Override\n                    public void run() {\n                        // post relayout\n                        if (mRecyclerView != null)\n                            mRecyclerView.requestLayout();\n                    }\n                });\n            }\n        }\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            Trace.endSection();\n        }\n    }\n\n    /**\n     * Entry method for scrolling\n     * {@inheritDoc}\n     */\n    @Override\n    protected int scrollInternalBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            Trace.beginSection(TRACE_SCROLL);\n        }\n\n        runPreLayout(recycler, state);\n\n        int scrolled = 0;\n        try {\n            if (!mNoScrolling) {\n                scrolled = super.scrollInternalBy(dy, recycler, state);\n            } else {\n                if (getChildCount() == 0 || dy == 0) {\n                    return 0;\n                }\n\n                mLayoutState.mRecycle = true;\n                ensureLayoutStateExpose();\n                final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;\n                final int absDy = Math.abs(dy);\n                updateLayoutStateExpose(layoutDirection, absDy, true, state);\n                final int freeScroll = mLayoutState.mScrollingOffset;\n\n                final int consumed = freeScroll + fill(recycler, mLayoutState, state, false);\n                if (consumed < 0) {\n                    return 0;\n                }\n                scrolled = absDy > consumed ? layoutDirection * consumed : dy;\n            }\n        } catch (Exception e) {\n            Log.w(TAG, Log.getStackTraceString(e), e);\n            if (sDebuggable)\n                throw e;\n\n        } finally {\n            runPostLayout(recycler, state, scrolled);\n        }\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            Trace.endSection();\n        }\n\n        return scrolled;\n    }\n\n    @Override\n    public void onScrollStateChanged(int state) {\n        super.onScrollStateChanged(state);\n\n        int startPosition = findFirstVisibleItemPosition();\n        int endPosition = findLastVisibleItemPosition();\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            layoutHelper.onScrollStateChanged(state, startPosition, endPosition, this);\n        }\n    }\n\n    @Override\n    public void offsetChildrenHorizontal(int dx) {\n        super.offsetChildrenHorizontal(dx);\n\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            layoutHelper.onOffsetChildrenHorizontal(dx, this);\n\n        }\n    }\n\n    @Override\n    public void offsetChildrenVertical(int dy) {\n        super.offsetChildrenVertical(dy);\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            layoutHelper.onOffsetChildrenVertical(dy, this);\n        }\n\n        if (null != mViewLifeCycleHelper) {\n            mViewLifeCycleHelper.checkViewStatusInScreen();\n        }\n    }\n\n    public void setViewLifeCycleListener(@NonNull ViewLifeCycleListener viewLifeCycleListener) {\n        if (null == viewLifeCycleListener) {\n            throw new IllegalArgumentException(\"ViewLifeCycleListener should not be null!\");\n        }\n\n        mViewLifeCycleHelper = new ViewLifeCycleHelper(this, viewLifeCycleListener);\n    }\n\n    public int getVirtualLayoutDirection() {\n        return mLayoutState.mLayoutDirection;\n    }\n\n    private LayoutStateWrapper mTempLayoutStateWrapper = new LayoutStateWrapper();\n\n    private List<Pair<Range<Integer>, Integer>> mRangeLengths = new ArrayList<>();\n\n    @Nullable\n    private int findRangeLength(@NonNull final Range<Integer> range) {\n        final int count = mRangeLengths.size();\n        if (count == 0) {\n            return -1;\n        }\n\n        int s = 0, e = count - 1, m = -1;\n        Pair<Range<Integer>, Integer> rs = null;\n\n        // binary search range\n        while (s <= e) {\n            m = (s + e) / 2;\n            rs = mRangeLengths.get(m);\n\n            Range<Integer> r = rs.first;\n            if (r == null) {\n                rs = null;\n                break;\n            }\n\n            if (r.contains(range.getLower()) || r.contains(range.getUpper()) || range.contains(r)) {\n                break;\n            } else if (r.getLower() > range.getUpper()) {\n                e = m - 1;\n            } else if (r.getUpper() < range.getLower()) {\n                s = m + 1;\n            }\n\n            rs = null;\n        }\n\n        return rs == null ? -1 : m;\n    }\n\n\n    @Override\n    protected void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult result) {\n        final int position = layoutState.mCurrentPosition;\n        mTempLayoutStateWrapper.mLayoutState = layoutState;\n        LayoutHelper layoutHelper = mHelperFinder == null ? null : mHelperFinder.getLayoutHelper(position);\n        if (layoutHelper == null)\n            layoutHelper = mDefaultLayoutHelper;\n\n        layoutHelper.doLayout(recycler, state, mTempLayoutStateWrapper, result, this);\n\n\n        mTempLayoutStateWrapper.mLayoutState = null;\n\n\n        // no item consumed\n        if (layoutState.mCurrentPosition == position) {\n            if (sDebuggable) {\n                Log.w(TAG, \"layoutHelper[\" + layoutHelper.getClass().getSimpleName() + \"@\" + layoutHelper.toString() + \"] consumes no item!\");\n            }\n            // break as no item consumed\n            result.mFinished = true;\n        } else {\n            // Update height consumed in each layoutChunck pass\n            final int positionAfterLayout = layoutState.mCurrentPosition - layoutState.mItemDirection;\n            final int consumed = result.mIgnoreConsumed ? 0 : result.mConsumed;\n\n            // TODO: change when supporting reverseLayout\n            Range<Integer> range = new Range<>(Math.min(position, positionAfterLayout), Math.max(position, positionAfterLayout));\n\n            final int idx = findRangeLength(range);\n            if (idx >= 0) {\n                Pair<Range<Integer>, Integer> pair = mRangeLengths.get(idx);\n                if (pair != null && pair.first.equals(range) && pair.second == consumed)\n                    return;\n\n                mRangeLengths.remove(idx);\n            }\n\n            mRangeLengths.add(Pair.create(range, consumed));\n            Collections.sort(mRangeLengths, mRangeComparator);\n        }\n    }\n\n\n    /**\n     * Return current position related to the top, only works when scrolling from the top\n     *\n     * @return offset from current position to original top of RecycledView\n     */\n    public int getOffsetToStart() {\n        if (getChildCount() == 0) return -1;\n\n        final View view = getChildAt(0);\n\n        if (view == null) {\n            //in some conditions, for exapmle, calling this method when outter activity destroy, may cause npe\n            return -1;\n        }\n\n        int position = getPosition(view);\n        final int idx = findRangeLength(Range.create(position, position));\n        if (idx < 0 || idx >= mRangeLengths.size()) {\n            return -1;\n        }\n\n        int offset = -mOrientationHelper.getDecoratedStart(view);\n        for (int i = 0; i < idx; i++) {\n            Pair<Range<Integer>, Integer> pair = mRangeLengths.get(i);\n            if (pair != null) {\n                offset += pair.second;\n            }\n        }\n\n        return offset;\n    }\n\n\n    private static LayoutHelper DEFAULT_LAYOUT_HELPER = new DefaultLayoutHelper();\n\n    private LayoutHelper mDefaultLayoutHelper = DEFAULT_LAYOUT_HELPER;\n\n    /**\n     * Change default LayoutHelper\n     *\n     * @param layoutHelper default layoutHelper apply to items without specified layoutHelper, it should not be null\n     */\n    private void setDefaultLayoutHelper(@NonNull final LayoutHelper layoutHelper) {\n        //noinspection ConstantConditions\n        if (layoutHelper == null)\n            throw new IllegalArgumentException(\"layoutHelper should not be null\");\n\n        this.mDefaultLayoutHelper = layoutHelper;\n    }\n\n    @Override\n    public void scrollToPosition(int position) {\n        super.scrollToPosition(position);\n    }\n\n\n    @Override\n    public void scrollToPositionWithOffset(int position, int offset) {\n        super.scrollToPositionWithOffset(position, offset);\n    }\n\n    @Override\n    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {\n        super.smoothScrollToPosition(recyclerView, state, position);\n    }\n\n\n    @Override\n    public boolean supportsPredictiveItemAnimations() {\n        return mCurrentPendingSavedState == null;\n    }\n\n\n    /**\n     * Do updates when items change\n     *\n     * @param recyclerView  recyclerView that belong to\n     * @param positionStart start position that items changed\n     * @param itemCount     number of items that changed\n     */\n    @Override\n    public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {\n        onItemsChanged(recyclerView);\n    }\n\n    @Override\n    public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {\n        onItemsChanged(recyclerView);\n    }\n\n    @Override\n    public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {\n        onItemsChanged(recyclerView);\n    }\n\n    @Override\n    public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {\n        onItemsChanged(recyclerView);\n    }\n\n    @Override\n    public void onItemsChanged(RecyclerView recyclerView) {\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            layoutHelper.onItemsChanged(this);\n        }\n\n        // setLayoutHelpers(mHelperFinder.getLayoutHelpers());\n    }\n\n\n    @Override\n    public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {\n        return lp instanceof LayoutParams;\n    }\n\n    @Override\n    public RecyclerView.LayoutParams generateDefaultLayoutParams() {\n        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,\n                ViewGroup.LayoutParams.WRAP_CONTENT);\n    }\n\n    @Override\n    public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {\n        if (lp instanceof LayoutParams) {\n            return new LayoutParams((LayoutParams) lp);\n        } else if (lp instanceof RecyclerView.LayoutParams) {\n            return new LayoutParams((RecyclerView.LayoutParams) lp);\n        } else if (lp instanceof ViewGroup.MarginLayoutParams) {\n            return new LayoutParams((ViewGroup.MarginLayoutParams) lp);\n        } else {\n            return new LayoutParams(lp);\n        }\n    }\n\n    @Override\n    public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {\n        return new InflateLayoutParams(c, attrs);\n    }\n\n    @Override\n    public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) {\n        super.onAdapterChanged(oldAdapter, newAdapter);\n    }\n\n\n    @Override\n    public void onAttachedToWindow(RecyclerView view) {\n        super.onAttachedToWindow(view);\n        mRecyclerView = view;\n    }\n\n    @Override\n    public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) {\n        super.onDetachedFromWindow(view, recycler);\n\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            layoutHelper.clear(this);\n        }\n\n        mRecyclerView = null;\n    }\n\n\n    @SuppressWarnings(\"unused\")\n    public static class LayoutParams extends RecyclerView.LayoutParams {\n        public static final int INVALIDE_SIZE = Integer.MIN_VALUE;\n\n\n        public int zIndex = 0;\n\n        public float mAspectRatio = Float.NaN;\n\n        private int mOriginWidth = INVALIDE_SIZE;\n        private int mOriginHeight = INVALIDE_SIZE;\n\n\n        public void storeOriginWidth() {\n            if (mOriginWidth == INVALIDE_SIZE) {\n                mOriginWidth = width;\n            }\n        }\n\n        public void storeOriginHeight() {\n            if (mOriginHeight == INVALIDE_SIZE) {\n                mOriginHeight = height;\n            }\n        }\n\n        public void restoreOriginWidth() {\n            if (mOriginWidth != INVALIDE_SIZE) {\n                width = mOriginWidth;\n            }\n        }\n\n        public void restoreOriginHeight() {\n            if (mOriginHeight != INVALIDE_SIZE) {\n                height = mOriginHeight;\n            }\n        }\n\n        public LayoutParams(Context c, AttributeSet attrs) {\n            super(c, attrs);\n        }\n\n        public LayoutParams(int width, int height) {\n            super(width, height);\n        }\n\n        public LayoutParams(ViewGroup.MarginLayoutParams source) {\n            super(source);\n        }\n\n        public LayoutParams(ViewGroup.LayoutParams source) {\n            super(source);\n        }\n\n        public LayoutParams(RecyclerView.LayoutParams source) {\n            super(source);\n        }\n\n    }\n\n    public static class InflateLayoutParams extends LayoutParams {\n\n        public InflateLayoutParams(Context c, AttributeSet attrs) {\n            super(c, attrs);\n        }\n    }\n\n\n    public static class AnchorInfoWrapper {\n\n        public int position;\n\n        public int coordinate;\n\n        public boolean layoutFromEnd;\n\n        AnchorInfoWrapper() {\n\n        }\n\n    }\n\n\n    @SuppressWarnings({\"JavaDoc\", \"unused\"})\n    public static class LayoutStateWrapper {\n        public final static int LAYOUT_START = -1;\n\n        public final static int LAYOUT_END = 1;\n\n        final static int INVALID_LAYOUT = Integer.MIN_VALUE;\n\n        public final static int ITEM_DIRECTION_HEAD = -1;\n\n        public final static int ITEM_DIRECTION_TAIL = 1;\n\n        final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE;\n\n        private LayoutState mLayoutState;\n\n        LayoutStateWrapper() {\n\n        }\n\n        LayoutStateWrapper(LayoutState layoutState) {\n            this.mLayoutState = layoutState;\n        }\n\n\n        public int getOffset() {\n            return mLayoutState.mOffset;\n        }\n\n        public int getCurrentPosition() {\n            return mLayoutState.mCurrentPosition;\n        }\n\n        public boolean hasScrapList() {\n            return mLayoutState.mScrapList != null;\n        }\n\n        public void skipCurrentPosition() {\n            mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;\n        }\n\n        /**\n         * We may not want to recycle children in some cases (e.g. layout)\n         */\n        public boolean isRecycle() {\n            return mLayoutState.mRecycle;\n        }\n\n\n        /**\n         * This {@link #layoutChunk(RecyclerView.Recycler, RecyclerView.State, LayoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult)} pass is in layouting or scrolling\n         */\n        public boolean isRefreshLayout() {\n            return mLayoutState.mOnRefresLayout;\n        }\n\n        /**\n         * Number of pixels that we should fill, in the layout direction.\n         */\n        public int getAvailable() {\n            return mLayoutState.mAvailable;\n        }\n\n\n        /**\n         * Defines the direction in which the data adapter is traversed.\n         * Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}\n         */\n        public int getItemDirection() {\n            return mLayoutState.mItemDirection;\n        }\n\n        /**\n         * Defines the direction in which the layout is filled.\n         * Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}\n         */\n        public int getLayoutDirection() {\n            return mLayoutState.mLayoutDirection;\n        }\n\n        /**\n         * Used when LayoutState is constructed in a scrolling state.\n         * It should be set the amount of scrolling we can make without creating a new view.\n         * Settings this is required for efficient view recycling.\n         */\n        public int getScrollingOffset() {\n            return mLayoutState.mScrollingOffset;\n        }\n\n        /**\n         * Used if you want to pre-layout items that are not yet visible.\n         * The difference with {@link #getAvailable()} is that, when recycling, distance laid out for\n         * {@link #getExtra()} is not considered to avoid recycling visible children.\n         */\n        public int getExtra() {\n            return mLayoutState.mExtra;\n        }\n\n        /**\n         * Equal to {@link RecyclerView.State#isPreLayout()}. When consuming scrap, if this value\n         * is set to true, we skip removed views since they should not be laid out in post layout\n         * step.\n         */\n        public boolean isPreLayout() {\n            return mLayoutState.mIsPreLayout;\n        }\n\n\n        public boolean hasMore(RecyclerView.State state) {\n            return mLayoutState.hasMore(state);\n        }\n\n        public View next(RecyclerView.Recycler recycler) {\n            View next = mLayoutState.next(recycler);\n            // set recycler\n            return next;\n        }\n\n        public View retrieve(RecyclerView.Recycler recycler, int position) {\n            int originPosition = mLayoutState.mCurrentPosition;\n            mLayoutState.mCurrentPosition = position;\n            View view = next(recycler);\n            mLayoutState.mCurrentPosition = originPosition;\n            return view;\n        }\n    }\n\n\n    private static class LayoutViewHolder extends RecyclerView.ViewHolder {\n\n        public LayoutViewHolder(View itemView) {\n            super(itemView);\n        }\n\n    }\n\n\n    public List<View> getFixedViews() {\n        if (mRecyclerView == null) return Collections.emptyList();\n\n        // TODO: support zIndex?\n        List<View> views = new LinkedList<>();\n        List<LayoutHelper> layoutHelpers = mHelperFinder.getLayoutHelpers();\n        Iterator<LayoutHelper> iterator = layoutHelpers.iterator();\n        LayoutHelper layoutHelper = null;\n        while (iterator.hasNext()) {\n            layoutHelper = iterator.next();\n            View fixedView = layoutHelper.getFixedView();\n            if (fixedView != null) {\n                views.add(fixedView);\n            }\n        }\n\n        return views;\n    }\n\n\n    private LayoutViewFactory mLayoutViewFatory = new LayoutViewFactory() {\n        @Override\n        public View generateLayoutView(@NonNull Context context) {\n            return new LayoutView(context);\n        }\n    };\n\n    /**\n     * Set LayoutView Factory, so you can replace LayoutView for LayoutHelpers\n     *\n     * @param factory\n     */\n    public void setLayoutViewFactory(@NonNull final LayoutViewFactory factory) {\n        if (factory == null)\n            throw new IllegalArgumentException(\"factory should not be null\");\n        mLayoutViewFatory = factory;\n    }\n\n    @Override\n    public final View generateLayoutView() {\n        if (mRecyclerView == null) return null;\n\n        View layoutView = mLayoutViewFatory.generateLayoutView(mRecyclerView.getContext());\n        LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n        attachViewHolder(params, new LayoutViewHolder(layoutView));\n\n        layoutView.setLayoutParams(params);\n        return layoutView;\n    }\n\n\n    @Override\n    public void addChildView(View view, int index) {\n        super.addView(view, index);\n    }\n\n\n    @Override\n    public void moveView(int fromIndex, int toIndex) {\n        super.moveView(fromIndex, toIndex);\n    }\n\n    @Override\n    public void addChildView(LayoutStateWrapper layoutState, View view) {\n        addChildView(layoutState, view, layoutState.getItemDirection() == LayoutStateWrapper.ITEM_DIRECTION_TAIL ? -1 : 0);\n    }\n\n\n    @Override\n    public void addChildView(LayoutStateWrapper layoutState, View view, int index) {\n        showView(view);\n\n        if (!layoutState.hasScrapList()) {\n            // can not find in scrapList\n            addView(view, index);\n        } else {\n            addDisappearingView(view, index);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void addOffFlowView(View view, boolean head) {\n        showView(view);\n        addHiddenView(view, head);\n\n    }\n\n    @Override\n    public void addBackgroundView(View view, boolean head) {\n        showView(view);\n        int index = head ? 0 : -1;\n        addView(view, index);\n    }\n\n    @Override\n    public void addFixedView(View view) {\n        //removeChildView(view);\n        //mFixedContainer.addView(view);\n        addOffFlowView(view, false);\n    }\n\n    @Override\n    public void hideView(View view) {\n        super.hideView(view);\n    }\n\n    @Override\n    public void showView(View view) {\n        super.showView(view);\n    }\n\n    @Override\n    public RecyclerView getRecyclerView() {\n        return mRecyclerView;\n    }\n\n    @Override\n    public RecyclerView.ViewHolder getChildViewHolder(View view) {\n        if (mRecyclerView != null)\n            return mRecyclerView.getChildViewHolder(view);\n        return null;\n    }\n\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public boolean isViewHolderUpdated(View view) {\n        RecyclerView.ViewHolder holder = getChildViewHolder(view);\n        return holder == null || isViewHolderUpdated(holder);\n\n    }\n\n    @Override\n    public void removeChildView(View child) {\n        removeView(child);\n    }\n\n    @Override\n    public OrientationHelperEx getMainOrientationHelper() {\n        return mOrientationHelper;\n    }\n\n    @Override\n    public OrientationHelperEx getSecondaryOrientationHelper() {\n        return mSecondaryOrientationHelper;\n    }\n\n    @Override\n    public void measureChild(View child, int widthSpec, int heightSpec) {\n        measureChildWithDecorations(child, widthSpec, heightSpec);\n    }\n\n    @Override\n    public void measureChildWithMargins(View child, int widthUsed, int heightUsed) {\n        measureChildWithDecorationsAndMargin(child, widthUsed, heightUsed);\n    }\n\n    @Override\n    public int getChildMeasureSpec(int parentSize, int size, boolean canScroll) {\n        return getChildMeasureSpec(parentSize, 0, size, canScroll);\n    }\n\n\n    @Override\n    public boolean canScrollHorizontally() {\n        boolean ret = true;\n        if (layoutManagerCanScrollListener != null) {\n            ret = ret && layoutManagerCanScrollListener.canScrollHorizontally();\n        }\n        return mCanScrollHorizontally && !mNoScrolling && ret;\n    }\n\n    @Override\n    public boolean canScrollVertically() {\n        boolean ret = true;\n        if (layoutManagerCanScrollListener != null) {\n            ret = ret && layoutManagerCanScrollListener.canScrollVertically();\n        }\n        return mCanScrollVertically && !mNoScrolling && ret;\n    }\n\n    @Override\n    public void layoutChildWithMargins(View child, int left, int top, int right, int bottom) {\n        final ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child.getLayoutParams();\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordStart(PHASE_LAYOUT, child);\n        }\n        layoutDecorated(child, left + lp.leftMargin, top + lp.topMargin,\n                right - lp.rightMargin, bottom - lp.bottomMargin);\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordEnd(PHASE_LAYOUT, child);\n        }\n    }\n\n    @Override\n    public void layoutChild(View child, int left, int top, int right, int bottom) {\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordStart(PHASE_LAYOUT, child);\n        }\n        layoutDecorated(child, left, top,\n                right, bottom);\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordEnd(PHASE_LAYOUT, child);\n        }\n    }\n\n    @Override\n    protected void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {\n        if (startIndex == endIndex) {\n            return;\n        }\n\n        if (sDebuggable) {\n            Log.d(TAG, \"Recycling \" + Math.abs(startIndex - endIndex) + \" items\");\n        }\n\n        if (endIndex > startIndex) {\n\n            View endView = getChildAt(endIndex - 1);\n            View startView = getChildAt(startIndex);\n\n            int startPos = getPosition(startView);\n            int endPos = getPosition(endView);\n\n            int idx = startIndex;\n\n            for (int i = startIndex; i < endIndex; i++) {\n                View v = getChildAt(idx);\n                int pos = getPosition(v);\n                if (pos != RecyclerView.NO_POSITION) {\n                    LayoutHelper layoutHelper = mHelperFinder.getLayoutHelper(pos);\n                    if (layoutHelper == null || layoutHelper.isRecyclable(pos, startPos, endPos, this, true)) {\n                        removeAndRecycleViewAt(idx, recycler);\n                    } else {\n                        idx++;\n                    }\n                } else\n                    removeAndRecycleViewAt(idx, recycler);\n            }\n        } else {\n\n            View endView = getChildAt(startIndex);\n            View startView = getChildAt(endIndex + 1);\n\n            int startPos = getPosition(startView);\n            int endPos = getPosition(endView);\n\n            for (int i = startIndex; i > endIndex; i--) {\n                View v = getChildAt(i);\n                int pos = getPosition(v);\n                if (pos != RecyclerView.NO_POSITION) {\n                    LayoutHelper layoutHelper = mHelperFinder.getLayoutHelper(pos);\n                    if (layoutHelper == null || layoutHelper.isRecyclable(pos, startPos, endPos, this, false)) {\n                        removeAndRecycleViewAt(i, recycler);\n                    }\n                } else\n                    removeAndRecycleViewAt(i, recycler);\n            }\n        }\n    }\n\n\n    @Override\n    public void detachAndScrapAttachedViews(RecyclerView.Recycler recycler) {\n        int childCount = this.getChildCount();\n\n        for (int i = childCount - 1; i >= 0; --i) {\n            View v = this.getChildAt(i);\n            RecyclerView.ViewHolder holder = getChildViewHolder(v);\n            if (holder instanceof CacheViewHolder && ((CacheViewHolder) holder).needCached()) {\n                // mark not invalid, ignore DataSetChange(), make the ViewHolder itself to maitain the data\n                ViewHolderWrapper.setFlags(holder, 0, FLAG_INVALID | FLAG_UPDATED);\n            }\n        }\n\n\n        super.detachAndScrapAttachedViews(recycler);\n    }\n\n    @Override\n    public void detachAndScrapViewAt(int index, RecyclerView.Recycler recycler) {\n        View child = getChildAt(index);\n        RecyclerView.ViewHolder holder = getChildViewHolder(child);\n        if (holder instanceof CacheViewHolder && ((CacheViewHolder) holder).needCached()) {\n            // mark not invalid\n            ViewHolderWrapper.setFlags(holder, 0, FLAG_INVALID);\n        }\n\n        super.detachAndScrapViewAt(index, recycler);\n    }\n\n    @Override\n    public void detachAndScrapView(View child, RecyclerView.Recycler recycler) {\n        super.detachAndScrapView(child, recycler);\n    }\n\n    public interface CacheViewHolder {\n        boolean needCached();\n    }\n\n    @Override\n    public int getContentWidth() {\n        return super.getWidth();\n    }\n\n    @Override\n    public int getContentHeight() {\n        return super.getHeight();\n    }\n\n    @Override\n    public boolean isDoLayoutRTL() {\n        return isLayoutRTL();\n    }\n\n    private Rect mDecorInsets = new Rect();\n\n    private void measureChildWithDecorations(View child, int widthSpec, int heightSpec) {\n        calculateItemDecorationsForChild(child, mDecorInsets);\n        widthSpec = updateSpecWithExtra(widthSpec, mDecorInsets.left, mDecorInsets.right);\n        heightSpec = updateSpecWithExtra(heightSpec, mDecorInsets.top, mDecorInsets.bottom);\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordStart(PHASE_MEASURE, child);\n        }\n        child.measure(widthSpec, heightSpec);\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordEnd(PHASE_MEASURE, child);\n        }\n    }\n\n    private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {\n        calculateItemDecorationsForChild(child, mDecorInsets);\n        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();\n\n        if (getOrientation() == VERTICAL) {\n            widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left,\n                    lp.rightMargin + mDecorInsets.right);\n        }\n        if (getOrientation() == HORIZONTAL) {\n            heightSpec = updateSpecWithExtra(heightSpec, mDecorInsets.top,\n                    mDecorInsets.bottom);\n        }\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordStart(PHASE_MEASURE, child);\n        }\n        child.measure(widthSpec, heightSpec);\n        if (mPerformanceMonitor != null) {\n            mPerformanceMonitor.recordEnd(PHASE_MEASURE, child);\n        }\n    }\n\n    /**\n     * Update measure spec with insets\n     *\n     * @param spec\n     * @param startInset\n     * @param endInset\n     * @return\n     */\n    private int updateSpecWithExtra(int spec, int startInset, int endInset) {\n        if (startInset == 0 && endInset == 0) {\n            return spec;\n        }\n        final int mode = View.MeasureSpec.getMode(spec);\n        if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {\n            int size = View.MeasureSpec.getSize(spec);\n            if (size - startInset - endInset < 0) {\n                return View.MeasureSpec.makeMeasureSpec(0, mode);\n            } else {\n                return View.MeasureSpec.makeMeasureSpec(\n                        View.MeasureSpec.getSize(spec) - startInset - endInset, mode);\n            }\n        }\n        return spec;\n    }\n\n\n    @Override\n    public View findViewByPosition(int position) {\n        View view = super.findViewByPosition(position);\n        if (view != null && getPosition(view) == position)\n            return view;\n\n        for (int i = 0; i < getChildCount(); i++) {\n            view = getChildAt(i);\n            if (view != null && getPosition(view) == position) {\n                return view;\n            }\n        }\n\n        return null;\n    }\n\n\n    @Override\n    public void recycleView(View view) {\n        if (mRecyclerView != null) {\n            ViewParent parent = view.getParent();\n            if (parent != null && parent == mRecyclerView) {\n                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(view);\n                mRecyclerView.getRecycledViewPool().putRecycledView(holder);\n            }\n        }\n    }\n\n    @Override\n    public LayoutHelper findLayoutHelperByPosition(int position) {\n        return mHelperFinder.getLayoutHelper(position);\n    }\n\n\n\n\n    /*\n     * extend to full show view\n     */\n\n\n    // when set no scrolling, the max size should have limit\n    private static final int MAX_NO_SCROLLING_SIZE = Integer.MAX_VALUE >> 4;\n\n    private boolean mSpaceMeasured = false;\n\n    private int mMeasuredFullSpace = 0;\n\n    private boolean mSpaceMeasuring = false;\n\n\n    @Override\n    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {\n        if (!mNoScrolling && !mNestedScrolling) {\n\n            super.onMeasure(recycler, state, widthSpec, heightSpec);\n            return;\n        }\n\n\n        int initialSize = MAX_NO_SCROLLING_SIZE;\n\n        if (mRecyclerView != null && mNestedScrolling) {\n            if (mMaxMeasureSize > 0) {\n                initialSize = mMaxMeasureSize;\n            } else {\n                ViewParent parent = mRecyclerView.getParent();\n                if (parent instanceof View) {\n                    initialSize = ((View) parent).getMeasuredHeight();\n                }\n            }\n        }\n\n        int measuredSize = mSpaceMeasured ? mMeasuredFullSpace : initialSize;\n\n        if (mNoScrolling) {\n            mSpaceMeasuring = !mSpaceMeasured;\n\n            if (getChildCount() > 0 || getChildCount() != getItemCount()) {\n                View lastChild = getChildAt(getChildCount() - 1);\n\n                int bottom = mMeasuredFullSpace;\n                if (lastChild != null) {\n                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) lastChild.getLayoutParams();\n                    bottom = getDecoratedBottom(lastChild) + params.bottomMargin + computeAlignOffset(lastChild, true, false);\n                }\n\n                if (getChildCount() != getItemCount() || (lastChild != null && bottom != mMeasuredFullSpace)) {\n                    measuredSize = MAX_NO_SCROLLING_SIZE;\n                    mSpaceMeasured = false;\n                    mSpaceMeasuring = true;\n                }\n            } else if (getItemCount() == 0) {\n                measuredSize = 0;\n                mSpaceMeasured = true;\n                mSpaceMeasuring = false;\n            }\n        }\n\n\n        if (getOrientation() == VERTICAL) {\n            super.onMeasure(recycler, state, widthSpec, View.MeasureSpec.makeMeasureSpec(measuredSize, View.MeasureSpec.AT_MOST));\n        } else {\n            super.onMeasure(recycler, state, View.MeasureSpec.makeMeasureSpec(measuredSize, View.MeasureSpec.AT_MOST), heightSpec);\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/extend/InnerRecycledViewPool.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.extend;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.util.SparseIntArray;\nimport android.view.View;\n\nimport java.io.Closeable;\n\n/**\n * Wrapping original RecycledViewPool to provides destroy callback on Views\n * and also provides wrapping another ViewPool.\n * This ViewPool doesn't support multi thread.\n * The default max size of recycler is 5 as the original one, you can also modify to a larger one to satisfy your need.\n */\npublic final class InnerRecycledViewPool extends RecyclerView.RecycledViewPool {\n\n    private static final String TAG = \"InnerRecycledViewPool\";\n\n    private static int DEFAULT_MAX_SIZE = 20;\n\n    /*\n     * Wrapped InnerPool\n     */\n    private RecyclerView.RecycledViewPool mInnerPool;\n\n\n    private SparseIntArray mScrapLength = new SparseIntArray();\n    private SparseIntArray mMaxScrap = new SparseIntArray();\n\n    /**\n     * Wrap an existing pool\n     *\n     * @param pool\n     */\n    public InnerRecycledViewPool(RecyclerView.RecycledViewPool pool) {\n        this.mInnerPool = pool;\n    }\n\n\n    public InnerRecycledViewPool() {\n        this(new RecyclerView.RecycledViewPool());\n    }\n\n    @Override\n    public void clear() {\n        for (int i = 0, size = mScrapLength.size(); i < size; i++) {\n            int viewType = mScrapLength.keyAt(i);\n            RecyclerView.ViewHolder holder = mInnerPool.getRecycledView(viewType);\n            while (holder != null) {\n                destroyViewHolder(holder);\n                holder = mInnerPool.getRecycledView(viewType);\n            }\n        }\n\n        mScrapLength.clear();\n        super.clear();\n    }\n\n    @Override\n    public void setMaxRecycledViews(int viewType, int max) {\n        // When viewType is changed, because can not get items in wrapped pool,\n        // destroy all the items for the viewType\n        RecyclerView.ViewHolder holder = mInnerPool.getRecycledView(viewType);\n        while (holder != null) {\n            destroyViewHolder(holder);\n            holder = mInnerPool.getRecycledView(viewType);\n        }\n\n        // change maxRecycledViews\n        this.mMaxScrap.put(viewType, max);\n        this.mScrapLength.put(viewType, 0);\n        mInnerPool.setMaxRecycledViews(viewType, max);\n    }\n\n    @Override\n    public RecyclerView.ViewHolder getRecycledView(int viewType) {\n        RecyclerView.ViewHolder holder = mInnerPool.getRecycledView(viewType);\n        if (holder != null) {\n            int scrapHeapSize = mScrapLength.indexOfKey(viewType) >= 0 ? this.mScrapLength.get(viewType) : 0;\n            if (scrapHeapSize > 0)\n                mScrapLength.put(viewType, scrapHeapSize - 1);\n        }\n\n        return holder;\n    }\n\n\n    /**\n     * Get all items size in current pool\n     *\n     * @return the size of items in ViewPool\n     */\n    public int size() {\n        int count = 0;\n\n        for (int i = 0, size = mScrapLength.size(); i < size; i++) {\n            int val = mScrapLength.valueAt(i);\n            count += val;\n        }\n\n        return count;\n    }\n\n    /**\n     * This will be only run in UI Thread\n     *\n     * @param scrap ViewHolder scrap that will be recycled\n     */\n    @SuppressWarnings(\"unchecked\")\n    public void putRecycledView(RecyclerView.ViewHolder scrap) {\n        int viewType = scrap.getItemViewType();\n\n        if (mMaxScrap.indexOfKey(viewType) < 0) {\n            // does't contains this viewType, initial scrap list\n            mMaxScrap.put(viewType, DEFAULT_MAX_SIZE);\n            setMaxRecycledViews(viewType, DEFAULT_MAX_SIZE);\n        }\n\n        // get current heap size\n        int scrapHeapSize = mScrapLength.indexOfKey(viewType) >= 0 ? this.mScrapLength.get(viewType) : 0;\n\n        if (this.mMaxScrap.get(viewType) > scrapHeapSize) {\n            // if exceed current heap size\n            mInnerPool.putRecycledView(scrap);\n            mScrapLength.put(viewType, scrapHeapSize + 1);\n        } else {\n            // destroy viewHolder\n            destroyViewHolder(scrap);\n        }\n    }\n\n\n    private void destroyViewHolder(RecyclerView.ViewHolder holder) {\n        View view = holder.itemView;\n        // if view inherits {@link Closeable}, cal close method\n        if (view instanceof Closeable) {\n            try {\n                ((Closeable) view).close();\n            } catch (Exception e) {\n                Log.w(TAG, Log.getStackTraceString(e), e);\n            }\n        }\n\n        if (holder instanceof Closeable) {\n            try {\n                ((Closeable) holder).close();\n            } catch (Exception e) {\n                Log.w(TAG, Log.getStackTraceString(e), e);\n            }\n        }\n    }\n\n    public void setDefaultMaxSize(int maxSize) {\n        DEFAULT_MAX_SIZE = maxSize;\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/extend/LayoutManagerCanScrollListener.java",
    "content": "package com.alibaba.android.vlayout.extend;\n\npublic interface LayoutManagerCanScrollListener {\n    boolean canScrollVertically();\n\n    boolean canScrollHorizontally();\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/extend/PerformanceMonitor.java",
    "content": "package com.alibaba.android.vlayout.extend;\n\nimport android.support.annotation.Keep;\nimport android.view.View;\n\n/**\n * Add callback during measure and layout, help you to monitor your view's performance.<br />\n * Designed as Class instead of Interface is able to extend api in future. <br />\n *\n * Created by longerian on 2018/5/16.\n *\n * @author longerian\n * @date 2018/05/16\n */\npublic class PerformanceMonitor {\n\n    /**\n     * Record the start time\n     * @param phase\n     * @param viewType\n     */\n    @Keep\n    public void recordStart(String phase, String viewType) {\n\n    }\n\n    /**\n     * Record the end time\n     * @param phase\n     * @param viewType\n     */\n    @Keep\n    public void recordEnd(String phase, String viewType) {\n\n    }\n\n    /**\n     * Record the start time\n     * @param phase\n     * @param view\n     */\n    @Keep\n    public void recordStart(String phase, View view) {\n\n    }\n\n    /**\n     * Record the end time\n     * @param phase\n     * @param view\n     */\n    @Keep\n    public void recordEnd(String phase, View view) {\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleHelper.java",
    "content": "package com.alibaba.android.vlayout.extend;\n\nimport android.support.annotation.NonNull;\nimport android.support.v4.util.ArrayMap;\nimport android.view.View;\n\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\n\nimport java.util.HashMap;\n\npublic class ViewLifeCycleHelper {\n    public enum STATUS {\n        APPEARING,\n        APPEARED,\n        DISAPPEARING,\n        DISAPPEARED\n    }\n\n    private HashMap<View, STATUS> mViewStatusMap = new HashMap<>();\n\n    private ViewLifeCycleListener mViewLifeCycleListener;\n\n    private VirtualLayoutManager mVirtualLayoutManager;\n\n    private int scrHeight;\n\n    public ViewLifeCycleHelper(VirtualLayoutManager virtualLayoutManager, @NonNull ViewLifeCycleListener mViewLifeCycleListener) {\n        this.mViewLifeCycleListener = mViewLifeCycleListener;\n        this.mVirtualLayoutManager = virtualLayoutManager;\n    }\n\n    public void checkViewStatusInScreen() {\n        for (int i = 0; i < mVirtualLayoutManager.getChildCount(); i++) {\n            View view = mVirtualLayoutManager.getChildAt(i);\n            if (scrHeight == 0) {\n                scrHeight = view.getContext().getResources().getDisplayMetrics().heightPixels;\n            }\n\n            if (mVirtualLayoutManager.getVirtualLayoutDirection() == VirtualLayoutManager.LayoutState.LAYOUT_END) {\n                if (view.getTop() <= 0 && view.getBottom() >= 0 && isViewReadyDisAppearing(view)) {\n                    setViewDisappearing(view);\n                } else if (view.getTop() <= scrHeight && view.getBottom() >= scrHeight && isViewReadyAppearing(view)) {\n                    setViewAppearing(view);\n                }\n            } else {\n                if (view.getTop() <= 0 && view.getBottom() >= 0 && isViewReadyAppearing(view)) {\n                    setViewAppearing(view);\n                } else if (view.getTop() <= scrHeight && view.getBottom() >= scrHeight && isViewReadyDisAppearing(view)) {\n                    setViewDisappearing(view);\n                }\n            }\n\n            if (view.getTop() >= 0 && view.getBottom() <= scrHeight) {\n                // fully in screen\n\n                if (isViewReadyAppearing(view)) {\n                    setViewAppearing(view);\n\n                } else if (isViewReadyAppeared(view)) {\n                    setViewAppeared(view);\n                }\n            } else if (view.getBottom() <= 0 || view.getTop() >= scrHeight) {\n                // not in screen\n                if (isViewReadyDisAppearing(view)) {\n                    setViewDisappearing(view);\n\n                } else if (isViewReadyDisAppeared(view)) {\n                    setViewDisappeared(view);\n                }\n\n            }\n        }\n    }\n\n    private STATUS getViewStatus(View view) {\n        if (!mViewStatusMap.containsKey(view)) {\n            mViewStatusMap.put(view, STATUS.DISAPPEARED);\n            return STATUS.DISAPPEARED;\n        }\n        return mViewStatusMap.get(view);\n    }\n\n    private void setViewstatus(View view, STATUS status) {\n        mViewStatusMap.put(view, status);\n    }\n\n    private boolean isViewReadyAppearing(View view) {\n        return getViewStatus(view) == STATUS.DISAPPEARED;\n    }\n\n    private void setViewAppearing(View view) {\n        if (getViewStatus(view) == STATUS.APPEARING) {\n            return;\n        }\n\n        setViewstatus(view, STATUS.APPEARING);\n        if (null != mViewLifeCycleListener) {\n            mViewLifeCycleListener.onAppearing(view);\n        }\n    }\n\n    private boolean isViewReadyAppeared(View view) {\n        return getViewStatus(view) == STATUS.APPEARING;\n    }\n\n    private void setViewAppeared(View view) {\n        if (getViewStatus(view) == STATUS.APPEARED) {\n            return;\n        }\n        setViewstatus(view, STATUS.APPEARED);\n        if (null != mViewLifeCycleListener) {\n            mViewLifeCycleListener.onAppeared(view);\n        }\n    }\n\n    private boolean isViewReadyDisAppearing(View view) {\n        return getViewStatus(view) == STATUS.APPEARED;\n    }\n\n    private void setViewDisappearing(View view) {\n        if (getViewStatus(view) == STATUS.DISAPPEARING) {\n            return;\n        }\n\n        setViewstatus(view, STATUS.DISAPPEARING);\n        if (null != mViewLifeCycleListener) {\n            mViewLifeCycleListener.onDisappearing(view);\n        }\n    }\n\n    private boolean isViewReadyDisAppeared(View view) {\n        return getViewStatus(view) == STATUS.DISAPPEARING;\n    }\n\n    private void setViewDisappeared(View view) {\n        if (getViewStatus(view) == STATUS.DISAPPEARED) {\n            return;\n        }\n        setViewstatus(view, STATUS.DISAPPEARED);\n        if (null != mViewLifeCycleListener) {\n            mViewLifeCycleListener.onDisappeared(view);\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/extend/ViewLifeCycleListener.java",
    "content": "package com.alibaba.android.vlayout.extend;\n\nimport android.view.View;\n\npublic interface ViewLifeCycleListener {\n    void onAppearing(View view);\n\n    void onDisappearing(View view);\n\n    void onAppeared(View view);\n\n    void onDisappeared(View view);\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/AbstractFullFillLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport android.graphics.Rect;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.RecyclerView.Recycler;\nimport android.support.v7.widget.RecyclerView.State;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup.LayoutParams;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.Range;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n\n/**\n * 'FullFill' means that all child views it is responsible for should be layouted or recycled at once.\n * Otherwise the inner layout state will enter into chaos.\n */\npublic abstract class AbstractFullFillLayoutHelper extends BaseLayoutHelper {\n\n    private static final String TAG = \"FullFillLayoutHelper\";\n\n    protected boolean hasHeader = false;\n\n    protected boolean hasFooter = false;\n\n    protected boolean mLayoutWithAnchor = false;\n\n    protected int getAllChildren(View[] toFill,\n                                 RecyclerView.Recycler recycler, LayoutStateWrapper layoutState,\n                                 LayoutChunkResult result, LayoutManagerHelper helper) {\n\n        final boolean layingOutInPrimaryDirection = layoutState.getItemDirection() == LayoutStateWrapper.ITEM_DIRECTION_TAIL;\n\n        int count = 0;\n        int firstPos = layingOutInPrimaryDirection ? getRange().getLower() : getRange().getUpper();\n        final int curPos = layoutState.getCurrentPosition();\n\n        if (layingOutInPrimaryDirection ? (curPos > firstPos) : (curPos > firstPos)) {\n            // do ugly bug fix now\n            Log.w(TAG, \"Please handle strange order views carefully\");\n        }\n\n        while (count < toFill.length) {\n            if (isOutOfRange(layoutState.getCurrentPosition()))\n                break;\n\n            View view = nextView(recycler, layoutState, helper, result);\n            if (view == null) {\n                break;\n            }\n\n            toFill[count] = view;\n\n            // normalize layout params\n            LayoutParams layoutParams = view.getLayoutParams();\n            if (layoutParams == null) {\n                view.setLayoutParams(generateDefaultLayoutParams());\n            } else if (!checkLayoutParams(layoutParams)) {\n                view.setLayoutParams(generateLayoutParams(layoutParams));\n            }\n\n            count++;\n        }\n\n        if (count > 0 && !layingOutInPrimaryDirection) {\n            // reverse array\n            int s = 0, e = count - 1;\n            while (s < e) {\n                View temp = toFill[s];\n                toFill[s] = toFill[e];\n                toFill[e] = temp;\n                s++;\n                e--;\n            }\n        }\n\n        return count;\n    }\n\n    private LayoutManagerHelper mTempLayoutHelper;\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        mTempLayoutHelper = helper;\n\n        doLayoutView(recycler, state, layoutState, result, helper);\n\n        mTempLayoutHelper = null;\n    }\n\n    protected void doLayoutView(RecyclerView.Recycler recycler, RecyclerView.State state,\n                                LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n\n    }\n\n    // Mock measure/layout process in ViewGroup\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\n    }\n\n    protected void onLayout(boolean changed, int l, int t, int r, int b) {\n\n    }\n\n    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {\n\n    }\n\n    @Override\n    public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        if (anchorInfo.layoutFromEnd) {\n            if (!hasFooter) {\n                anchorInfo.position = getRange().getUpper();\n            } else {\n                //keep the previously calculated position\n            }\n        } else {\n            if (!hasHeader) {\n                anchorInfo.position = getRange().getLower();\n            } else {\n                //keep the previously calculated position\n            }\n        }\n        mLayoutWithAnchor = true;\n    }\n\n    @Override\n    public void afterLayout(Recycler recycler, State state, int startPosition, int endPosition, int scrolled,\n        LayoutManagerHelper helper) {\n        super.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n        mLayoutWithAnchor = false;\n    }\n\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            if (isLayoutEnd) {\n                return mMarginBottom + mPaddingBottom;\n            } else {\n                return -mMarginTop - mPaddingTop;\n            }\n        } else {\n            if (isLayoutEnd) {\n                return mMarginRight + mPaddingRight;\n            } else {\n                return -mMarginLeft - mPaddingLeft;\n            }\n        }\n    }\n\n    protected boolean checkLayoutParams(LayoutParams p) {\n        return true;\n    }\n\n    protected LayoutParams generateDefaultLayoutParams() {\n        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);\n    }\n\n    protected LayoutParams generateLayoutParams(LayoutParams p) {\n        return new LayoutParams(p);\n    }\n\n\n    @Override\n    public boolean isRecyclable(int childPos, int startIndex, int endIndex, LayoutManagerHelper helper, boolean fromStart) {\n        Range<Integer> range = getRange();\n        if (range.contains(childPos)) {\n            if (hasHeader && childPos == getRange().getLower()) {\n                return true;\n            }\n            if (hasFooter && childPos == getRange().getUpper()) {\n                return true;\n            }\n            Range<Integer> childRange = Range.create(range.getLower() + (hasHeader ? 1 : 0),\n                range.getUpper() - (hasFooter ? 1 : 0));\n            return Range.create(startIndex, endIndex).contains(childRange);\n        } else {\n            Log.w(TAG, \"Child item not match\");\n            return true;\n        }\n    }\n\n    public void setHasHeader(boolean hasHeader) {\n        this.hasHeader = hasHeader;\n    }\n\n    public void setHasFooter(boolean hasFooter) {\n        this.hasFooter = hasFooter;\n    }\n\n    protected void calculateRect(int mainAxisSize, Rect areaRect, LayoutStateWrapper layoutState, LayoutManagerHelper helper) {\n        if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n            areaRect.left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;\n            areaRect.right = helper.getContentWidth() - helper.getPaddingRight() - mMarginRight - mPaddingRight;\n\n            // whether this layout pass is layout to start or to end\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                // fill start, from bottom to top\n                areaRect.bottom = layoutState.getOffset() - (mLayoutWithAnchor ? 0 : (hasFooter ? 0 : mMarginBottom + mPaddingBottom));\n                areaRect.top = areaRect.bottom - mainAxisSize;\n            } else {\n                areaRect.top = layoutState.getOffset() + (mLayoutWithAnchor ? 0 : (hasHeader ? 0 : mMarginTop + mPaddingTop));\n                areaRect.bottom = areaRect.top + mainAxisSize;\n            }\n        } else {\n            areaRect.top = helper.getPaddingTop() + mMarginTop + mPaddingTop;\n            areaRect.bottom = helper.getContentHeight() - helper.getPaddingBottom() - mMarginBottom - mPaddingBottom;\n\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                areaRect.right = layoutState.getOffset() - (mLayoutWithAnchor ? 0 :(hasFooter ? 0 : mMarginRight + mPaddingRight));\n                areaRect.left = areaRect.right - mainAxisSize;\n            } else {\n                areaRect.left = layoutState.getOffset() + (mLayoutWithAnchor ? 0 : (hasHeader ? 0 : mMarginLeft + mPaddingLeft));\n                areaRect.right = areaRect.left + mainAxisSize;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/BaseLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.R;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport android.graphics.Rect;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\n\n/**\n * {@link com.alibaba.android.vlayout.LayoutHelper} that provides basic methods\n */\npublic abstract class BaseLayoutHelper extends MarginLayoutHelper {\n\n    private static final String TAG = \"BaseLayoutHelper\";\n\n    public static boolean DEBUG = false;\n\n    protected Rect mLayoutRegion = new Rect();\n\n    View mLayoutView;\n\n    int mBgColor;\n\n    float mAspectRatio = Float.NaN;\n\n    public BaseLayoutHelper() {\n\n    }\n\n    @Override\n    public boolean isFixLayout() {\n        return false;\n    }\n\n    public int getBgColor() {\n        return this.mBgColor;\n    }\n\n    /**\n     * Set backgroundColor for LayoutView\n     *\n     * @param bgColor\n     */\n    public void setBgColor(int bgColor) {\n        this.mBgColor = bgColor;\n    }\n\n    public void setAspectRatio(float aspectRatio) {\n        this.mAspectRatio = aspectRatio;\n    }\n\n    public float getAspectRatio() {\n        return mAspectRatio;\n    }\n\n    private int mItemCount = 0;\n\n    /**\n     * The number of items in current layout\n     *\n     * @return the number of child views\n     */\n    @Override\n    public int getItemCount() {\n        return mItemCount;\n    }\n\n    @Override\n    public void setItemCount(int itemCount) {\n        this.mItemCount = itemCount;\n    }\n\n    /**\n     * Retrieve next view and add it into layout, this is to make sure that view are added by order\n     *\n     * @param recycler    recycler generate views\n     * @param layoutState current layout state\n     * @param helper      helper to add views\n     * @param result      chunk result to tell layoutManager whether layout process goes end\n     * @return next view to render, null if no more view available\n     */\n    @Nullable\n    public final View nextView(RecyclerView.Recycler recycler, LayoutStateWrapper layoutState, LayoutManagerHelper helper, LayoutChunkResult result) {\n        View view = layoutState.next(recycler);\n        if (view == null) {\n            // if we are laying out views in scrap, this may return null which means there is\n            // no more items to layout.\n            if (DEBUG && !layoutState.hasScrapList()) {\n                throw new RuntimeException(\"received null view when unexpected\");\n            }\n            // if there is no more views can be retrieved, this layout process is finished\n            result.mFinished = true;\n            return null;\n        }\n\n        helper.addChildView(layoutState, view);\n        return view;\n    }\n\n\n    @Override\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n                             LayoutManagerHelper helper) {\n        if (DEBUG) {\n            Log.d(TAG, \"call beforeLayout() on \" + this.getClass().getSimpleName());\n        }\n\n\n        if (requireLayoutView()) {\n            if (mLayoutView != null) {\n                // TODO: recycle LayoutView\n                // helper.detachChildView(mLayoutView);\n            }\n        } else {\n            // if no layoutView is required, remove it\n            if (mLayoutView != null) {\n                if (mLayoutViewUnBindListener != null) {\n                    mLayoutViewUnBindListener.onUnbind(mLayoutView, this);\n                }\n                helper.removeChildView(mLayoutView);\n                mLayoutView = null;\n            }\n        }\n    }\n\n    /**\n     * Tell whether the scrolled value is valid, if not, means it's a layout processing without scrolling\n     *\n     * @param scrolled value of how many pixels does scrolled\n     * @return true means during a scrolling process, false means during a layout process.\n     */\n    protected boolean isValidScrolled(int scrolled) {\n        return scrolled != Integer.MAX_VALUE && scrolled != Integer.MIN_VALUE;\n    }\n\n\n    @Override\n    public void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            int startPosition, int endPosition, int scrolled,\n                            LayoutManagerHelper helper) {\n        if (DEBUG) {\n            Log.d(TAG, \"call afterLayout() on \" + this.getClass().getSimpleName());\n        }\n\n\n        if (requireLayoutView()) {\n            if (isValidScrolled(scrolled) && mLayoutView != null) {\n                // initial layout do reset\n                mLayoutRegion.union(mLayoutView.getLeft(), mLayoutView.getTop(), mLayoutView.getRight(), mLayoutView.getBottom());\n            }\n\n\n            if (!mLayoutRegion.isEmpty()) {\n                if (isValidScrolled(scrolled)) {\n                    if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n                        mLayoutRegion.offset(0, -scrolled);\n                    } else {\n                        mLayoutRegion.offset(-scrolled, 0);\n                    }\n                }\n                int contentWidth = helper.getContentWidth();\n                int contentHeight = helper.getContentHeight();\n                if (helper.getOrientation() == VirtualLayoutManager.VERTICAL ?\n                        mLayoutRegion.intersects(0, -contentHeight / 4, contentWidth, contentHeight + contentHeight / 4) :\n                        mLayoutRegion.intersects(-contentWidth / 4, 0, contentWidth + contentWidth / 4, contentHeight)) {\n\n                    if (mLayoutView == null) {\n                        mLayoutView = helper.generateLayoutView();\n                        helper.addOffFlowView(mLayoutView, true);\n                    }\n                    //finally fix layoutRegion's height and with here to avoid visual blank\n                    if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n                        mLayoutRegion.left = helper.getPaddingLeft() + mMarginLeft;\n                        mLayoutRegion.right = helper.getContentWidth() - helper.getPaddingRight() - mMarginRight;\n                    } else {\n                        mLayoutRegion.top = helper.getPaddingTop() + mMarginTop;\n                        mLayoutRegion.bottom = helper.getContentHeight() - helper.getPaddingBottom() - mMarginBottom;\n                    }\n\n                    bindLayoutView(mLayoutView);\n                    return;\n                } else {\n                    mLayoutRegion.set(0, 0, 0, 0);\n                    if (mLayoutView != null) {\n                        mLayoutView.layout(0, 0, 0, 0);\n                    }\n                }\n            }\n        }\n\n        if (mLayoutView != null) {\n            if (mLayoutViewUnBindListener != null) {\n                mLayoutViewUnBindListener.onUnbind(mLayoutView, this);\n            }\n            helper.removeChildView(mLayoutView);\n            mLayoutView = null;\n        }\n\n    }\n\n    @Override\n    public void adjustLayout(int startPosition, int endPosition, LayoutManagerHelper helper) {\n        if (requireLayoutView()) {\n            View refer = null;\n            Rect tempRect = new Rect();\n            final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n            for (int i = 0; i < helper.getChildCount(); i++) {\n                refer = helper.getChildAt(i);\n                int anchorPos = helper.getPosition(refer);\n                if (getRange().contains(anchorPos)) {\n                    if (refer.getVisibility() == View.GONE) {\n                        tempRect.setEmpty();\n                    } else {\n                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                            refer.getLayoutParams();\n                        if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n                            tempRect.union(helper.getDecoratedLeft(refer) - params.leftMargin,\n                                orientationHelper.getDecoratedStart(refer),\n                                helper.getDecoratedRight(refer) + params.rightMargin,\n                                orientationHelper.getDecoratedEnd(refer));\n                        } else {\n                            tempRect.union(orientationHelper.getDecoratedStart(refer),\n                                helper.getDecoratedTop(refer) - params.topMargin, orientationHelper.getDecoratedEnd(refer),\n                                helper.getDecoratedBottom(refer) + params.bottomMargin);\n                        }\n                    }\n                }\n            }\n            if (!tempRect.isEmpty()) {\n                mLayoutRegion.set(tempRect.left - mPaddingLeft, tempRect.top - mPaddingTop,\n                    tempRect.right + mPaddingRight, tempRect.bottom + mPaddingBottom);\n            } else {\n                mLayoutRegion.setEmpty();\n            }\n            if (mLayoutView != null) {\n                mLayoutView.layout(mLayoutRegion.left, mLayoutRegion.top, mLayoutRegion.right, mLayoutRegion.bottom);\n            }\n        }\n    }\n\n    /**\n     * Called when {@link com.alibaba.android.vlayout.LayoutHelper} get dropped\n     * Do default clean jobs defined by framework\n     *\n     * @param helper LayoutManagerHelper\n     */\n    @Override\n    public final void clear(LayoutManagerHelper helper) {\n        // remove LayoutViews if there is one\n        if (mLayoutView != null) {\n            if (mLayoutViewUnBindListener != null) {\n                mLayoutViewUnBindListener.onUnbind(mLayoutView, this);\n            }\n            helper.removeChildView(mLayoutView);\n            mLayoutView = null;\n        }\n\n        // call user defined\n        onClear(helper);\n    }\n\n    /**\n     * Called when {@link com.alibaba.android.vlayout.LayoutHelper} get dropped, do clean custom jobs\n     *\n     * @param helper\n     */\n    protected void onClear(LayoutManagerHelper helper) {\n\n    }\n\n    /**\n     * @return\n     */\n    @Override\n    public boolean requireLayoutView() {\n        return mBgColor != 0 || mLayoutViewBindListener != null;\n    }\n\n    public abstract void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n                                     LayoutStateWrapper layoutState, LayoutChunkResult result,\n                                     LayoutManagerHelper helper);\n\n\n    @Override\n    public void doLayout(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        layoutViews(recycler, state, layoutState, result, helper);\n    }\n\n    /**\n     * Helper function which do layout children and also update layoutRegion\n     * but it won't consider margin in layout, so you need take care of margin if you apply margin to your layoutView\n     *\n     * @param child  child that will be laid\n     * @param left   left position\n     * @param top    top position\n     * @param right  right position\n     * @param bottom bottom position\n     * @param helper layoutManagerHelper, help to lay child\n     */\n    protected void layoutChildWithMargin(final View child, int left, int top, int right, int bottom, @NonNull LayoutManagerHelper helper) {\n        layoutChildWithMargin(child, left, top, right, bottom, helper, false);\n    }\n\n    protected void layoutChildWithMargin(final View child, int left, int top, int right, int bottom, @NonNull LayoutManagerHelper helper, boolean addLayoutRegionWithMargin) {\n        helper.layoutChildWithMargins(child, left, top, right, bottom);\n        if (requireLayoutView()) {\n            if (addLayoutRegionWithMargin) {\n                mLayoutRegion\n                        .union(left - mPaddingLeft - mMarginLeft, top - mPaddingTop - mMarginTop,\n                                right + mPaddingRight + mMarginRight,\n                                bottom + mPaddingBottom + mMarginBottom);\n            } else {\n                mLayoutRegion.union(left - mPaddingLeft, top - mPaddingTop, right + mPaddingRight, bottom + mPaddingBottom);\n            }\n        }\n\n    }\n\n    /**\n     * Helper function which do layout children and also update layoutRegion\n     *\n     * @param child  child that will be laid\n     * @param left   left position\n     * @param top    top position\n     * @param right  right position\n     * @param bottom bottom position\n     * @param helper layoutManagerHelper, help to lay child\n     */\n    protected void layoutChild(final View child, int left, int top, int right, int bottom, @NonNull LayoutManagerHelper helper) {\n        layoutChild(child, left, top, right, bottom, helper, false);\n    }\n\n    protected void layoutChild(final View child, int left, int top, int right, int bottom, @NonNull LayoutManagerHelper helper, boolean addLayoutRegionWithMargin) {\n        helper.layoutChild(child, left, top, right, bottom);\n        if (requireLayoutView()) {\n            if (addLayoutRegionWithMargin) {\n                mLayoutRegion\n                        .union(left - mPaddingLeft - mMarginLeft, top - mPaddingTop - mMarginTop,\n                                right + mPaddingRight + mMarginRight,\n                                bottom + mPaddingBottom + mMarginBottom);\n            } else {\n                mLayoutRegion.union(left - mPaddingLeft, top - mPaddingTop, right + mPaddingRight, bottom + mPaddingBottom);\n            }\n        }\n\n    }\n\n    /**\n     * Listener to handle LayoutViews, like bgImage\n     */\n    public interface LayoutViewBindListener {\n        void onBind(View layoutView, BaseLayoutHelper baseLayoutHelper);\n    }\n\n    /**\n     * Listener to handle LayoutViews, like bgImage\n     */\n    public interface LayoutViewUnBindListener {\n        void onUnbind(View layoutView, BaseLayoutHelper baseLayoutHelper);\n    }\n\n\n    public interface LayoutViewHelper {\n\n        /**\n         * Implement it by maintaining a map between layoutView and image url or setting a unique tag to view. It's up to your choice.\n         * @param layoutView view ready to be binded with an image\n*        * @param id layoutView's identifier\n         */\n        void onBindViewSuccess(View layoutView, String id);\n    }\n\n    private LayoutViewUnBindListener mLayoutViewUnBindListener;\n\n    private LayoutViewBindListener mLayoutViewBindListener;\n\n    /**\n     * Helper to decide whether call {@link LayoutViewBindListener#onBind(View, BaseLayoutHelper)}.\n     * Here is a performance issue: {@link LayoutViewBindListener#onBind(View, BaseLayoutHelper)} is called during layout phase,\n     * when binding image to it would cause view tree to relayout, then the same  {@link LayoutViewBindListener#onBind(View, BaseLayoutHelper)} would be called.\n     * User should provide enough information to tell LayoutHelper whether image has been bind success.\n     * If image has been successfully binded , no more dead loop happens.\n     *\n     * Of course you can handle this logic by yourself and ignore this helper.\n     */\n    public static class DefaultLayoutViewHelper implements LayoutViewBindListener, LayoutViewUnBindListener, LayoutViewHelper {\n\n        private final LayoutViewBindListener mLayoutViewBindListener;\n\n        private final LayoutViewUnBindListener mLayoutViewUnBindListener;\n\n        public DefaultLayoutViewHelper(\n            LayoutViewBindListener layoutViewBindListener,\n            LayoutViewUnBindListener layoutViewUnBindListener) {\n            mLayoutViewBindListener = layoutViewBindListener;\n            mLayoutViewUnBindListener = layoutViewUnBindListener;\n        }\n\n        @Override\n        public void onBindViewSuccess(View layoutView, String id) {\n            layoutView.setTag(R.id.tag_layout_helper_bg, id);\n        }\n\n        @Override\n        public void onBind(View layoutView, BaseLayoutHelper baseLayoutHelper) {\n            if (layoutView.getTag(R.id.tag_layout_helper_bg) == null) {\n                if (mLayoutViewBindListener != null) {\n                    mLayoutViewBindListener.onBind(layoutView, baseLayoutHelper);\n                }\n            }\n        }\n\n        @Override\n        public void onUnbind(View layoutView, BaseLayoutHelper baseLayoutHelper) {\n            if (mLayoutViewUnBindListener != null) {\n                mLayoutViewUnBindListener.onUnbind(layoutView, baseLayoutHelper);\n            }\n            layoutView.setTag(R.id.tag_layout_helper_bg, null);\n        }\n    }\n\n    public void setLayoutViewHelper(DefaultLayoutViewHelper layoutViewHelper) {\n        mLayoutViewBindListener = layoutViewHelper;\n        mLayoutViewUnBindListener = layoutViewHelper;\n    }\n\n    /**\n     * Better to use {@link #setLayoutViewHelper(DefaultLayoutViewHelper)}\n     * @param bindListener\n     */\n    public void setLayoutViewBindListener(LayoutViewBindListener bindListener) {\n        mLayoutViewBindListener = bindListener;\n    }\n\n    /**\n     * Better to use {@link #setLayoutViewHelper(DefaultLayoutViewHelper)}\n     * @param layoutViewUnBindListener\n     */\n    public void setLayoutViewUnBindListener(\n            LayoutViewUnBindListener layoutViewUnBindListener) {\n        mLayoutViewUnBindListener = layoutViewUnBindListener;\n    }\n\n    @Override\n    public void bindLayoutView(@NonNull final View layoutView) {\n        layoutView.measure(View.MeasureSpec.makeMeasureSpec(mLayoutRegion.width(), View.MeasureSpec.EXACTLY),\n                View.MeasureSpec.makeMeasureSpec(mLayoutRegion.height(), View.MeasureSpec.EXACTLY));\n        layoutView.layout(mLayoutRegion.left, mLayoutRegion.top, mLayoutRegion.right, mLayoutRegion.bottom);\n        layoutView.setBackgroundColor(mBgColor);\n\n        if (mLayoutViewBindListener != null) {\n            mLayoutViewBindListener.onBind(layoutView, this);\n        }\n\n        // reset region rectangle\n        mLayoutRegion.set(0, 0, 0, 0);\n    }\n\n    protected void handleStateOnResult(LayoutChunkResult result, View view) {\n        if (view == null) {\n            return;\n        }\n\n        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();\n\n        // Consume the available space if the view is not removed OR changed\n        if (params.isItemRemoved() || params.isItemChanged()) {\n            result.mIgnoreConsumed = true;\n        }\n\n        // used when search a focusable view\n        result.mFocusable = result.mFocusable || view.isFocusable();\n\n    }\n\n    /**\n     * Helper methods to handle focus states for views\n     * @param result\n     * @param views\n     */\n    protected void handleStateOnResult(LayoutChunkResult result, View[] views) {\n        if (views == null) return;\n\n        for (int i = 0; i < views.length; i++) {\n            View view = views[i];\n            if (view == null) {\n                continue;\n            }\n            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();\n\n            // Consume the available space if the view is not removed OR changed\n            if (params.isItemRemoved() || params.isItemChanged()) {\n                result.mIgnoreConsumed = true;\n            }\n\n            // used when search a focusable view\n            result.mFocusable = result.mFocusable || view.isFocusable();\n\n            if (result.mFocusable && result.mIgnoreConsumed) {\n                break;\n            }\n        }\n    }\n\n    protected int computeStartSpace(LayoutManagerHelper helper, boolean layoutInVertical, boolean isLayoutEnd, boolean isOverLapMargin) {\n        int startSpace = 0;\n        LayoutHelper lastLayoutHelper = null;\n        if (helper instanceof VirtualLayoutManager) {\n            lastLayoutHelper = ((VirtualLayoutManager) helper).findNeighbourNonfixLayoutHelper(this, isLayoutEnd);\n        }\n        MarginLayoutHelper lastMarginLayoutHelper = null;\n\n        if (lastLayoutHelper != null && lastLayoutHelper instanceof MarginLayoutHelper) {\n            lastMarginLayoutHelper = (MarginLayoutHelper) lastLayoutHelper;\n        }\n        if (lastLayoutHelper == this)\n            return 0;\n\n        if (!isOverLapMargin) {\n            startSpace = layoutInVertical ? mMarginTop + mPaddingTop : mMarginLeft + mPaddingLeft;\n        } else {\n            int offset = 0;\n\n            if (lastMarginLayoutHelper == null) {\n                offset = layoutInVertical ? mMarginTop + mPaddingTop : mMarginLeft + mPaddingLeft;\n            } else {\n                offset = layoutInVertical\n                        ? (isLayoutEnd ? calGap(lastMarginLayoutHelper.mMarginBottom, mMarginTop) : calGap(lastMarginLayoutHelper.mMarginTop, mMarginBottom))\n                        : (isLayoutEnd ? calGap(lastMarginLayoutHelper.mMarginRight, mMarginLeft) : calGap(lastMarginLayoutHelper.mMarginLeft, mMarginRight));\n            }\n            //Log.e(\"huang\", \"computeStartSpace offset: \" + offset + \", isLayoutEnd: \" + isLayoutEnd + \", \" + this);\n            startSpace += layoutInVertical\n                    ? (isLayoutEnd ? mPaddingTop : mPaddingBottom)\n                    : (isLayoutEnd ? mPaddingLeft : mPaddingRight);\n\n            startSpace += offset;\n        }\n        return startSpace;\n    }\n\n    protected int computeEndSpace(LayoutManagerHelper helper, boolean layoutInVertical, boolean isLayoutEnd, boolean isOverLapMargin) {\n        int endSpace = layoutInVertical\n                ? mMarginBottom + mPaddingBottom : mMarginLeft + mPaddingLeft;\n        //Log.e(\"huang\", \"computeEndSpace offset: \" + endSpace + \", isLayoutEnd: \" + isLayoutEnd + \", \" + this);\n        //Log.e(\"huang\", \"===================\\n\\n\");\n        return endSpace;\n    }\n\n    private int calGap(int gap, int currGap) {\n        if (gap < currGap) {\n            return currGap - gap;\n        } else {\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/ColumnLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport java.util.Arrays;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\n\nimport android.graphics.Rect;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * Layout view in one line with the same of different cloumn width\n * <pre>\n * 1 column\n * -------------------------\n * |                       |\n * |                       |\n * |                       |\n * -------------------------\n *\n * 2 columns with same column width for each one\n * -------------------------\n * |           |           |\n * |           |           |\n * |           |           |\n * |           |           |\n * |           |           |\n * |           |           |\n * |           |           |\n * -------------------------\n *\n * 3 columns with different column widh for each one\n * -------------------------\n * |     |         |       |\n * |     |         |       |\n * |     |         |       |\n * |     |         |       |\n * |     |         |       |\n * |     |         |       |\n * |     |         |       |\n * -------------------------\n *\n * </pre>\n */\npublic class ColumnLayoutHelper extends AbstractFullFillLayoutHelper {\n\n    private View[] mEqViews;\n\n    private View[] mViews;\n\n    private Rect mTempArea = new Rect();\n\n    private float[] mWeights = new float[0];\n\n    public void setWeights(float[] weights) {\n        if (weights != null) {\n            this.mWeights = Arrays.copyOf(weights, weights.length);\n        } else {\n            this.mWeights = new float[0];\n        }\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, VirtualLayoutManager.LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final int itemCount = getItemCount();\n\n        if (mViews == null || mViews.length != itemCount) {\n            mViews = new View[itemCount];\n        }\n\n        if (mEqViews == null || mEqViews.length != itemCount) {\n            mEqViews = new View[itemCount];\n        } else {\n            Arrays.fill(mEqViews, null);\n        }\n\n\n        final int count = getAllChildren(mViews, recycler, layoutState, result, helper);\n\n        if (layoutInVertical) {\n            // TODO: only handle vertical layout now\n            int maxVMargin = 0;\n            int lastHMargin = 0;\n            int totalMargin = 0;\n\n            for (int i = 0; i < count; i++) {\n                View view = mViews[i];\n                ViewGroup.LayoutParams layoutParams = view.getLayoutParams();\n                if (layoutParams instanceof RecyclerView.LayoutParams) {\n                    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) layoutParams;\n\n                    params.leftMargin = Math.max(lastHMargin, params.leftMargin);\n\n                    totalMargin += params.leftMargin;\n\n                    if (i != count - 1) {\n                        // not last item\n                        lastHMargin = params.rightMargin;\n                        params.rightMargin = 0;\n                    } else {\n                        totalMargin += params.rightMargin;\n                    }\n                    maxVMargin = Math.max(maxVMargin, params.topMargin + params.bottomMargin);\n                }\n            }\n\n            final int totalWidth = helper.getContentWidth() - helper.getPaddingLeft() - helper\n                    .getPaddingRight() - getHorizontalMargin() - getHorizontalPadding();\n            final int availableWidth = totalWidth - totalMargin;\n\n            int usedWidth = 0;\n            int minHeight = Integer.MAX_VALUE;\n\n            int uniformHeight = -1;\n            if (!Float.isNaN(mAspectRatio)) {\n                uniformHeight = (int) (totalWidth / mAspectRatio + 0.5f);\n            }\n\n\n            int eqSize = 0;\n\n            for (int i = 0; i < count; i++) {\n                View view = mViews[i];\n                VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n                int heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(),\n                        uniformHeight > 0 ? uniformHeight : params.height, true);\n                if (mWeights != null && i < mWeights.length && !Float.isNaN(mWeights[i]) && mWeights[i] >= 0) {\n\n                    // calculate width with weight in percentage\n                    int resizeWidth = (int) (mWeights[i] * 1.0f / 100 * availableWidth + 0.5f);\n\n                    if (!Float.isNaN(params.mAspectRatio)) {\n                        int specialHeight = (int) (resizeWidth / params.mAspectRatio + 0.5f);\n                        heightSpec = View.MeasureSpec\n                                .makeMeasureSpec(specialHeight, View.MeasureSpec.EXACTLY);\n                    }\n\n                    helper.measureChildWithMargins(view, View.MeasureSpec.makeMeasureSpec(resizeWidth, View.MeasureSpec.EXACTLY), heightSpec);\n\n                    // add width into usedWidth\n                    usedWidth += resizeWidth;\n\n                    // find minHeight\n                    minHeight = Math.min(minHeight, view.getMeasuredHeight());\n                } else {\n                    mEqViews[eqSize++] = view;\n                }\n            }\n\n\n            for (int i = 0; i < eqSize; i++) {\n                View view = mEqViews[i];\n                VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n                int heightSpec;\n                int resizeWidth = (int) ((availableWidth - usedWidth) * 1.0f / eqSize + 0.5f);\n                if (!Float.isNaN(params.mAspectRatio)) {\n                    int specialHeight = (int) (resizeWidth / params.mAspectRatio + 0.5f);\n                    heightSpec = View.MeasureSpec\n                            .makeMeasureSpec(specialHeight, View.MeasureSpec.EXACTLY);\n                } else {\n                    heightSpec = helper.getChildMeasureSpec(\n                            helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(),\n                            uniformHeight > 0 ? uniformHeight : params.height, true);\n                }\n\n                //if cols' length is less than view's count, then remainder views share the rest space\n                helper.measureChildWithMargins(view,\n                        View.MeasureSpec.makeMeasureSpec(resizeWidth, View.MeasureSpec.EXACTLY),\n                        heightSpec);\n\n                // find minHeight\n                minHeight = Math.min(minHeight, view.getMeasuredHeight());\n            }\n\n            // uniform all views into min height\n            for (int i = 0; i < count; i++) {\n                View view = mViews[i];\n                if (view.getMeasuredHeight() != minHeight) {\n                    //noinspection ResourceType\n                    helper.measureChildWithMargins(view, View.MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(), View.MeasureSpec.EXACTLY),\n                            View.MeasureSpec.makeMeasureSpec(minHeight, View.MeasureSpec.EXACTLY));\n                }\n            }\n\n            result.mConsumed = minHeight + maxVMargin + getVerticalMargin() + getVerticalPadding();\n\n            calculateRect(minHeight + maxVMargin, mTempArea, layoutState, helper);\n\n            // do layout\n            int left = mTempArea.left;\n            for (int i = 0; i < count; i++) {\n                View view = mViews[i];\n                int top = mTempArea.top, bottom = mTempArea.bottom;\n\n                int right = left + orientationHelper.getDecoratedMeasurementInOther(view);\n\n                layoutChildWithMargin(view, left, top, right, bottom, helper);\n\n                left = right;\n            }\n\n        }\n\n        Arrays.fill(mViews, null);\n        Arrays.fill(mEqViews, null);\n    }\n\n    @Override\n    public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        if (anchorInfo.layoutFromEnd) {\n            anchorInfo.position = getRange().getUpper();\n        } else {\n            anchorInfo.position = getRange().getLower();\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/DefaultLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutHelper;\n\n/**\n * Created by villadora on 15/8/12.\n */\npublic class DefaultLayoutHelper extends LinearLayoutHelper {\n\n    public static LayoutHelper newHelper(int itemCount) {\n        DefaultLayoutHelper helper = new DefaultLayoutHelper();\n        helper.setItemCount(itemCount);\n        return helper;\n    }\n\n    @Override\n    public boolean isOutOfRange(int position) {\n        return false;\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/FixAreaAdjuster.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\n/**\n * Used to give an offset for four edges\n */\npublic class FixAreaAdjuster {\n\n    public final int left;\n    public final int top;\n    public final int right;\n    public final int bottom;\n\n    public static final FixAreaAdjuster mDefaultAdjuster = new FixAreaAdjuster(0, 0, 0, 0);\n\n    public FixAreaAdjuster(int left, int top, int right, int bottom) {\n        this.left = left;\n        this.top = top;\n        this.right = right;\n        this.bottom = bottom;\n\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/FixAreaLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\n\nimport android.view.View;\nimport android.view.ViewPropertyAnimator;\n\n/**\n * LayoutHelper that will be located as fix position\n */\npublic abstract class FixAreaLayoutHelper extends BaseLayoutHelper {\n    protected FixAreaAdjuster mAdjuster = FixAreaAdjuster.mDefaultAdjuster;\n\n    protected FixViewAnimatorHelper mFixViewAnimatorHelper;\n\n    public void setAdjuster(FixAreaAdjuster adjuster) {\n        this.mAdjuster = adjuster;\n    }\n\n    public void setFixViewAnimatorHelper(\n            FixViewAnimatorHelper fixViewAnimatorHelper) {\n        mFixViewAnimatorHelper = fixViewAnimatorHelper;\n    }\n\n    @Override\n    public void adjustLayout(int startPosition, int endPosition, LayoutManagerHelper helper) {\n\n    }\n\n    public boolean isFixLayout() {\n        return true;\n    }\n\n    public interface FixViewAnimatorHelper {\n\n        ViewPropertyAnimator onGetFixViewAppearAnimator(View fixView);\n\n        ViewPropertyAnimator onGetFixViewDisappearAnimator(View fixView);\n\n    }\n\n    @Override\n    public int computeMarginStart(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        return 0;\n    }\n\n    @Override\n    public int computeMarginEnd(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        return 0;\n    }\n\n    @Override\n    public int computePaddingStart(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        return 0;\n    }\n\n    @Override\n    public int computePaddingEnd(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/FixLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup.LayoutParams;\nimport android.view.ViewPropertyAnimator;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n\n/**\n * LayoutHelper that will be located as fix position. The appearance and disappearance are able to\n * transit with animation.<br />\n * Animation sample: <br />\n *<blockquote>\n * <pre>\n *     {@code\n * setFixViewAnimatorHelper(new FixViewAnimatorHelper() {\n * @Override\n * public ViewPropertyAnimator onGetFixViewAppearAnimator(View fixView) {\n * int height = fixView.getMeasuredHeight();\n * fixView.setTranslationY(-height);\n * return fixView.animate().translationYBy(height).alpha(1.0f).setDuration(500);\n * }\n *\n * @Override\n * public ViewPropertyAnimator onGetFixViewDisappearAnimator(View fixView) {\n * int height = fixView.getMeasuredHeight();\n * return fixView.animate().translationYBy(-height).alpha(0.0f).setDuration(500);\n * }\n * });\n *     }\n * </pre>\n *</blockquote>\n *\n * Created by villadora on 15/8/18.\n */\npublic class FixLayoutHelper extends FixAreaLayoutHelper {\n\n    private static final String TAG = \"FixLayoutHelper\";\n\n    public static final int TOP_LEFT = 0;\n\n    public static final int TOP_RIGHT = 1;\n\n    public static final int BOTTOM_LEFT = 2;\n\n    public static final int BOTTOM_RIGHT = 3;\n\n    private int mPos = -1;\n\n    private int mAlignType = TOP_LEFT;\n\n    protected int mX = 0;\n\n    protected int mY = 0;\n\n    private boolean mSketchMeasure = false;\n\n    protected View mFixView = null;\n\n    protected boolean mDoNormalHandle = false;\n\n    private boolean mShouldDrawn = true;\n\n    private boolean isAddFixViewImmediately = false;\n\n    private boolean isRemoveFixViewImmediately = true;\n\n    private FixViewAppearAnimatorListener\n            mFixViewAppearAnimatorListener = new FixViewAppearAnimatorListener();\n\n    private FixViewDisappearAnimatorListener\n            mFixViewDisappearAnimatorListener = new FixViewDisappearAnimatorListener();\n\n    public FixLayoutHelper(int x, int y) {\n        this(TOP_LEFT, x, y);\n    }\n\n    public FixLayoutHelper(int alignType, int x, int y) {\n        this.mAlignType = alignType;\n        this.mX = x;\n        this.mY = y;\n        setItemCount(1);\n    }\n\n    @Override\n    public void setItemCount(int itemCount) {\n        if (itemCount > 0) {\n            super.setItemCount(1);\n        } else {\n            super.setItemCount(0);\n        }\n    }\n\n\n    /**\n     * The margins in FixLayoutHelper are disabled\n     */\n    @Override\n    public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin) {\n    }\n\n\n    public void setX(int x) {\n        this.mX = x;\n    }\n\n    public void setY(int y) {\n        this.mY = y;\n    }\n\n    public void setAlignType(int alignType) {\n        this.mAlignType = alignType;\n    }\n\n\n    public void setSketchMeasure(boolean sketchMeasure) {\n        mSketchMeasure = sketchMeasure;\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p/>\n     * Only start is used, use should not use this measured\n     *\n     * @param start position of items handled by this layoutHelper\n     * @param end   will be ignored by {@link ScrollFixLayoutHelper}\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        this.mPos = start;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n            VirtualLayoutManager.LayoutStateWrapper layoutState, LayoutChunkResult result,\n            final LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        if (!mShouldDrawn) {\n            layoutState.skipCurrentPosition();\n            return;\n        }\n\n        // find view in currentPosition\n        View view = mFixView;\n        if (view == null) {\n            view = layoutState.next(recycler);\n        } else {\n            layoutState.skipCurrentPosition();\n        }\n\n        if (view == null) {\n            result.mFinished = true;\n            return;\n        }\n\n        mDoNormalHandle = state.isPreLayout();\n\n        if (mDoNormalHandle) {\n            // in PreLayout do normal layout\n            helper.addChildView(layoutState, view);\n        }\n\n        mFixView = view;\n\n        doMeasureAndLayout(view, helper);\n\n        result.mConsumed = 0;\n        result.mIgnoreConsumed = true;\n\n        handleStateOnResult(result, view);\n\n    }\n\n    @Override\n    public boolean requireLayoutView() {\n        return false;\n    }\n\n    @Override\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n            LayoutManagerHelper helper) {\n        super.beforeLayout(recycler, state, helper);\n\n        if (mFixView != null && helper.isViewHolderUpdated(mFixView)) {\n            // recycle view for later usage\n            helper.removeChildView(mFixView);\n            recycler.recycleView(mFixView);\n            mFixView = null;\n            isAddFixViewImmediately = true;\n        }\n\n        mDoNormalHandle = false;\n    }\n\n    @Override\n    public void afterLayout(final RecyclerView.Recycler recycler, RecyclerView.State state,\n            int startPosition, int endPosition, int scrolled,\n            final LayoutManagerHelper helper) {\n        super.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n\n        // disabled if mPos is negative number\n        if (mPos < 0) {\n            return;\n        }\n\n        if (mDoNormalHandle && state.isPreLayout()) {\n            if (mFixView != null) {\n//                Log.d(TAG, \"after layout doNormal removeView\");\n                helper.removeChildView(mFixView);\n                recycler.recycleView(mFixView);\n                isAddFixViewImmediately = false;\n            }\n\n            mFixView = null;\n            return;\n        }\n\n        // Not in normal flow\n        if (shouldBeDraw(helper, startPosition, endPosition, scrolled)) {\n            mShouldDrawn = true;\n            if (mFixView != null) {\n                // already capture in layoutViews phase\n                // if it's not shown on screen\n                if (mFixView.getParent() == null) {\n                    addFixViewWithAnimator(helper, mFixView);\n                } else {\n                    // helper.removeChildView(mFixView);\n                    helper.addFixedView(mFixView);\n                    isRemoveFixViewImmediately = false;\n                }\n            } else {\n                Runnable action = new Runnable() {\n                    @Override\n                    public void run() {\n                        mFixView = recycler.getViewForPosition(mPos);\n                        doMeasureAndLayout(mFixView, helper);\n                        if (isAddFixViewImmediately) {\n                            helper.addFixedView(mFixView);\n                            isRemoveFixViewImmediately = false;\n                        } else {\n                            addFixViewWithAnimator(helper, mFixView);\n                        }\n                    }\n                };\n                if (mFixViewDisappearAnimatorListener.isAnimating()) {\n                    mFixViewDisappearAnimatorListener.withEndAction(action);\n                } else {\n                    action.run();\n                }\n            }\n        } else {\n            mShouldDrawn = false;\n            if (mFixView != null) {\n                removeFixViewWithAnimator(recycler, helper, mFixView);\n                mFixView = null;\n            }\n        }\n\n    }\n\n    private void addFixViewWithAnimator(LayoutManagerHelper layoutManagerHelper, View fixView) {\n        if (mFixViewAnimatorHelper != null) {\n            ViewPropertyAnimator animator = mFixViewAnimatorHelper\n                    .onGetFixViewAppearAnimator(fixView);\n            if (animator != null) {\n                fixView.setVisibility(View.INVISIBLE);\n                layoutManagerHelper.addFixedView(fixView);\n                mFixViewAppearAnimatorListener.bindAction(layoutManagerHelper, fixView);\n                animator.setListener(mFixViewAppearAnimatorListener).start();\n            } else {\n                layoutManagerHelper.addFixedView(fixView);\n            }\n        } else {\n            layoutManagerHelper.addFixedView(fixView);\n        }\n        isRemoveFixViewImmediately = false;\n    }\n\n    private void removeFixViewWithAnimator(RecyclerView.Recycler recycler,\n            LayoutManagerHelper layoutManagerHelper, View fixView) {\n        if (!isRemoveFixViewImmediately && mFixViewAnimatorHelper != null) {\n            ViewPropertyAnimator animator = mFixViewAnimatorHelper\n                    .onGetFixViewDisappearAnimator(fixView);\n            if (animator != null) {\n                mFixViewDisappearAnimatorListener\n                        .bindAction(recycler, layoutManagerHelper, fixView);\n                animator.setListener(mFixViewDisappearAnimatorListener).start();\n                isAddFixViewImmediately = false;\n            } else {\n                layoutManagerHelper.removeChildView(fixView);\n                recycler.recycleView(fixView);\n                isAddFixViewImmediately = false;\n            }\n        } else {\n            layoutManagerHelper.removeChildView(fixView);\n            recycler.recycleView(fixView);\n            isAddFixViewImmediately = false;\n        }\n\n    }\n\n    /**\n     * Decide whether the view should be shown\n     *\n     * @param helper layoutManagerHelper\n     * @param startPosition the first visible position in RecyclerView\n     * @param endPosition   the last visible position in RecyclerView\n     * @param scrolled      how many pixels will be scrolled during this scrolling, 0 during\n     *                      layouting\n     * @return Whether the view in current layoutHelper should be shown\n     */\n    protected boolean shouldBeDraw(LayoutManagerHelper helper, int startPosition, int endPosition, int scrolled) {\n        return true;\n    }\n\n    @Override\n    public View getFixedView() {\n        return mFixView;\n    }\n\n    @Override\n    public void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n        if (mFixView != null) {\n            helper.removeChildView(mFixView);\n            helper.recycleView(mFixView);\n            mFixView.animate().cancel();\n            mFixView = null;\n            isAddFixViewImmediately = false;\n        }\n    }\n\n    private void doMeasureAndLayout(View view, LayoutManagerHelper helper) {\n        if (view == null || helper == null) {\n            return;\n        }\n\n        final VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view\n                .getLayoutParams();\n\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            final int widthSpec = helper.getChildMeasureSpec(\n                    helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight(),\n                    params.width >= 0 ? params.width\n                            : ((mSketchMeasure && layoutInVertical) ? LayoutParams.MATCH_PARENT\n                                    : LayoutParams.WRAP_CONTENT), false);\n            int heightSpec;\n            if (!Float.isNaN(params.mAspectRatio) && params.mAspectRatio > 0) {\n                heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper\n                                .getPaddingBottom(),\n                        (int) (View.MeasureSpec.getSize(widthSpec) / params.mAspectRatio + 0.5f),\n                        false);\n            } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n                heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper\n                                .getPaddingBottom(),\n                        (int) (View.MeasureSpec.getSize(widthSpec) / mAspectRatio + 0.5f),\n                        false);\n            } else {\n                heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper\n                                .getPaddingBottom(),\n                        params.height >= 0 ? params.height\n                                : ((mSketchMeasure && !layoutInVertical) ? LayoutParams.MATCH_PARENT\n                                        : LayoutParams.WRAP_CONTENT),\n                        false);\n            }\n\n            // do measurement\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        } else {\n            final int heightSpec = helper.getChildMeasureSpec(\n                    helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(),\n                            params.height >= 0 ? params.height\n                            : ((mSketchMeasure && !layoutInVertical) ? LayoutParams.MATCH_PARENT\n                                    : LayoutParams.WRAP_CONTENT), false);\n            int widthSpec;\n\n            if (!Float.isNaN(params.mAspectRatio) && params.mAspectRatio > 0) {\n                widthSpec = helper.getChildMeasureSpec(\n                        helper.getContentWidth() - helper.getPaddingLeft() - helper\n                                .getPaddingRight(),\n                        (int) (View.MeasureSpec.getSize(heightSpec) * params.mAspectRatio + 0.5f),\n                        false);\n            } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n                widthSpec = helper.getChildMeasureSpec(\n                        helper.getContentWidth() - helper.getPaddingLeft() - helper\n                                .getPaddingRight(),\n                        (int) (View.MeasureSpec.getSize(heightSpec) * mAspectRatio + 0.5f),\n                        false);\n            } else {\n                widthSpec = helper.getChildMeasureSpec(\n                        helper.getContentWidth() - helper.getPaddingLeft() - helper\n                                .getPaddingRight(),\n                        params.width >= 0 ? params.width\n                                : ((mSketchMeasure && layoutInVertical) ? LayoutParams.MATCH_PARENT\n                                        : LayoutParams.WRAP_CONTENT),\n                        false);\n            }\n\n\n            // do measurement\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        }\n\n        int left, top, right, bottom;\n        if (mAlignType == TOP_RIGHT) {\n            top = helper.getPaddingTop() + mY + mAdjuster.top;\n            right = helper.getContentWidth() - helper.getPaddingRight() - mX - mAdjuster.right;\n            left = right - params.leftMargin - params.rightMargin - view.getMeasuredWidth();\n            bottom = top + params.topMargin + params.bottomMargin + view.getMeasuredHeight();\n        } else if (mAlignType == BOTTOM_LEFT) {\n            left = helper.getPaddingLeft() + mX + mAdjuster.left;\n            bottom = helper.getContentHeight() - helper.getPaddingBottom() - mY - mAdjuster.bottom;\n            right = left + params.leftMargin + params.rightMargin + view.getMeasuredWidth();\n            top = bottom - view.getMeasuredHeight() - params.topMargin - params.bottomMargin;\n        } else if (mAlignType == BOTTOM_RIGHT) {\n            right = helper.getContentWidth() - helper.getPaddingRight() - mX - mAdjuster.right;\n            bottom = helper.getContentHeight() - helper.getPaddingBottom() - mY - mAdjuster.bottom;\n            left = right - params.leftMargin - params.rightMargin - view.getMeasuredWidth();\n            top = bottom - view.getMeasuredHeight() - params.topMargin - params.bottomMargin;\n        } else {\n            // TOP_LEFT\n            left = helper.getPaddingLeft() + mX + mAdjuster.left;\n            top = helper.getPaddingTop() + mY + mAdjuster.top;\n            right = left + (layoutInVertical ? orientationHelper\n                    .getDecoratedMeasurementInOther(view)\n                    : orientationHelper.getDecoratedMeasurement(view));\n            bottom = top + (layoutInVertical ? orientationHelper.getDecoratedMeasurement(view)\n                    : orientationHelper.getDecoratedMeasurementInOther(view));\n        }\n\n        layoutChildWithMargin(view, left, top, right, bottom, helper);\n    }\n\n    private static class FixViewAppearAnimatorListener extends AnimatorListenerAdapter {\n\n        private LayoutManagerHelper mLayoutManagerHelper;\n\n        private View mFixView;\n\n        public void bindAction(LayoutManagerHelper layoutManagerHelper, View fixView) {\n            mLayoutManagerHelper = layoutManagerHelper;\n            mFixView = fixView;\n        }\n\n        @Override\n        public void onAnimationStart(Animator animation) {\n            mFixView.setVisibility(View.VISIBLE);\n        }\n\n        @Override\n        public void onAnimationEnd(Animator animation) {\n        }\n    }\n\n    private static class FixViewDisappearAnimatorListener extends AnimatorListenerAdapter {\n\n        private boolean isAnimating;\n\n        private RecyclerView.Recycler mRecycler;\n\n        private LayoutManagerHelper mLayoutManagerHelper;\n\n        private View mFixView;\n\n        private Runnable mEndAction;\n\n        public void bindAction(RecyclerView.Recycler recycler,\n                LayoutManagerHelper layoutManagerHelper, View fixView) {\n            isAnimating = true;\n            mRecycler = recycler;\n            mLayoutManagerHelper = layoutManagerHelper;\n            mFixView = fixView;\n        }\n\n        @Override\n        public void onAnimationStart(Animator animation) {\n        }\n\n        @Override\n        public void onAnimationEnd(Animator animation) {\n            mLayoutManagerHelper.removeChildView(mFixView);\n            mRecycler.recycleView(mFixView);\n            isAnimating = false;\n            if (mEndAction != null) {\n                mEndAction.run();\n                mEndAction = null;\n            }\n        }\n\n        public boolean isAnimating() {\n            return isAnimating;\n        }\n\n        public void withEndAction(Runnable action) {\n            this.mEndAction = action;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/FloatLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\n\nimport android.animation.ObjectAnimator;\nimport android.graphics.Rect;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\nimport static com.alibaba.android.vlayout.layout.FixLayoutHelper.BOTTOM_LEFT;\nimport static com.alibaba.android.vlayout.layout.FixLayoutHelper.BOTTOM_RIGHT;\nimport static com.alibaba.android.vlayout.layout.FixLayoutHelper.TOP_RIGHT;\n\n/**\n * LayoutHelper that will be located as fix position at first layout, but its position could be changed by dragingg and dropping\n * <p>\n * Created by villadora on 15/8/28.\n */\npublic class FloatLayoutHelper extends FixAreaLayoutHelper {\n\n    private static final String TAG = \"FloatLayoutHelper\";\n\n    private int mTransitionX = 0;\n    private int mTransitionY = 0;\n    private boolean dragEnable;\n\n    public FloatLayoutHelper() {\n\n        this.dragEnable = true;\n    }\n\n    private int mZIndex = 1;\n\n    private int mPos = -1;\n\n    protected View mFixView = null;\n\n    protected boolean mDoNormalHandle = false;\n\n    private int mX = 0;\n    private int mY = 0;\n    private int mAlignType = FixLayoutHelper.TOP_LEFT;\n\n    public void setDefaultLocation(int x, int y) {\n        this.mX = x;\n        this.mY = y;\n    }\n\n    public void setX(int x) {\n        this.mX = x;\n    }\n\n    public void setY(int y) {\n        this.mY = y;\n    }\n\n    public void setAlignType(int alignType) {\n        this.mAlignType = alignType;\n    }\n\n    @Override\n    public void setItemCount(int itemCount) {\n        if (itemCount > 0) {\n            super.setItemCount(1);\n        } else {\n            super.setItemCount(0);\n        }\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p/>\n     * Only start is used, use should not use this measured\n     *\n     * @param start position of items handled by this layoutHelper\n     * @param end   should be the same as start\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        this.mPos = start;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            VirtualLayoutManager.LayoutStateWrapper layoutState, LayoutChunkResult result,\n                            final LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n\n        // find view in currentPosition\n        View view = mFixView;\n        if (view == null)\n            view = layoutState.next(recycler);\n        else {\n            layoutState.skipCurrentPosition();\n        }\n\n        if (view == null) {\n            result.mFinished = true;\n            return;\n        }\n\n        helper.getChildViewHolder(view).setIsRecyclable(false);\n\n        mDoNormalHandle = state.isPreLayout();\n\n        if (mDoNormalHandle) {\n            // in PreLayout do normal layout\n            helper.addChildView(layoutState, view);\n        }\n\n        mFixView = view;\n        mFixView.setClickable(true);\n\n        doMeasureAndLayout(view, helper);\n\n\n        result.mConsumed = 0;\n        result.mIgnoreConsumed = true;\n\n        handleStateOnResult(result, view);\n\n    }\n\n    @Override\n    public void setBgColor(int bgColor) {\n        // disable bgColor\n    }\n\n    @Override\n    public boolean requireLayoutView() {\n        return false;\n    }\n\n    @Override\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutManagerHelper helper) {\n        super.beforeLayout(recycler, state, helper);\n\n        if (mFixView != null && helper.isViewHolderUpdated(mFixView)) {\n            // remove view, not recycle\n            helper.removeChildView(mFixView);\n            helper.recycleView(mFixView);\n            mFixView.setOnTouchListener(null);\n            mFixView = null;\n        }\n\n        mDoNormalHandle = false;\n    }\n\n    @Override\n    public void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            int startPosition, int endPosition, int scrolled,\n                            LayoutManagerHelper helper) {\n        super.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n\n        // disabled if mPos is negative number\n        if (mPos < 0) return;\n\n        if (mDoNormalHandle) {\n            mFixView = null;\n            return;\n        }\n\n        // Not in normal flow\n        if (shouldBeDraw(startPosition, endPosition)) {\n            if (mFixView != null) {\n                // already capture in layoutViews phase\n                // if it's not shown on screen\n                // TODO: nested scrollBy\n                if (mFixView.getParent() == null) {\n                    helper.addFixedView(mFixView);\n                    if (dragEnable) {\n                        mFixView.setOnTouchListener(touchDragListener);\n                    }\n                    mFixView.setTranslationX(mTransitionX);\n                    mFixView.setTranslationY(mTransitionY);\n                } else {\n                    helper.showView(mFixView);\n                    // helper.removeChildView(mFixView);\n                    if (dragEnable) {\n                        mFixView.setOnTouchListener(touchDragListener);\n                    }\n                    helper.addFixedView(mFixView);\n                }\n            } else {\n                mFixView = recycler.getViewForPosition(mPos);\n                helper.getChildViewHolder(mFixView).setIsRecyclable(false);\n                doMeasureAndLayout(mFixView, helper);\n                helper.addFixedView(mFixView);\n                mFixView.setTranslationX(mTransitionX);\n                mFixView.setTranslationY(mTransitionY);\n                if (dragEnable) {\n                    mFixView.setOnTouchListener(touchDragListener);\n                }\n            }\n        }\n\n    }\n\n    protected boolean shouldBeDraw(int startPosition, int endPosition) {\n        return true;\n    }\n\n\n    @Nullable\n    @Override\n    public View getFixedView() {\n        return mFixView;\n    }\n\n    @Override\n    public void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n        if (mFixView != null) {\n            mFixView.setOnTouchListener(null);\n            helper.removeChildView(mFixView);\n            helper.recycleView(mFixView);\n            mFixView = null;\n        }\n    }\n\n    private void doMeasureAndLayout(View view, LayoutManagerHelper helper) {\n        if (view == null || helper == null) return;\n\n        final VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            final int widthSpec = helper.getChildMeasureSpec(\n                    helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight(), params.width, !layoutInVertical);\n            int heightSpec;\n            if (!Float.isNaN(params.mAspectRatio) && params.mAspectRatio > 0) {\n                heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(),\n                        (int) (View.MeasureSpec.getSize(widthSpec) / params.mAspectRatio + 0.5f), layoutInVertical);\n            } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n                heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(),\n                        (int) (View.MeasureSpec.getSize(widthSpec) / mAspectRatio + 0.5f), layoutInVertical);\n            } else {\n                heightSpec = helper.getChildMeasureSpec(\n                        helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(),\n                        params.height, layoutInVertical);\n            }\n            // do measurement, measure child without taking off margins, see https://github.com/alibaba/Tangram-Android/issues/81\n            helper.measureChild(view, widthSpec, heightSpec);\n        } else {\n            int widthSpec;\n            final int heightSpec = helper.getChildMeasureSpec(\n                    helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom(), params.height, layoutInVertical);\n            if (!Float.isNaN(params.mAspectRatio) && params.mAspectRatio > 0) {\n                widthSpec = helper.getChildMeasureSpec(\n                        helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight(),\n                        (int) (View.MeasureSpec.getSize(heightSpec) * params.mAspectRatio + 0.5f), !layoutInVertical);\n            } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n                widthSpec = helper.getChildMeasureSpec(\n                        helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight(),\n                        (int) (View.MeasureSpec.getSize(heightSpec) * mAspectRatio + 0.5f), !layoutInVertical);\n            } else {\n                widthSpec = helper.getChildMeasureSpec(\n                        helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight(),\n                        params.width, !layoutInVertical);\n            }\n            // do measurement,  measure child without taking off margins, see https://github.com/alibaba/Tangram-Android/issues/81\n            helper.measureChild(view, widthSpec, heightSpec);\n        }\n\n\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        int left, top, right, bottom;\n\n        if (mAlignType == TOP_RIGHT) {\n            top = helper.getPaddingTop() + mY + mAdjuster.top;\n            right = helper.getContentWidth() - helper.getPaddingRight() - mX - mAdjuster.right;\n            left = right - params.leftMargin - params.rightMargin - view.getMeasuredWidth();\n            bottom = top + params.topMargin + params.bottomMargin + view.getMeasuredHeight();\n        } else if (mAlignType == BOTTOM_LEFT) {\n            left = helper.getPaddingLeft() + mX + mAdjuster.left;\n            bottom = helper.getContentHeight() - helper.getPaddingBottom() - mY - mAdjuster.bottom;\n            right = left + params.leftMargin + params.rightMargin + view.getMeasuredWidth();\n            top = bottom - view.getMeasuredHeight() - params.topMargin - params.bottomMargin;\n        } else if (mAlignType == BOTTOM_RIGHT) {\n            right = helper.getContentWidth() - helper.getPaddingRight() - mX - mAdjuster.right;\n            bottom = helper.getContentHeight() - helper.getPaddingBottom() - mY - mAdjuster.bottom;\n            left = right - (layoutInVertical ? orientationHelper.getDecoratedMeasurementInOther(view) : orientationHelper.getDecoratedMeasurement(view));\n            top = bottom - (layoutInVertical ? orientationHelper.getDecoratedMeasurement(view) : orientationHelper.getDecoratedMeasurementInOther(view));\n        } else {\n            // TOP_LEFT\n            left = helper.getPaddingLeft() + mX + mAdjuster.left;\n            top = helper.getPaddingTop() + mY + mAdjuster.top;\n            right = left + (layoutInVertical ? orientationHelper.getDecoratedMeasurementInOther(view) : orientationHelper.getDecoratedMeasurement(view));\n            bottom = top + (layoutInVertical ? orientationHelper.getDecoratedMeasurement(view) : orientationHelper.getDecoratedMeasurementInOther(view));\n        }\n\n        if (left < helper.getPaddingLeft() + mAdjuster.left) {\n            left = helper.getPaddingLeft() + mAdjuster.left;\n            right = left + (layoutInVertical ? orientationHelper.getDecoratedMeasurementInOther(view) : orientationHelper.getDecoratedMeasurement(view));\n        }\n\n        if (right > helper.getContentWidth() - helper.getPaddingRight() - mAdjuster.right) {\n            right = helper.getContentWidth() - helper.getPaddingRight() - mAdjuster.right;\n            left = right - params.leftMargin - params.rightMargin - view.getMeasuredWidth();\n        }\n\n        if (top < helper.getPaddingTop() + mAdjuster.top) {\n            top = helper.getPaddingTop() + mAdjuster.top;\n            bottom = top + (layoutInVertical ? orientationHelper.getDecoratedMeasurement(view) : orientationHelper.getDecoratedMeasurementInOther(view));\n        }\n\n        if (bottom > helper.getContentHeight() - helper.getPaddingBottom() - mAdjuster.bottom) {\n            bottom = helper.getContentHeight() - helper.getPaddingBottom() - mAdjuster.bottom;\n            top = bottom - (layoutInVertical ? orientationHelper.getDecoratedMeasurement(view) : orientationHelper.getDecoratedMeasurementInOther(view));\n        }\n\n        layoutChildWithMargin(view, left, top, right, bottom, helper);\n    }\n\n\n    private final View.OnTouchListener touchDragListener = new View.OnTouchListener() {\n        private boolean isDrag;\n\n        private int mTouchSlop;\n\n        private int lastPosX;\n\n        private int lastPosY;\n\n        private int parentViewHeight;\n\n        private int parentViewWidth;\n\n        private int leftMargin;\n        private int topMargin;\n        private int rightMargin;\n        private int bottomMargin;\n\n\n        private final Rect parentLoction = new Rect();\n\n        @Override\n        public boolean onTouch(View v, MotionEvent event) {\n            // 做初始化\n            if (mTouchSlop == 0) {\n                final ViewConfiguration configuration = ViewConfiguration.get(v\n                        .getContext());\n                mTouchSlop = configuration.getScaledTouchSlop();\n                parentViewHeight = ((View) (v.getParent())).getHeight();\n                parentViewWidth = ((View) (v.getParent())).getWidth();\n                ((View) (v.getParent())).getGlobalVisibleRect(parentLoction);\n\n                ViewGroup.LayoutParams layoutParams = v.getLayoutParams();\n                if (layoutParams instanceof ViewGroup.MarginLayoutParams) {\n                    ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) layoutParams;\n                    leftMargin = params.leftMargin;\n                    topMargin = params.topMargin;\n                    rightMargin = params.rightMargin;\n                    bottomMargin = params.bottomMargin;\n                }\n\n            }\n\n            int action = event.getAction();\n            switch (action) {\n                case MotionEvent.ACTION_DOWN:\n                    isDrag = false;\n                    (v.getParent()).requestDisallowInterceptTouchEvent(true);\n                    lastPosX = (int) event.getX();\n                    lastPosY = (int) event.getY();\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    if (Math.abs(event.getX() - lastPosX) > mTouchSlop\n                            || Math.abs(event.getY() - lastPosY) > mTouchSlop) {\n                        isDrag = true;\n                    }\n                    if (isDrag) {\n                        int posX = (int) event.getRawX();\n                        int posY = (int) event.getRawY();\n                        int rParentX = posX - parentLoction.left;\n                        int rParentY = posY - parentLoction.top;\n                        int width = v.getWidth();\n                        int height = v.getHeight();\n                        int translateY = rParentY - height / 2;\n                        int translateX = rParentX - width / 2;\n                        int curTranslateX = translateX - v.getLeft() - leftMargin - mAdjuster.left;\n                        v.setTranslationX(curTranslateX);\n                        int curTranslateY = translateY - v.getTop() - topMargin/* - mAdjuster.top*/;\n                        if (curTranslateY + v.getHeight() + v.getTop() + bottomMargin/* + mAdjuster.bottom */ > parentViewHeight) {\n                            curTranslateY = parentViewHeight - v.getHeight()\n                                    - v.getTop() - bottomMargin/* - mAdjuster.bottom*/;\n                        }\n                        if (curTranslateY + v.getTop() - topMargin/* - mAdjuster.top*/ < 0) {\n                            curTranslateY = -v.getTop() + topMargin/* + mAdjuster.top*/;\n                        }\n                        v.setTranslationY(curTranslateY);\n                    }\n                    break;\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    doPullOverAnimation(v);\n                    (v.getParent()).requestDisallowInterceptTouchEvent(false);\n                    v.setPressed(false);\n                    break;\n            }\n            return isDrag;\n        }\n\n        private void doPullOverAnimation(final View v) {\n            ObjectAnimator animator;\n            if (v.getTranslationX() + v.getWidth() / 2 + v.getLeft() > parentViewWidth / 2) {\n                animator = ObjectAnimator.ofFloat(v, \"translationX\",\n                        v.getTranslationX(), parentViewWidth - v.getWidth()\n                                - v.getLeft() - rightMargin - mAdjuster.right);\n                mTransitionX = parentViewWidth - v.getWidth() - v.getLeft() - rightMargin - mAdjuster.right;\n            } else {\n                animator = ObjectAnimator.ofFloat(v, \"translationX\",\n                        v.getTranslationX(), -v.getLeft() + leftMargin + mAdjuster.left);\n                mTransitionX = -v.getLeft() + leftMargin + mAdjuster.left;\n            }\n\n            mTransitionY = (int) v.getTranslationY();\n            animator.setDuration(200);\n            animator.start();\n        }\n    };\n\n    public void setDragEnable(boolean dragEnable) {\n        this.dragEnable = dragEnable;\n        if (null != mFixView) {\n            mFixView.setOnTouchListener(dragEnable ? touchDragListener : null);\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/GridLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport android.support.annotation.NonNull;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.util.SparseIntArray;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport java.util.Arrays;\n\nimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;\n\n\n/**\n * LayoutHelper provides GridLayout. The difference with {@link ColumnLayoutHelper} is that this layoutHelper can layout and recycle child views one line by one line.\n *\n * @author villadora\n * @since 1.0.0\n */\npublic class GridLayoutHelper extends BaseLayoutHelper {\n    private static final String TAG = \"GridLayoutHelper\";\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private static boolean DEBUG = false;\n\n    private int mSpanCount = 4;\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private int mSizePerSpan = 0;\n\n\n    private int mTotalSize = 0;\n\n    private boolean mIsAutoExpand = true;\n\n    private boolean mIgnoreExtra = false;\n\n    @NonNull\n    private SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();\n\n    private int mVGap = 0;\n    private int mHGap = 0;\n\n\n    private float[] mWeights = new float[0];\n\n\n    private View[] mSet;\n\n    /**\n     * store index of each span\n     */\n    private int[] mSpanIndices;\n\n    /**\n     * store size of each span when {@link #mWeights} is not empty\n     */\n    private int[] mSpanCols;\n\n    /**\n     * @param spanCount number of columns/rows in grid, must be greater than 0\n     */\n    public GridLayoutHelper(int spanCount) {\n        this(spanCount, -1, -1);\n    }\n\n    /**\n     * @param spanCount number of columns/rows in grid, must be greater than 0\n     * @param itemCount number of items in this layoutHelper\n     */\n    public GridLayoutHelper(int spanCount, int itemCount) {\n        this(spanCount, itemCount, 0);\n    }\n\n    public GridLayoutHelper(int spanCount, int itemCount, int gap) {\n        this(spanCount, itemCount, gap, gap);\n    }\n\n    /**\n     * @param spanCount number of columns/rows in grid, must be greater than 0\n     * @param itemCount number of items in this layoutHelper\n     * @param vGap      vertical gap\n     * @param hGap      horizontal gap\n     */\n    public GridLayoutHelper(int spanCount, int itemCount, int vGap, int hGap) {\n        setSpanCount(spanCount);\n        mSpanSizeLookup.setSpanIndexCacheEnabled(true);\n\n        setItemCount(itemCount);\n        setVGap(vGap);\n        setHGap(hGap);\n    }\n\n\n    public void setWeights(float[] weights) {\n        if (weights != null) {\n            this.mWeights = Arrays.copyOf(weights, weights.length);\n        } else {\n            this.mWeights = new float[0];\n        }\n    }\n\n    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {\n        if (spanSizeLookup != null) {\n            // TODO: handle reverse layout?\n            spanSizeLookup.setStartPosition(mSpanSizeLookup.getStartPosition());\n\n            this.mSpanSizeLookup = spanSizeLookup;\n        }\n    }\n\n    public void setAutoExpand(boolean isAutoExpand) {\n        this.mIsAutoExpand = isAutoExpand;\n    }\n\n    public void setIgnoreExtra(boolean ignoreExtra) {\n        this.mIgnoreExtra = ignoreExtra;\n    }\n\n\n    /**\n     * {@inheritDoc}\n     * Set SpanCount for grid\n     *\n     * @param spanCount grid column number, must be greater than 0. {@link IllegalArgumentException}\n     *                  will be thrown otherwise\n     */\n    public void setSpanCount(int spanCount) {\n        if (spanCount == mSpanCount) {\n            return;\n        }\n        if (spanCount < 1) {\n            throw new IllegalArgumentException(\"Span count should be at least 1. Provided \"\n                    + spanCount);\n        }\n        mSpanCount = spanCount;\n        mSpanSizeLookup.invalidateSpanIndexCache();\n\n        ensureSpanCount();\n    }\n\n    public int getVGap() {\n        return mVGap;\n    }\n\n    public int getHGap() {\n        return mHGap;\n    }\n\n    public int getSpanCount() {\n        return mSpanCount;\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param start start position of items handled by this layoutHelper\n     * @param end   end position of items handled by this layoutHelper, if end < start, it will throw {@link IllegalArgumentException}\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        mSpanSizeLookup.setStartPosition(start);\n        mSpanSizeLookup.invalidateSpanIndexCache();\n    }\n\n\n    public void setGap(int gap) {\n        setVGap(gap);\n        setHGap(gap);\n    }\n\n    public void setVGap(int vGap) {\n        if (vGap < 0) vGap = 0;\n        this.mVGap = vGap;\n    }\n\n    public void setHGap(int hGap) {\n        if (hGap < 0) hGap = 0;\n        this.mHGap = hGap;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        boolean isStartLine = false, isEndLine = false;\n        final int currentPosition = layoutState.getCurrentPosition();\n        final boolean isOverLapMargin = helper.isEnableMarginOverLap();\n\n        final int itemDirection = layoutState.getItemDirection();\n        final boolean layingOutInPrimaryDirection =\n            itemDirection == LayoutStateWrapper.ITEM_DIRECTION_TAIL;\n\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        if (layoutInVertical) {\n            mTotalSize = helper.getContentWidth() - helper.getPaddingRight() - helper.getPaddingLeft() - getHorizontalMargin() - getHorizontalPadding();\n            mSizePerSpan = (int) ((mTotalSize - (mSpanCount - 1) * mHGap) * 1.0f / mSpanCount + 0.5f);\n        } else {\n            mTotalSize = helper.getContentHeight() - helper.getPaddingBottom() - helper.getPaddingTop() - getVerticalMargin() - getVerticalPadding();\n            mSizePerSpan = (int) ((mTotalSize - (mSpanCount - 1) * mVGap) * 1.0f / mSpanCount + 0.5f);\n        }\n\n\n        int count = 0;\n        int consumedSpanCount = 0;\n        int remainingSpan = mSpanCount;\n\n\n        ensureSpanCount();\n\n\n        if (!layingOutInPrimaryDirection) {\n            // fill the remaining spacing this row\n            int itemSpanIndex = getSpanIndex(recycler, state, layoutState.getCurrentPosition());\n            int itemSpanSize = getSpanSize(recycler, state, layoutState.getCurrentPosition());\n\n\n            remainingSpan = itemSpanIndex + itemSpanSize;\n\n            // should find the last element of this row\n            if (itemSpanIndex != mSpanCount - 1) {\n                int index = layoutState.getCurrentPosition();\n                int revRemainingSpan = mSpanCount - remainingSpan;\n                while (count < mSpanCount && revRemainingSpan > 0) {\n                    // go reverse direction to find views fill current row\n                    index -= itemDirection;\n                    if (isOutOfRange(index)) {\n                        break;\n                    }\n                    final int spanSize = getSpanSize(recycler, state, index);\n                    if (spanSize > mSpanCount) {\n                        throw new IllegalArgumentException(\"Item at position \" + index + \" requires \" +\n                            spanSize + \" spans but GridLayoutManager has only \" + mSpanCount\n                            + \" spans.\");\n                    }\n\n                    View view = layoutState.retrieve(recycler, index);\n                    if (view == null)\n                        break;\n\n                    if (!isStartLine) {\n                        isStartLine = helper.getReverseLayout() ? index == getRange().getUpper() : index == getRange().getLower();\n                    }\n\n                    if (!isEndLine) {\n                        isEndLine = helper.getReverseLayout() ? index == getRange().getLower() : index == getRange().getUpper();\n                    }\n\n                    revRemainingSpan -= spanSize;\n                    if (revRemainingSpan < 0)\n                        break;\n\n\n                    consumedSpanCount += spanSize;\n                    mSet[count] = view;\n                    count++;\n                }\n\n                if (count > 0) {\n                    // reverse array\n                    int s = 0, e = count - 1;\n                    while (s < e) {\n                        View temp = mSet[s];\n                        mSet[s] = mSet[e];\n                        mSet[e] = temp;\n                        s++;\n                        e--;\n                    }\n                }\n            }\n        }\n\n        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {\n            int pos = layoutState.getCurrentPosition();\n            if (isOutOfRange(pos)) {\n                if (DEBUG)\n                    Log.d(TAG, \"pos [\" + pos + \"] is out of range\");\n                break;\n            }\n\n            final int spanSize = getSpanSize(recycler, state, pos);\n            if (spanSize > mSpanCount) {\n                throw new IllegalArgumentException(\"Item at position \" + pos + \" requires \" +\n                    spanSize + \" spans but GridLayoutManager has only \" + mSpanCount\n                    + \" spans.\");\n            }\n            remainingSpan -= spanSize;\n            if (remainingSpan < 0) {\n                break; // item did not fit into this row or column\n            }\n\n            View view = layoutState.next(recycler);\n            if (view == null) {\n                break;\n            }\n\n            if (!isStartLine) {\n                isStartLine = helper.getReverseLayout() ? pos == getRange().getUpper().intValue() : pos == getRange().getLower().intValue();\n            }\n\n            if (!isEndLine) {\n                isEndLine = helper.getReverseLayout() ? pos == getRange().getLower().intValue() : pos == getRange().getUpper().intValue();\n            }\n\n            consumedSpanCount += spanSize;\n            mSet[count] = view;\n            count++;\n        }\n\n\n        if (count == 0) {\n            return;\n        }\n\n        int maxSize = 0;\n\n\n        // we should assign spans before item decor offsets are calculated\n        assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection, helper);\n\n        if (remainingSpan > 0 && (count == consumedSpanCount) && mIsAutoExpand) {\n            //autoExpand only support when each cell occupy one span.\n            if (layoutInVertical) {\n                mSizePerSpan = (mTotalSize - (count - 1) * mHGap) / count;\n            } else {\n                mSizePerSpan = (mTotalSize - (count - 1) * mVGap) / count;\n            }\n        } else if (!layingOutInPrimaryDirection && remainingSpan == 0 && (count == consumedSpanCount) && mIsAutoExpand) {\n            //autoExpand only support when each cell occupy one span.\n            if (layoutInVertical) {\n                mSizePerSpan = (mTotalSize - (count - 1) * mHGap) / count;\n            } else {\n                mSizePerSpan = (mTotalSize - (count - 1) * mVGap) / count;\n            }\n        }\n\n\n        boolean weighted = false;\n        if (mWeights != null && mWeights.length > 0) {\n            weighted = true;\n            int totalSpace;\n            if (layoutInVertical) {\n                totalSpace = mTotalSize - (count - 1) * mHGap;\n            } else {\n                totalSpace = mTotalSize - (count - 1) * mVGap;\n            }\n\n            // calculate width with weight in percentage\n\n            int eqCnt = 0, remainingSpace = totalSpace;\n            int colCnt = (remainingSpan > 0 && mIsAutoExpand) ? count : mSpanCount;\n            for (int i = 0; i < colCnt; i++) {\n                if (i < mWeights.length && !Float.isNaN(mWeights[i]) && mWeights[i] >= 0) {\n                    float weight = mWeights[i];\n                    mSpanCols[i] = (int) (weight * 1.0f / 100 * totalSpace + 0.5f);\n                    remainingSpace -= mSpanCols[i];\n                } else {\n                    eqCnt++;\n                    mSpanCols[i] = -1;\n                }\n            }\n\n            if (eqCnt > 0) {\n                int eqLength = remainingSpace / eqCnt;\n                for (int i = 0; i < colCnt; i++) {\n                    if (mSpanCols[i] < 0) {\n                        mSpanCols[i] = eqLength;\n                    }\n                }\n            }\n        }\n\n\n        for (int i = 0; i < count; i++) {\n            View view = mSet[i];\n            helper.addChildView(layoutState, view, layingOutInPrimaryDirection ? -1 : 0);\n\n            int spanSize = getSpanSize(recycler, state, helper.getPosition(view)), spec;\n            if (weighted) {\n                final int index = mSpanIndices[i];\n                int spanLength = 0;\n                for (int j = 0; j < spanSize; j++) {\n                    spanLength += mSpanCols[j + index];\n                }\n\n                spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);\n            } else {\n                spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize +\n                        Math.max(0, spanSize - 1) * (layoutInVertical ? mHGap : mVGap),\n                    View.MeasureSpec.EXACTLY);\n            }\n            final VirtualLayoutManager.LayoutParams lp = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n\n            if (helper.getOrientation() == VERTICAL) {\n                helper.measureChildWithMargins(view, spec, getMainDirSpec(lp.height, mTotalSize,\n                    View.MeasureSpec.getSize(spec), lp.mAspectRatio));\n            } else {\n                helper.measureChildWithMargins(view,\n                    getMainDirSpec(lp.width, mTotalSize, View.MeasureSpec.getSize(spec),\n                        lp.mAspectRatio), View.MeasureSpec.getSize(spec));\n            }\n            final int size = orientationHelper.getDecoratedMeasurement(view);\n            if (size > maxSize) {\n                maxSize = size;\n            }\n        }\n\n        // views that did not measure the maxSize has to be re-measured\n        final int maxMeasureSpec = getMainDirSpec(maxSize, mTotalSize, 0, Float.NaN);\n        for (int i = 0; i < count; i++) {\n            final View view = mSet[i];\n            if (orientationHelper.getDecoratedMeasurement(view) != maxSize) {\n                int spanSize = getSpanSize(recycler, state, helper.getPosition(view)), spec;\n                if (weighted) {\n                    final int index = mSpanIndices[i];\n                    int spanLength = 0;\n                    for (int j = 0; j < spanSize; j++) {\n                        spanLength += mSpanCols[j + index];\n                    }\n\n                    spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);\n                } else {\n                    spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize +\n                            Math.max(0, spanSize - 1) * (layoutInVertical ? mHGap : mVGap),\n                        View.MeasureSpec.EXACTLY);\n                }\n\n                if (helper.getOrientation() == VERTICAL) {\n                    helper.measureChildWithMargins(view, spec, maxMeasureSpec);\n                } else {\n                    helper.measureChildWithMargins(view, maxMeasureSpec, spec);\n                }\n            }\n        }\n\n        int startSpace = 0, endSpace = 0;\n\n        if (isStartLine) {\n            startSpace = computeStartSpace(helper, layoutInVertical, !helper.getReverseLayout(), isOverLapMargin);\n        }\n\n        if (isEndLine) {\n            endSpace = computeEndSpace(helper, layoutInVertical, !helper.getReverseLayout(), isOverLapMargin);\n        }\n\n\n        result.mConsumed = maxSize + startSpace + endSpace;\n\n        final boolean layoutStart = layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START;\n        if (!mLayoutWithAnchor && (!isEndLine || !layoutStart) && (!isStartLine || layoutStart)) {\n            result.mConsumed += (layoutInVertical ? mVGap : mHGap);\n        }\n\n\n        int left = 0, right = 0, top = 0, bottom = 0;\n        if (layoutInVertical) {\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                bottom = layoutState.getOffset() - endSpace - ((mLayoutWithAnchor || isEndLine) ? 0 : mVGap);\n                top = bottom - maxSize;\n            } else {\n                top = layoutState.getOffset() + startSpace + ((mLayoutWithAnchor || isStartLine) ? 0 : mVGap);\n                bottom = top + maxSize;\n            }\n        } else {\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                right = layoutState.getOffset() - endSpace - (mLayoutWithAnchor || isEndLine ? 0 : mHGap);\n                left = right - maxSize;\n            } else {\n                left = layoutState.getOffset() + startSpace + (mLayoutWithAnchor || isStartLine ? 0 : mHGap);\n                right = left + maxSize;\n            }\n        }\n\n        for (int i = 0; i < count; i++) {\n            View view = mSet[i];\n            final int index = mSpanIndices[i];\n\n            LayoutParams params = (LayoutParams) view.getLayoutParams();\n            if (layoutInVertical) {\n                if (weighted) {\n                    left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;\n                    for (int j = 0; j < index; j++) {\n                        left += mSpanCols[j] + mHGap;\n                    }\n                } else {\n                    left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft + mSizePerSpan * index + index * mHGap;\n                }\n\n                right = left + orientationHelper.getDecoratedMeasurementInOther(view);\n            } else {\n\n                if (weighted) {\n                    top = helper.getPaddingTop() + mMarginTop + mPaddingTop;\n                    for (int j = 0; j < index; j++) {\n                        top += mSpanCols[j] + mVGap;\n                    }\n                } else {\n                    top = helper.getPaddingTop() + mMarginTop + mPaddingTop\n                        + mSizePerSpan * index + index * mVGap;\n                }\n\n                bottom = top + orientationHelper.getDecoratedMeasurementInOther(view);\n            }\n\n            if (DEBUG) {\n                Log.d(TAG, \"layout item in position: \" + params.getViewPosition() + \" with text \" + ((TextView) view).getText() + \" with SpanIndex: \" + index + \" into (\" +\n                    left + \", \" + top + \", \" + right + \", \" + bottom + \" )\");\n            }\n\n            // We calculate everything with View's bounding box (which includes decor and margins)\n            // To calculate correct layout position, we subtract margins.\n            // modified by huifeng at 20160907, margins are already subtracted\n            layoutChildWithMargin(view, left, top, right, bottom, helper);\n\n            // Consume the available space if the view is not removed OR changed\n            if (params.isItemRemoved() || params.isItemChanged()) {\n                result.mIgnoreConsumed = true;\n            }\n\n            result.mFocusable |= view.isFocusable();\n        }\n\n\n        mLayoutWithAnchor = false;\n        Arrays.fill(mSet, null);\n        Arrays.fill(mSpanIndices, 0);\n        Arrays.fill(mSpanCols, 0);\n    }\n\n\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        if (isLayoutEnd) {\n            if (offset == getItemCount() - 1) {\n                return layoutInVertical ? mMarginBottom + mPaddingBottom : mMarginRight + mPaddingRight;\n            }\n        } else {\n            if (offset == 0) {\n                return layoutInVertical ? -mMarginTop - mPaddingTop : -mMarginLeft - mPaddingLeft;\n            }\n        }\n\n        return super.computeAlignOffset(offset, isLayoutEnd, useAnchor, helper);\n    }\n\n    @Override\n    public void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n        mSpanSizeLookup.invalidateSpanIndexCache();\n    }\n\n    @Override\n    public void onItemsChanged(LayoutManagerHelper helper) {\n        super.onItemsChanged(helper);\n        mSpanSizeLookup.invalidateSpanIndexCache();\n    }\n\n    private static final int MAIN_DIR_SPEC =\n            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);\n\n    private int getMainDirSpec(int dim, int otherSize, int viewSize, float viewAspectRatio) {\n        if (!Float.isNaN(viewAspectRatio) && viewAspectRatio > 0 && viewSize > 0) {\n            return View.MeasureSpec.makeMeasureSpec((int) (viewSize / viewAspectRatio + 0.5f), View.MeasureSpec.EXACTLY);\n        } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n            return View.MeasureSpec.makeMeasureSpec((int) (otherSize / mAspectRatio + 0.5f), View.MeasureSpec.EXACTLY);\n        } else if (dim < 0) {\n            return MAIN_DIR_SPEC;\n        } else {\n            return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);\n        }\n    }\n\n\n    private void ensureSpanCount() {\n\n        if (mSet == null || mSet.length != mSpanCount) {\n            mSet = new View[mSpanCount];\n        }\n\n        if (mSpanIndices == null || mSpanIndices.length != mSpanCount) {\n            mSpanIndices = new int[mSpanCount];\n        }\n\n        if (mSpanCols == null || mSpanCols.length != mSpanCount) {\n            mSpanCols = new int[mSpanCount];\n        }\n    }\n\n\n    private boolean mLayoutWithAnchor = false;\n\n    @Override\n    public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        if (state.getItemCount() > 0 && !state.isPreLayout()) {\n            int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.position, mSpanCount);\n            if (anchorInfo.layoutFromEnd) {\n                while (span < mSpanCount - 1 && anchorInfo.position < getRange().getUpper()) {\n                    anchorInfo.position++;\n                    span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.position, mSpanCount);\n                }\n            } else {\n                while (span > 0 && anchorInfo.position > 0) {\n                    anchorInfo.position--;\n                    span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.position, mSpanCount);\n                }\n            }\n\n            mLayoutWithAnchor = true;\n\n/*\n            if (anchorInfo.position == getRange().getLower() || anchorInfo.position == getRange().getUpper()) {\n                return;\n            }\n\n            boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n            if (anchorInfo.layoutFromEnd) {\n                anchorInfo.coordinate += layoutInVertical ? mVGap : mHGap;\n            } else {\n                anchorInfo.coordinate -= layoutInVertical ? mVGap : mHGap;\n            }\n */\n\n        }\n    }\n\n\n    private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {\n        if (!state.isPreLayout()) {\n            return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);\n        }\n\n        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);\n        if (adapterPosition == -1) {\n            return 0;\n        }\n        return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);\n    }\n\n\n    private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {\n        if (!state.isPreLayout()) {\n            return mSpanSizeLookup.getSpanSize(pos);\n        }\n\n        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);\n        if (adapterPosition == -1) {\n            return 0;\n        }\n\n        return mSpanSizeLookup.getSpanSize(adapterPosition);\n    }\n\n    private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,\n                             int consumedSpanCount, boolean layingOutInPrimaryDirection, LayoutManagerHelper helper) {\n        int span, spanDiff, start, end, diff;\n        // make sure we traverse from min position to max position\n        if (layingOutInPrimaryDirection) {\n            start = 0;\n            end = count;\n            diff = 1;\n        } else {\n            start = count - 1;\n            end = -1;\n            diff = -1;\n        }\n\n        if (helper.getOrientation() == VERTICAL && helper.isDoLayoutRTL()) { // start from last span\n            span = consumedSpanCount - 1;\n            spanDiff = -1;\n        } else {\n            span = 0;\n            spanDiff = 1;\n        }\n\n        for (int i = start; i != end; i += diff) {\n            View view = mSet[i];\n            int spanSize = getSpanSize(recycler, state, helper.getPosition(view));\n            if (spanDiff == -1 && spanSize > 1) {\n                mSpanIndices[i] = span - (spanSize - 1);\n            } else {\n                mSpanIndices[i] = span;\n            }\n            span += spanDiff * spanSize;\n        }\n    }\n\n\n    static final class DefaultSpanSizeLookup extends SpanSizeLookup {\n\n        @Override\n        public int getSpanSize(int position) {\n            return 1;\n        }\n\n        @Override\n        public int getSpanIndex(int span, int spanCount) {\n            return (span - mStartPosition) % spanCount;\n        }\n    }\n\n\n    public static abstract class SpanSizeLookup {\n\n        final SparseIntArray mSpanIndexCache = new SparseIntArray();\n\n        private boolean mCacheSpanIndices = false;\n\n        int mStartPosition = 0;\n\n        /**\n         * Returns the number of span occupied by the item at <code>position</code>.\n         *\n         * @param position The adapter position of the item\n         * @return The number of spans occupied by the item at the provided position\n         */\n        abstract public int getSpanSize(int position);\n\n        /**\n         * Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or\n         * not. By default these values are not cached. If you are not overriding\n         * {@link #getSpanIndex(int, int)}, you should set this to true for better performance.\n         *\n         * @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.\n         */\n        public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {\n            mCacheSpanIndices = cacheSpanIndices;\n        }\n\n        public void setStartPosition(int startPosition) {\n            this.mStartPosition = startPosition;\n        }\n\n        public int getStartPosition() {\n            return this.mStartPosition;\n        }\n\n        /**\n         * Clears the span index cache. GridLayoutManager automatically calls this method when\n         * adapter changes occur.\n         */\n        public void invalidateSpanIndexCache() {\n            mSpanIndexCache.clear();\n        }\n\n        /**\n         * Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.\n         *\n         * @return True if results of {@link #getSpanIndex(int, int)} are cached.\n         */\n        public boolean isSpanIndexCacheEnabled() {\n            return mCacheSpanIndices;\n        }\n\n        int getCachedSpanIndex(int position, int spanCount) {\n            if (!mCacheSpanIndices) {\n                return getSpanIndex(position, spanCount);\n            }\n            final int existing = mSpanIndexCache.get(position, -1);\n            if (existing != -1) {\n                return existing;\n            }\n            final int value = getSpanIndex(position, spanCount);\n            mSpanIndexCache.put(position, value);\n            return value;\n        }\n\n        /**\n         * Returns the final span index of the provided position.\n         * <p/>\n         * If you have a faster way to calculate span index for your items, you should override\n         * this method. Otherwise, you should enable span index cache\n         * ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is\n         * disabled, default implementation traverses all items from 0 to\n         * <code>position</code>. When caching is enabled, it calculates from the closest cached\n         * value before the <code>position</code>.\n         * <p/>\n         * If you override this method, you need to make sure it is consistent with\n         * {@link #getSpanSize(int)}. GridLayoutManager does not call this method for\n         * each item. It is called only for the reference item and rest of the items\n         * are assigned to spans based on the reference item. For example, you cannot assign a\n         * position to span 2 while span 1 is empty.\n         * <p/>\n         * Note that span offsets always start with 0 and are not affected by RTL.\n         *\n         * @param position  The position of the item\n         * @param spanCount The total number of spans in the grid\n         * @return The final span position of the item. Should be between 0 (inclusive) and\n         * <code>spanCount</code>(exclusive)\n         */\n        public int getSpanIndex(int position, int spanCount) {\n            int positionSpanSize = getSpanSize(position);\n            if (positionSpanSize == spanCount) {\n                return 0; // quick return for full-span items\n            }\n            int span = 0;\n            int startPos = mStartPosition;\n            // If caching is enabled, try to jump\n            if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {\n                int prevKey = findReferenceIndexFromCache(position);\n                if (prevKey >= 0) {\n                    span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);\n                    startPos = prevKey + 1;\n                }\n            }\n            for (int i = startPos; i < position; i++) {\n                int size = getSpanSize(i);\n                span += size;\n                if (span == spanCount) {\n                    span = 0;\n                } else if (span > spanCount) {\n                    // did not fit, moving to next row / column\n                    span = size;\n                }\n            }\n            if (span + positionSpanSize <= spanCount) {\n                return span;\n            }\n            return 0;\n        }\n\n        int findReferenceIndexFromCache(int position) {\n            int lo = 0;\n            int hi = mSpanIndexCache.size() - 1;\n\n            while (lo <= hi) {\n                final int mid = (lo + hi) >>> 1;\n                final int midVal = mSpanIndexCache.keyAt(mid);\n                if (midVal < position) {\n                    lo = mid + 1;\n                } else {\n                    hi = mid - 1;\n                }\n            }\n            int index = lo - 1;\n            if (index >= 0 && index < mSpanIndexCache.size()) {\n                return mSpanIndexCache.keyAt(index);\n            }\n            return -1;\n        }\n\n        /**\n         * Returns the index of the group this position belongs.\n         * <p/>\n         * For example, if grid has 3 columns and each item occupies 1 span, span group index\n         * for item 1 will be 0, item 5 will be 1.\n         *\n         * @param adapterPosition The position in adapter\n         * @param spanCount       The total number of spans in the grid\n         * @return The index of the span group including the item at the given adapter position\n         */\n        public int getSpanGroupIndex(int adapterPosition, int spanCount) {\n            int span = 0;\n            int group = 0;\n            int positionSpanSize = getSpanSize(adapterPosition);\n            for (int i = 0; i < adapterPosition; i++) {\n                int size = getSpanSize(i);\n                span += size;\n                if (span == spanCount) {\n                    span = 0;\n                    group++;\n                } else if (span > spanCount) {\n                    // did not fit, moving to next row / column\n                    span = size;\n                    group++;\n                }\n            }\n            if (span + positionSpanSize > spanCount) {\n                group++;\n            }\n            return group;\n        }\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/LayoutChunkResult.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\n/**\n * Created by villadora on 15/8/10.\n */\npublic class LayoutChunkResult {\n    public int mConsumed;\n    public boolean mFinished;\n    public boolean mIgnoreConsumed;\n    public boolean mFocusable;\n\n    public void resetInternal() {\n        mConsumed = 0;\n        mFinished = false;\n        mIgnoreConsumed = false;\n        mFocusable = false;\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/LinearLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.AnchorInfoWrapper;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.RecyclerView.State;\nimport android.util.Log;\nimport android.view.View;\n\nimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;\n\n\n/**\n * LayoutHelper layouts views as linear/ListView\n */\npublic class LinearLayoutHelper extends BaseLayoutHelper {\n\n    private static final String TAG = \"LinearLayoutHelper\";\n\n    private static final boolean DEBUG = false;\n\n    private int mDividerHeight = 0;\n\n    private boolean mLayoutWithAnchor = false;\n\n    public LinearLayoutHelper() {\n        this(0);\n    }\n\n    public LinearLayoutHelper(int dividerHeight) {\n        // empty range\n        this(dividerHeight, 0);\n    }\n\n\n    public LinearLayoutHelper(int dividerHeight, int itemCount) {\n        setItemCount(itemCount);\n        setDividerHeight(dividerHeight);\n    }\n\n\n    public void setDividerHeight(int dividerHeight) {\n        if (dividerHeight < 0) {\n            dividerHeight = 0;\n        }\n        this.mDividerHeight = dividerHeight;\n    }\n\n    /**\n     * In {@link LinearLayoutHelper}, each iteration only consume one item,\n     * so it can let parent LayoutManager to decide whether the next item is in the range of this helper\n     */\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            VirtualLayoutManager.LayoutStateWrapper layoutState, LayoutChunkResult result,\n                            LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n        int currentPosition = layoutState.getCurrentPosition();\n\n        // find corresponding layout container\n        View view = nextView(recycler, layoutState, helper, result);\n        if (view == null) {\n            return;\n        }\n        final boolean isOverLapMargin = helper.isEnableMarginOverLap();\n\n        VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        int startSpace = 0, endSpace = 0, gap = 0;\n        boolean isLayoutEnd = layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_END;\n        boolean isStartLine = isLayoutEnd\n                ? currentPosition == getRange().getLower().intValue()\n                : currentPosition == getRange().getUpper().intValue();\n        boolean isEndLine = isLayoutEnd\n                ? currentPosition == getRange().getUpper().intValue()\n                : currentPosition == getRange().getLower().intValue();\n\n        if (isStartLine) {\n            startSpace = computeStartSpace(helper, layoutInVertical, isLayoutEnd, isOverLapMargin);\n        }\n\n        if (isEndLine) {\n            endSpace = computeEndSpace(helper, layoutInVertical, isLayoutEnd, isOverLapMargin);\n        }\n\n        if (!isStartLine) {\n            if (!isOverLapMargin) {\n                gap = mLayoutWithAnchor ? 0 : mDividerHeight;\n            } else {\n                //TODO check layout with anchor\n                if (isLayoutEnd) {\n                    int marginTop = params.topMargin;\n                    View sibling = helper.findViewByPosition(currentPosition - 1);\n                    int lastMarginBottom = sibling != null ? ((LayoutParams) sibling.getLayoutParams()).bottomMargin : 0;\n                    if (lastMarginBottom >= 0 && marginTop >= 0) {\n                        gap = Math.max(lastMarginBottom, marginTop);\n                    } else {\n                        gap = lastMarginBottom + marginTop;\n                    }\n                } else {\n                    int marginBottom = params.bottomMargin;\n                    View sibling = helper.findViewByPosition(currentPosition + 1);\n                    int lastMarginTop = sibling != null ? ((LayoutParams) sibling.getLayoutParams()).topMargin : 0;\n                    if (marginBottom >= 0 && lastMarginTop >= 0) {\n                        gap = Math.max(marginBottom, lastMarginTop);\n                    } else {\n                        gap = marginBottom + lastMarginTop;\n                    }\n                }\n            }\n        }\n\n        final int widthSize = helper.getContentWidth() - helper.getPaddingLeft() - helper\n                .getPaddingRight() - getHorizontalMargin() - getHorizontalPadding();\n        int widthSpec = helper.getChildMeasureSpec(widthSize, params.width, !layoutInVertical);\n        int heightSpec;\n        float viewAspectRatio = params.mAspectRatio;\n        if (!Float.isNaN(viewAspectRatio) && viewAspectRatio > 0) {\n            heightSpec = View.MeasureSpec.makeMeasureSpec((int) (widthSize / viewAspectRatio + 0.5f),\n                    View.MeasureSpec.EXACTLY);\n        } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n            heightSpec = View.MeasureSpec.makeMeasureSpec((int) (widthSize / mAspectRatio + 0.5),\n                    View.MeasureSpec.EXACTLY);\n        } else {\n            heightSpec = helper.getChildMeasureSpec(\n                    helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom()\n                            - getVerticalMargin() - getVerticalPadding(), params.height,\n                    layoutInVertical);\n        }\n\n        if (!isOverLapMargin) {\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        } else {\n            helper.measureChild(view, widthSpec, heightSpec);\n        }\n\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        result.mConsumed = orientationHelper.getDecoratedMeasurement(view) + startSpace + endSpace + gap;\n        int left, top, right, bottom;\n        if (helper.getOrientation() == VERTICAL) {\n            // not support RTL now\n            if (helper.isDoLayoutRTL()) {\n                right = helper.getContentWidth() - helper.getPaddingRight() - mMarginRight - mPaddingRight;\n                left = right - orientationHelper.getDecoratedMeasurementInOther(view);\n            } else {\n                left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;\n                right = left + orientationHelper.getDecoratedMeasurementInOther(view);\n            }\n\n            // whether this layout pass is layout to start or to end\n            if (layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START) {\n                // fill start, from bottom to top\n                bottom = layoutState.getOffset() - startSpace - (isStartLine ? 0 : gap);\n                top = bottom - orientationHelper.getDecoratedMeasurement(view);\n            } else {\n                // fill end, from top to bottom\n                top = layoutState.getOffset() + startSpace + (isStartLine ? 0 : gap);\n                bottom = top + orientationHelper.getDecoratedMeasurement(view);\n            }\n        } else {\n            top = helper.getPaddingTop() + mMarginTop + mPaddingTop;\n            bottom = top + orientationHelper.getDecoratedMeasurementInOther(view);\n\n            if (layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START) {\n                // fill left, from right to left\n                right = layoutState.getOffset() - startSpace - (isStartLine ? 0 : gap);\n                left = right - orientationHelper.getDecoratedMeasurement(view);\n            } else {\n                // fill right, from left to right\n                left = layoutState.getOffset() + startSpace + (isStartLine ? 0 : gap);\n                right = left + orientationHelper.getDecoratedMeasurement(view);\n            }\n        }\n        // We calculate everything with View's bounding box (which includes decor and margins)\n        // To calculate correct layout position, we subtract margins.\n        layoutChildWithMargin(view, left, top, right, bottom, helper);\n\n        if (DEBUG) {\n            Log.d(TAG, \"laid out child at position \" + helper.getPosition(view) + \", with l:\"\n                    + (left + params.leftMargin) + \", t:\" + (top + params.topMargin) + \", r:\"\n                    + (right - params.rightMargin) + \", b:\" + (bottom - params.bottomMargin));\n        }\n\n        handleStateOnResult(result, view);\n        mLayoutWithAnchor = false;\n    }\n\n    @Override\n    public void checkAnchorInfo(State state, AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        super.checkAnchorInfo(state, anchorInfo, helper);\n        mLayoutWithAnchor = true;\n    }\n\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        if (isLayoutEnd) {\n            if (offset == getItemCount() - 1) {\n                return layoutInVertical ? mMarginBottom + mPaddingBottom : mMarginRight + mPaddingRight;\n            }\n        } else {\n            if (offset == 0) {\n                return layoutInVertical ? -mMarginTop - mPaddingTop : -mMarginLeft - mPaddingLeft;\n            }\n        }\n\n        return super.computeAlignOffset(offset, isLayoutEnd, useAnchor, helper);\n    }\n\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/MarginLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * {@link LayoutHelper} provides margin and padding supports.\n */\npublic abstract class MarginLayoutHelper extends LayoutHelper {\n\n    protected int mPaddingLeft;\n    protected int mPaddingRight;\n    protected int mPaddingTop;\n    protected int mPaddingBottom;\n\n    protected int mMarginLeft;\n    protected int mMarginRight;\n    protected int mMarginTop;\n    protected int mMarginBottom;\n\n\n    /**\n     * set paddings for this layoutHelper\n     * @param leftPadding left padding\n     * @param topPadding top padding\n     * @param rightPadding right padding\n     * @param bottomPadding bottom padding\n     */\n    public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding) {\n        mPaddingLeft = leftPadding;\n        mPaddingRight = rightPadding;\n        mPaddingTop = topPadding;\n        mPaddingBottom = bottomPadding;\n    }\n\n    /**\n     * Set margins for this layoutHelper\n     *\n     * @param leftMargin left margin\n     * @param topMargin top margin\n     * @param rightMargin right margin\n     * @param bottomMargin bottom margin\n     */\n    public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin) {\n        this.mMarginLeft = leftMargin;\n        this.mMarginTop = topMargin;\n        this.mMarginRight = rightMargin;\n        this.mMarginBottom = bottomMargin;\n    }\n\n    /**\n     * Calculate align offset when start a new layout with anchor views\n     *\n     * @param offset      anchor child's offset in current layoutHelper, for example, 0 means first item\n     * @param isLayoutEnd is the layout process will do to end or start, true means it will lay views from start to end\n     * @param useAnchor   whether offset is computed for scrolling or for anchor reset\n     * @param helper      view layout helper\n     * @return pixel offset to start to the anchor view\n     */\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        return 0;\n    }\n\n    @Override\n    public int computeMarginStart(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            return mMarginTop;\n        } else {\n            return mMarginLeft;\n        }\n    }\n\n    @Override\n    public int computeMarginEnd(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            return mMarginBottom;\n        } else {\n            return mMarginRight;\n        }\n    }\n\n    @Override\n    public int computePaddingStart(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            return mPaddingTop;\n        } else {\n            return mPaddingLeft;\n        }\n    }\n\n    @Override\n    public int computePaddingEnd(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (layoutInVertical) {\n            return mPaddingBottom;\n        } else {\n            return mPaddingRight;\n        }\n    }\n\n    /**\n     * Get total margin in horizontal dimension\n     *\n     * @return\n     */\n    public int getHorizontalMargin() {\n        return mMarginLeft + mMarginRight;\n    }\n\n    /**\n     * Get total margin in vertical dimension\n     *\n     * @return\n     */\n    public int getVerticalMargin() {\n        return mMarginTop + mMarginBottom;\n    }\n\n    /**\n     * Get total padding in horizontal dimension\n     * @return\n     */\n    public int getHorizontalPadding() {\n        return mPaddingLeft + mPaddingRight;\n    }\n\n    /**\n     * Get total padding in vertical dimension\n     * @return\n     */\n    public int getVerticalPadding() {\n        return mPaddingTop + mPaddingBottom;\n    }\n\n    public int getPaddingLeft() {\n        return mPaddingLeft;\n    }\n\n    public int getPaddingRight() {\n        return mPaddingRight;\n    }\n\n    public int getPaddingTop() {\n        return mPaddingTop;\n    }\n\n    public int getPaddingBottom() {\n        return mPaddingBottom;\n    }\n\n    public int getMarginLeft() {\n        return mMarginLeft;\n    }\n\n    public int getMarginRight() {\n        return mMarginRight;\n    }\n\n    public int getMarginTop() {\n        return mMarginTop;\n    }\n\n    public int getMarginBottom() {\n        return mMarginBottom;\n    }\n\n    public void setPaddingLeft(int paddingLeft) {\n        mPaddingLeft = paddingLeft;\n    }\n\n    public void setPaddingRight(int paddingRight) {\n        mPaddingRight = paddingRight;\n    }\n\n    public void setPaddingTop(int paddingTop) {\n        mPaddingTop = paddingTop;\n    }\n\n    public void setPaddingBottom(int paddingBottom) {\n        mPaddingBottom = paddingBottom;\n    }\n\n    public void setMarginLeft(int marginLeft) {\n        mMarginLeft = marginLeft;\n    }\n\n    public void setMarginRight(int marginRight) {\n        mMarginRight = marginRight;\n    }\n\n    public void setMarginTop(int marginTop) {\n        mMarginTop = marginTop;\n    }\n\n    public void setMarginBottom(int marginBottom) {\n        mMarginBottom = marginBottom;\n    }\n}\n\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/OnePlusNLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport java.util.Arrays;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.AnchorInfoWrapper;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport android.graphics.Rect;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.RecyclerView.State;\nimport android.view.View;\nimport android.view.View.MeasureSpec;\nimport android.view.ViewGroup;\n\nimport static android.view.ViewGroup.LayoutParams.MATCH_PARENT;\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * <pre>\n * Currently support 1+3(max) layout\n * 1 + 0\n * -------------------------\n * |                       |\n * |                       |\n * |           1           |\n * |                       |\n * |                       |\n * |                       |\n * -------------------------\n *\n * 1 + 1\n * -------------------------\n * |           |           |\n * |           |           |\n * |           |           |\n * |     1     |     2     |\n * |           |           |\n * |           |           |\n * |           |           |\n * -------------------------\n *\n * 1 + 2\n * -------------------------\n * |           |           |\n * |           |     2     |\n * |           |           |\n * |     1     |-----------|\n * |           |           |\n * |           |     3     |\n * |           |           |\n * -------------------------\n *\n * 1 + 3\n * -------------------------\n * |           |           |\n * |           |     2     |\n * |           |           |\n * |     1     |-----------|\n * |           |     |     |\n * |           |  3  |  4  |\n * |           |     |     |\n * -------------------------\n *  1 + 4\n * -------------------------\n * |           |           |\n * |           |     2     |\n * |           |           |\n * |     1     |-----------|\n * |           |   |   |   |\n * |           | 3 | 4 | 5 |\n * |           |   |   |   |\n * -------------------------\n * </pre>\n *\n * @author villadora\n * @since 1.0.0\n */\npublic class OnePlusNLayoutHelper extends AbstractFullFillLayoutHelper {\n\n    private static final String TAG = \"OnePlusNLayoutHelper\";\n\n\n    private Rect mAreaRect = new Rect();\n\n    private View[] mChildrenViews;\n\n    private float[] mColWeights = new float[0];\n\n    private float mRowWeight = Float.NaN;\n\n    public OnePlusNLayoutHelper() {\n        setItemCount(0);\n    }\n\n    public OnePlusNLayoutHelper(int itemCount) {\n        this(itemCount, 0, 0, 0, 0);\n    }\n\n    public OnePlusNLayoutHelper(int itemCount, int leftMargin, int topMargin, int rightMargin,\n            int bottomMargin) {\n        setItemCount(itemCount);\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p/>\n     * Currently, this layout supports maximum children up to 5, otherwise {@link\n     * IllegalArgumentException}\n     * will be thrown\n     *\n     * @param start start position of items handled by this layoutHelper\n     * @param end   end position of items handled by this layoutHelper, if end &lt; start or end -\n     *              start &gt 6, it will throw {@link IllegalArgumentException}\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        if (end - start > 6) {\n            throw new IllegalArgumentException(\n                    \"OnePlusNLayoutHelper only supports maximum 7 children now\");\n        }\n    }\n\n\n\n    public void setColWeights(float[] weights) {\n        if (weights != null) {\n            this.mColWeights = Arrays.copyOf(weights, weights.length);\n        } else {\n            this.mColWeights = new float[0];\n        }\n    }\n\n    public void setRowWeight(float weight) {\n        this.mRowWeight = weight;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n            LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        final boolean layoutStart = layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START;\n        final int parentWidth = helper.getContentWidth();\n        final int parentHeight = helper.getContentHeight();\n        final int parentHPadding = helper.getPaddingLeft() + helper.getPaddingRight()\n            + getHorizontalMargin() + getHorizontalPadding();\n        final int parentVPadding = helper.getPaddingTop() + helper.getPaddingBottom()\n            + getVerticalMargin() + getVerticalPadding();\n\n        final int currentPosition = layoutState.getCurrentPosition();\n        if (hasHeader && currentPosition == getRange().getLower()) {\n            View header = nextView(recycler, layoutState, helper, result);\n            int headerConsumed = handleHeader(header, layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,\n                parentHPadding, parentVPadding);\n            if (header != null) {\n                int left = 0, right = 0, top = 0, bottom = 0;\n                if (layoutInVertical) {\n                    if (layoutStart) {\n                        bottom = layoutState.getOffset();\n                        top = bottom - headerConsumed;\n                    } else {\n                        top = layoutState.getOffset() + (mLayoutWithAnchor ? 0 : mMarginTop + mPaddingTop);\n                        bottom = top + headerConsumed;\n                    }\n                    left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;\n                    right = left + orientationHelper.getDecoratedMeasurementInOther(header);\n                } else {\n                    if (layoutStart) {\n                        right = layoutState.getOffset();\n                        left = right - headerConsumed;\n                    } else {\n                        left = layoutState.getOffset() + (mLayoutWithAnchor ? 0 : mMarginLeft + mPaddingLeft);\n                        right = left + headerConsumed;\n                    }\n                    top = helper.getPaddingTop() + mMarginTop + mPaddingTop;\n                    bottom = top + orientationHelper.getDecoratedMeasurementInOther(header);\n                }\n                layoutChildWithMargin(header, left, top, right, bottom, helper);\n            }\n            result.mConsumed = headerConsumed;\n            handleStateOnResult(result, header);\n        } else if (hasFooter && currentPosition == getRange().getUpper()) {\n            View footer = nextView(recycler, layoutState, helper, result);\n            int footerConsumed = handleFooter(footer, layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,\n                parentHPadding, parentVPadding);\n            if (footer != null) {\n                int left = 0, right = 0, top = 0, bottom = 0;\n                if (layoutInVertical) {\n                    if (layoutStart) {\n                        bottom = layoutState.getOffset() - (mLayoutWithAnchor ? 0 : mMarginBottom + mPaddingBottom); //TODO margin overlap\n                        top = bottom - footerConsumed;\n                    } else {\n                        top = layoutState.getOffset();\n                        bottom = top + footerConsumed;\n                    }\n                    left = helper.getPaddingLeft() + mMarginLeft + mPaddingLeft;\n                    right = left + orientationHelper.getDecoratedMeasurementInOther(footer);\n                } else {\n                    if (layoutStart) {\n                        right = layoutState.getOffset() - (mLayoutWithAnchor ? 0 : mMarginRight + mPaddingRight); //TODO margin overlap\n                        left = right - footerConsumed;\n                    } else {\n                        left = layoutState.getOffset();\n                        right = left + footerConsumed;\n                    }\n                    top = helper.getPaddingTop() + mMarginTop + mPaddingTop;\n                    bottom = top + orientationHelper.getDecoratedMeasurementInOther(footer);\n                }\n                layoutChildWithMargin(footer, left, top, right, bottom, helper);\n            }\n            result.mConsumed = footerConsumed;\n            handleStateOnResult(result, footer);\n        } else {\n            int contentCount = getItemCount() - (hasHeader ? 1 : 0) - (hasFooter ? 1 : 0);\n            if (mChildrenViews == null || mChildrenViews.length != contentCount) {\n                mChildrenViews = new View[contentCount];\n            }\n            int count = getAllChildren(mChildrenViews, recycler, layoutState, result, helper);\n            if (count == 0 || count < contentCount) {\n                return;\n            }\n            int mainConsumed = 0;\n            if (contentCount == 1) {\n                mainConsumed = handleOne(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);\n            } else if (contentCount == 2) {\n                mainConsumed = handleTwo(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);\n            } else if (contentCount == 3) {\n                mainConsumed = handleThree(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);\n            } else if (contentCount == 4) {\n                mainConsumed = handleFour(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);\n            } else if (contentCount == 5) {\n                mainConsumed = handleFive(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight, parentHPadding, parentVPadding);\n            }\n            result.mConsumed = mainConsumed;\n            Arrays.fill(mChildrenViews, null);\n        }\n    }\n\n    private float getViewMainWeight(int index) {\n        if (mColWeights.length > index) {\n            return mColWeights[index];\n        }\n\n        return Float.NaN;\n    }\n\n    @Override\n    protected void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n    }\n\n    @Override\n    public void checkAnchorInfo(State state, AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        super.checkAnchorInfo(state, anchorInfo, helper);\n        mLayoutWithAnchor = true;\n    }\n\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor,\n            LayoutManagerHelper helper) {\n        //Log.d(TAG,\n        //    \"range \" + getRange() + \" offset \" + offset + \" isLayoutEnd \" + isLayoutEnd + \" useAnchor \" + useAnchor\n        //        + \" helper \" + this.hashCode());\n        final boolean layoutInVertical = helper.getOrientation() == LinearLayoutManager.VERTICAL;\n\n        if (useAnchor) {\n            return 0;\n        }\n        if (isLayoutEnd) {\n            if (offset == getItemCount() - 1) {\n                return layoutInVertical ? mMarginBottom + mPaddingBottom : mMarginRight + mPaddingRight;\n            }\n        } else {\n            if (offset == 0) {\n                return layoutInVertical ? -mMarginTop - mPaddingTop : -mMarginLeft - mPaddingLeft;\n            }\n        }\n        return 0;\n    }\n\n    private int handleHeader(View header, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        if (header == null) {\n            return 0;\n        }\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final VirtualLayoutManager.LayoutParams lp = (LayoutParams) header.getLayoutParams();\n\n\n        // fill width\n        int widthSpec = helper.getChildMeasureSpec(parentWidth - parentHPadding,\n            layoutInVertical ? MATCH_PARENT : lp.width, !layoutInVertical);\n        int heightSpec = helper.getChildMeasureSpec(parentHeight - parentVPadding,\n            layoutInVertical ? lp.height : MeasureSpec.EXACTLY, layoutInVertical);\n        helper.measureChildWithMargins(header, widthSpec, heightSpec);\n        return orientationHelper.getDecoratedMeasurement(header);\n    }\n\n    private int handleFooter(View footer, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        if (footer == null) {\n            return 0;\n        }\n\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final VirtualLayoutManager.LayoutParams lp = (LayoutParams) footer.getLayoutParams();\n\n        // fill width\n        int widthSpec = helper.getChildMeasureSpec(parentWidth - parentHPadding,\n            layoutInVertical ? MATCH_PARENT : lp.width, !layoutInVertical);\n        int heightSpec = helper.getChildMeasureSpec(parentHeight - parentVPadding,\n            layoutInVertical ? lp.height : MeasureSpec.EXACTLY, layoutInVertical);\n        helper.measureChildWithMargins(footer, widthSpec, heightSpec);\n        return orientationHelper.getDecoratedMeasurement(footer);\n    }\n\n    private int handleOne(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        View view = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp = (LayoutParams) view.getLayoutParams();\n\n        if (!Float.isNaN(mAspectRatio)) {\n            if (layoutInVertical) {\n                lp.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            } else {\n                lp.width = (int) ((parentHeight - parentVPadding) * mAspectRatio);\n            }\n        }\n\n        final float weight = getViewMainWeight(0);\n\n        // fill width\n        int widthSpec = helper.getChildMeasureSpec(\n            Float.isNaN(weight) ? (parentWidth - parentHPadding)\n                : (int) ((parentWidth - parentHPadding) * weight),\n            layoutInVertical ? MATCH_PARENT : lp.width, !layoutInVertical);\n        int heightSpec = helper.getChildMeasureSpec(parentHeight - parentVPadding,\n            layoutInVertical ? lp.height : MeasureSpec.EXACTLY, layoutInVertical);\n\n        helper.measureChildWithMargins(view, widthSpec, heightSpec);\n\n        mainConsumed += orientationHelper.getDecoratedMeasurement(view);\n\n        calculateRect(mainConsumed, mAreaRect, layoutState, helper);\n\n        layoutChildWithMargin(view, mAreaRect.left, mAreaRect.top, mAreaRect.right, mAreaRect.bottom,\n            helper);\n        handleStateOnResult(result, view);\n        mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);\n        return mainConsumed;\n    }\n\n    private int handleTwo(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = mChildrenViews[1];\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n\n\n        if (layoutInVertical) {\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = lp2.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            lp2.topMargin = lp1.topMargin;\n            lp2.bottomMargin = lp1.bottomMargin;\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin - lp2.rightMargin;\n            int width1 = Float.isNaN(weight1) ? (int) (availableSpace / 2.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? (availableSpace - width1)\n                : (int) (availableSpace * weight2 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp2.height, true));\n\n            mainConsumed += Math.max(orientationHelper.getDecoratedMeasurement(child1),\n                orientationHelper.getDecoratedMeasurement(child2));\n\n            calculateRect(mainConsumed, mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper\n                .getDecoratedMeasurementInOther(child1);\n\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);\n\n            layoutChildWithMargin(child2, right1, mAreaRect.top,\n                right1 + orientationHelper.getDecoratedMeasurementInOther(child2), mAreaRect.bottom, helper);\n\n            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);\n        } else {\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.width = lp2.width = (int) ((parentHeight - parentVPadding) * mAspectRatio);\n            }\n\n            int availableSpace = parentHeight - parentVPadding - lp1.topMargin\n                - lp1.bottomMargin\n                - lp2.topMargin - lp2.bottomMargin;\n            int height1 = Float.isNaN(weight1) ? (int) (availableSpace / 2.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int height2 = Float.isNaN(weight2) ? (int) (availableSpace - height1)\n                : (int) (availableSpace * weight2 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                helper.getChildMeasureSpec(helper.getContentWidth(), lp1.width, true),\n                MeasureSpec.makeMeasureSpec(height1 + lp1.topMargin + lp1.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            int width = child1.getMeasuredWidth();\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            mainConsumed += Math.max(orientationHelper.getDecoratedMeasurement(child1),\n                orientationHelper.getDecoratedMeasurement(child2));\n\n            calculateRect(mainConsumed, mAreaRect, layoutState, helper);\n\n            int bottom1 = mAreaRect.top + orientationHelper\n                .getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top,\n                mAreaRect.right, bottom1, helper);\n\n            layoutChildWithMargin(child2, mAreaRect.left,\n                bottom1, mAreaRect.right,\n                bottom1 + orientationHelper.getDecoratedMeasurementInOther(child2), helper);\n            mainConsumed = mAreaRect.right - mAreaRect.left + (hasHeader ? 0 : mMarginLeft + mPaddingRight) + (hasFooter ? 0 : mMarginRight + mPaddingRight);\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n        return mainConsumed;\n    }\n\n    private int handleThree(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[1];\n        final View child3 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[2];\n\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();\n\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n        final float weight3 = getViewMainWeight(2);\n\n        if (layoutInVertical) {\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            // make border consistent\n            lp2.topMargin = lp1.topMargin;\n            lp3.bottomMargin = lp1.bottomMargin;\n\n            lp3.leftMargin = lp2.leftMargin;\n            lp3.rightMargin = lp2.rightMargin;\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin - lp2.rightMargin;\n            int width1 = Float.isNaN(weight1) ? (int) (availableSpace / 2.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? (int) (availableSpace - width1)\n                : (int) (availableSpace * weight2 / 100 + 0.5);\n            int width3 = Float.isNaN(weight3) ? (int) (width2)\n                : (int) (availableSpace * weight3 / 100 + 0.5);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            int height1 = child1.getMeasuredHeight();\n            int height2 =\n                Float.isNaN(mRowWeight) ?\n                    (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)\n                    : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight\n                        / 100 + 0.5f);\n\n            int height3 = height1 - lp2.bottomMargin - lp3.topMargin - height2;\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child3,\n                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            mainConsumed += Math.max(height1 + lp1.topMargin + lp1.bottomMargin,\n                height2 + lp2.topMargin + lp2.bottomMargin + height3 + lp3.topMargin\n                    + lp3.bottomMargin);\n\n            calculateRect(mainConsumed, mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper\n                .getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);\n\n            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);\n            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,\n                mAreaRect.top + child2.getMeasuredHeight() + lp2.topMargin + lp2.bottomMargin, helper);\n\n            layoutChildWithMargin(child3, right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child3),\n                right1 + orientationHelper.getDecoratedMeasurementInOther(child3), mAreaRect.bottom, helper);\n            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);\n        } else {\n            // TODO: horizontal support\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n        return mainConsumed;\n    }\n\n    private int handleFour(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[1];\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final View child3 = mChildrenViews[2];\n        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();\n        final View child4 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[3];\n        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();\n\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n        final float weight3 = getViewMainWeight(2);\n        final float weight4 = getViewMainWeight(3);\n\n        if (layoutInVertical) {\n\n            lp2.topMargin = lp1.topMargin;\n            lp3.bottomMargin = lp4.bottomMargin = lp1.bottomMargin;\n            lp3.leftMargin = lp2.leftMargin;\n            lp4.rightMargin = lp2.rightMargin;\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin\n                - lp2.rightMargin;\n\n            int width1 = Float.isNaN(weight1) ?\n                (int) (availableSpace / 2.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? (int) (availableSpace - width1) :\n                (int) (availableSpace * weight2 / 100 + 0.5f);\n\n            int width3 = Float.isNaN(weight3) ? (int) (\n                (width2 - lp3.rightMargin - lp4.leftMargin) / 2.0f + 0.5f)\n                : (int) (availableSpace * weight3 / 100 + 0.5f);\n            int width4 = Float.isNaN(weight4) ? (int) ((width2 - lp3.rightMargin\n                - lp4.leftMargin - width3))\n                : (int) (availableSpace * weight4 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            int height1 = child1.getMeasuredHeight();\n            int height2 = Float.isNaN(mRowWeight) ?\n                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)\n                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100\n                    + 0.5f);\n            int height3 = (int) ((height1 - lp2.bottomMargin - lp3.topMargin) - height2);\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child3,\n                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child4,\n                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp4.topMargin + lp4.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            mainConsumed += Math.max(height1 + lp1.topMargin + lp1.bottomMargin,\n                height2 + lp2.topMargin + lp2.bottomMargin + Math\n                    .max(height3 + lp3.topMargin + lp3.bottomMargin,\n                        height3 + lp4.topMargin + lp4.bottomMargin));\n\n            calculateRect(mainConsumed, mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper\n                .getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);\n\n            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);\n            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2), helper);\n\n            int right3 = right1 + orientationHelper.getDecoratedMeasurementInOther(child3);\n            layoutChildWithMargin(child3, right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child3),\n                right3, mAreaRect.bottom, helper);\n\n            layoutChildWithMargin(child4, right3, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4),\n                right3 + orientationHelper.getDecoratedMeasurementInOther(child4), mAreaRect.bottom, helper);\n            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);\n        } else {\n            // TODO: horizontal support\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n        return mainConsumed;\n    }\n\n    private int handleFive(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = helper.getReverseLayout() ? mChildrenViews[4] : mChildrenViews[1];\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final View child3 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[2];\n        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();\n        final View child4 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[3];\n        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();\n        final View child5 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[4];\n        final VirtualLayoutManager.LayoutParams lp5 = (VirtualLayoutManager.LayoutParams) child5.getLayoutParams();\n\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n        final float weight3 = getViewMainWeight(2);\n        final float weight4 = getViewMainWeight(3);\n        final float weight5 = getViewMainWeight(4);\n\n        if (layoutInVertical) {\n\n            lp2.topMargin = lp1.topMargin;\n            lp3.bottomMargin = lp4.bottomMargin = lp1.bottomMargin;\n            lp3.leftMargin = lp2.leftMargin;\n            lp4.rightMargin = lp2.rightMargin;\n            lp5.rightMargin = lp2.rightMargin;\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin\n                - lp2.rightMargin;\n\n            int width1 = Float.isNaN(weight1) ?\n                (int) (availableSpace / 2.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? (int) (availableSpace - width1) :\n                (int) (availableSpace * weight2 / 100 + 0.5f);\n\n            int width3 = Float.isNaN(weight3) ? (int) (\n                (width2 - lp3.rightMargin - lp4.leftMargin) / 3.0f + 0.5f)\n                : (int) (availableSpace * weight3 / 100 + 0.5f);\n            int width4 = Float.isNaN(weight4) ? (int) (\n                (width2 - lp3.rightMargin - lp4.leftMargin) / 3.0f + 0.5f)\n                : (int) (availableSpace * weight4 / 100 + 0.5f);\n            int width5 = Float.isNaN(weight5) ? (int) ((width2 - lp3.rightMargin\n                - lp4.leftMargin - width3 - width4))\n                : (int) (availableSpace * weight5 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            int height1 = child1.getMeasuredHeight();\n            int height2 = Float.isNaN(mRowWeight) ?\n                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)\n                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100\n                    + 0.5f);\n            int height3 = (int) ((height1 - lp2.bottomMargin - lp3.topMargin) - height2);\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child3,\n                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child4,\n                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp4.topMargin + lp4.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child5,\n                MeasureSpec.makeMeasureSpec(width5 + lp5.leftMargin + lp5.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp5.topMargin + lp5.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            mainConsumed += Math.max(height1 + lp1.topMargin + lp1.bottomMargin,\n                height2 + lp2.topMargin + lp2.bottomMargin + Math\n                    .max(height3 + lp3.topMargin + lp3.bottomMargin,\n                        height3 + lp4.topMargin + lp4.bottomMargin));\n\n            calculateRect(mainConsumed, mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper.getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);\n\n            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);\n            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2), helper);\n\n            int right3 = right1 + orientationHelper.getDecoratedMeasurementInOther(child3);\n            layoutChildWithMargin(child3, right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child3),\n                right3, mAreaRect.bottom, helper);\n\n            int right4 = right3 + orientationHelper.getDecoratedMeasurementInOther(child4);\n            layoutChildWithMargin(child4, right3, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4),\n                right3 + orientationHelper.getDecoratedMeasurementInOther(child4), mAreaRect.bottom, helper);\n\n            layoutChildWithMargin(child5, right4, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child5),\n                right4 + orientationHelper.getDecoratedMeasurementInOther(child5), mAreaRect.bottom, helper);\n            mainConsumed = mAreaRect.bottom - mAreaRect.top + (hasHeader ? 0 : mMarginTop + mPaddingTop) + (hasFooter ? 0 : mMarginBottom + mPaddingBottom);\n        } else {\n            // TODO: horizontal support\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n        return mainConsumed;\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/OnePlusNLayoutHelperEx.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport android.graphics.Rect;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.MeasureSpec;\nimport android.view.ViewGroup;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport java.util.Arrays;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * <pre>\n * Currently support 1+6(max) layout\n *\n * Created by J!nl!n on 2017/3/7.\n *\n * 1 + 4\n * -------------------------\n * |       |       |       |\n * |       |   2   |   3   |\n * |       |       |       |\n * |   1   |-------|-------|\n * |       |       |       |\n * |       |   4   |   5   |\n * |       |       |       |\n * -------------------------\n *\n *  1 + 5\n * -------------------------\n * |           |     2     |\n * |     1     |-----------|\n * |           |     3     |\n * -------------------------\n * |       |       |       |\n * |   4   |   5   |   6   |\n * |       |       |       |\n * -------------------------\n *\n *  1 + 6\n * -------------------------\n * |       |   2   |   3   |\n * |       |-------|-------|\n * |   1   |   4   |   5   |\n * |       |-------|-------|\n * |       |   6   |   7   |\n * -------------------------\n * </pre>\n */\npublic class OnePlusNLayoutHelperEx extends AbstractFullFillLayoutHelper {\n\n    private static final String TAG = \"OnePlusNLayoutHelper\";\n\n    private Rect mAreaRect = new Rect();\n\n    private View[] mChildrenViews;\n\n    private float[] mColWeights = new float[0];\n\n    private float mRowWeight = Float.NaN;\n\n    public OnePlusNLayoutHelperEx() {\n        setItemCount(0);\n    }\n\n    public OnePlusNLayoutHelperEx(int itemCount) {\n        this(itemCount, 0, 0, 0, 0);\n    }\n\n    public OnePlusNLayoutHelperEx(int itemCount, int leftMargin, int topMargin, int rightMargin,\n                                  int bottomMargin) {\n        setItemCount(itemCount);\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p/>\n     * Currently, this layout supports maximum children up to 5, otherwise {@link\n     * IllegalArgumentException}\n     * will be thrown\n     *\n     * @param start start position of items handled by this layoutHelper\n     * @param end   end position of items handled by this layoutHelper, if end &lt; start or end -\n     *              start &gt 4, it will throw {@link IllegalArgumentException}\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        if (end - start < 4) {\n            throw new IllegalArgumentException(\n                    \"pls use OnePlusNLayoutHelper instead of OnePlusNLayoutHelperEx which childcount <= 5\");\n        }\n        if (end - start > 6) {\n            throw new IllegalArgumentException(\n                    \"OnePlusNLayoutHelper only supports maximum 7 children now\");\n        }\n    }\n\n    public void setColWeights(float[] weights) {\n        if (weights != null) {\n            this.mColWeights = Arrays.copyOf(weights, weights.length);\n        } else {\n            this.mColWeights = new float[0];\n        }\n    }\n\n    public void setRowWeight(float weight) {\n        this.mRowWeight = weight;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        final int originCurPos = layoutState.getCurrentPosition();\n        if (isOutOfRange(originCurPos)) {\n            return;\n        }\n\n        if (mChildrenViews == null || mChildrenViews.length != getItemCount()) {\n            mChildrenViews = new View[getItemCount()];\n        }\n\n        int count = getAllChildren(mChildrenViews, recycler, layoutState, result, helper);\n\n        if (count != getItemCount()) {\n            Log.w(TAG, \"The real number of children is not match with range of LayoutHelper\");\n        }\n\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        final int parentWidth = helper.getContentWidth();\n        final int parentHeight = helper.getContentHeight();\n        final int parentHPadding = helper.getPaddingLeft() + helper.getPaddingRight()\n                + getHorizontalMargin() + getHorizontalPadding();\n        final int parentVPadding = helper.getPaddingTop() + helper.getPaddingBottom()\n                + getVerticalMargin() + getVerticalPadding();\n\n        int mainConsumed = 0;\n\n        if (count == 5) {\n            mainConsumed = handleFive(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,\n                parentHPadding, parentVPadding);\n        } else if (count == 6) {\n            mainConsumed = handSix(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,\n                parentHPadding, parentVPadding);\n        } else if (count == 7) {\n            mainConsumed = handSeven(layoutState, result, helper, layoutInVertical, parentWidth, parentHeight,\n                parentHPadding, parentVPadding);\n        }\n\n        result.mConsumed = mainConsumed;\n\n        Arrays.fill(mChildrenViews, null);\n    }\n\n\n    private float getViewMainWeight(int index) {\n        if (mColWeights.length > index) {\n            return mColWeights[index];\n        }\n\n        return Float.NaN;\n    }\n\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor,\n                                  LayoutManagerHelper helper) {\n        if (getItemCount() == 3) {\n            if (offset == 1 && isLayoutEnd) {\n                Log.w(TAG, \"Should not happen after adjust anchor\");\n                return 0;\n            }\n        } else if (getItemCount() == 4) {\n            if (offset == 1 && isLayoutEnd) {\n                return 0;\n            }\n        }\n\n        if (helper.getOrientation() == VERTICAL) {\n            if (isLayoutEnd) {\n                return mMarginBottom + mPaddingBottom;\n            } else {\n                return -mMarginTop - mPaddingTop;\n            }\n        } else {\n            if (isLayoutEnd) {\n                return mMarginRight + mPaddingRight;\n            } else {\n                return -mMarginLeft - mPaddingLeft;\n            }\n        }\n    }\n\n    private int handleFive(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = helper.getReverseLayout() ? mChildrenViews[4] : mChildrenViews[1];\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final View child3 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[2];\n        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();\n        final View child4 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[3];\n        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();\n        final View child5 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[4];\n        final VirtualLayoutManager.LayoutParams lp5 = (VirtualLayoutManager.LayoutParams) child5.getLayoutParams();\n\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n        final float weight3 = getViewMainWeight(2);\n        final float weight4 = getViewMainWeight(3);\n        final float weight5 = getViewMainWeight(4);\n\n        if (layoutInVertical) {\n\n            lp2.topMargin = lp1.topMargin;\n            lp3.bottomMargin = lp4.bottomMargin = lp1.bottomMargin;\n            lp3.leftMargin = lp2.leftMargin;\n            lp4.rightMargin = lp2.rightMargin;\n            lp5.rightMargin = lp3.rightMargin;\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin - lp2.rightMargin\n                - lp3.leftMargin - lp3.rightMargin;\n\n            int width1 = Float.isNaN(weight1) ? (int) (availableSpace / 3.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? (availableSpace - width1) / 2\n                : (int) (availableSpace * weight2 / 100 + 0.5f);\n            int width3 = Float.isNaN(weight3) ? width2\n                : (int) (availableSpace * weight3 / 100 + 0.5f);\n            int width4 = Float.isNaN(weight4) ? width2\n                : (int) (availableSpace * weight4 / 100 + 0.5f);\n            int width5 = Float.isNaN(weight5) ? width2\n                : (int) (availableSpace * weight5 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            int height1 = child1.getMeasuredHeight();\n            int height2 = Float.isNaN(mRowWeight) ?\n                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)\n                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100\n                    + 0.5f);\n            int height3 = (height1 - lp2.bottomMargin - lp3.topMargin) - height2;\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child3,\n                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child4,\n                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp4.topMargin + lp4.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child5,\n                MeasureSpec.makeMeasureSpec(width5 + lp5.leftMargin + lp5.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp5.topMargin + lp5.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            mainConsumed = Math.max(height1 + lp1.topMargin + lp1.bottomMargin,\n                height2 + lp2.topMargin + lp2.bottomMargin + Math\n                    .max(height3 + lp3.topMargin + lp3.bottomMargin,\n                        height3 + lp4.topMargin + lp4.bottomMargin))\n                + getVerticalMargin() + getVerticalPadding();\n\n            calculateRect(mainConsumed - getVerticalMargin() - getVerticalPadding(), mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper\n                .getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top,\n                right1, mAreaRect.bottom, helper);\n\n            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);\n            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2),\n                helper);\n\n            int right3 = right2 + orientationHelper.getDecoratedMeasurementInOther(child3);\n            layoutChildWithMargin(child3, right2,\n                mAreaRect.top,\n                right3, mAreaRect.top + orientationHelper.getDecoratedMeasurement(child3), helper);\n\n            int right4 = right1 + orientationHelper.getDecoratedMeasurementInOther(child4);\n            layoutChildWithMargin(child4, right1,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4),\n                right4,\n                mAreaRect.bottom, helper);\n\n            layoutChildWithMargin(child5, right4,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child5),\n                right4 + orientationHelper.getDecoratedMeasurementInOther(child5),\n                mAreaRect.bottom, helper);\n        } else {\n            // TODO: horizontal support\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n\n        return mainConsumed;\n    }\n\n    private int handSix(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = helper.getReverseLayout() ? mChildrenViews[5] : mChildrenViews[1];\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final View child3 = helper.getReverseLayout() ? mChildrenViews[4] : mChildrenViews[2];\n        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();\n        final View child4 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[3];\n        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();\n        final View child5 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[4];\n        final VirtualLayoutManager.LayoutParams lp5 = (VirtualLayoutManager.LayoutParams) child5.getLayoutParams();\n        final View child6 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[5];\n        final VirtualLayoutManager.LayoutParams lp6 = (VirtualLayoutManager.LayoutParams) child6.getLayoutParams();\n\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n        final float weight3 = getViewMainWeight(2);\n        final float weight4 = getViewMainWeight(3);\n        final float weight5 = getViewMainWeight(4);\n        final float weight6 = getViewMainWeight(5);\n\n        if (layoutInVertical) {\n\n            lp2.topMargin = lp1.topMargin;\n            lp3.bottomMargin = lp4.bottomMargin = lp1.bottomMargin;\n            lp3.leftMargin = lp2.leftMargin;\n            lp4.rightMargin = lp2.rightMargin;\n            lp5.rightMargin = lp2.rightMargin;\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin\n                - lp2.rightMargin;\n\n            int width1 = Float.isNaN(weight1) ?\n                (int) (availableSpace / 2.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? availableSpace - width1 :\n                (int) (availableSpace * weight2 / 100 + 0.5f);\n            int width3 = Float.isNaN(weight3) ? width2\n                : (int) (availableSpace * weight3 / 100 + 0.5);\n\n            int bottomavailableSpace = parentWidth - parentHPadding - lp4.leftMargin - lp4.rightMargin\n                - lp5.leftMargin - lp5.rightMargin\n                - lp6.leftMargin - lp6.rightMargin;\n\n            int width4 = Float.isNaN(weight4) ? (int) (bottomavailableSpace / 3.0f + 0.5f)\n                : (int) (availableSpace * weight4 / 100 + 0.5f);\n            int width5 = Float.isNaN(weight5) ? width4\n                : (int) (availableSpace * weight5 / 100 + 0.5f);\n            int width6 = Float.isNaN(weight6) ? width4\n                : (int) (availableSpace * weight6 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            int height1 = child1.getMeasuredHeight();\n            int height2 = Float.isNaN(mRowWeight) ?\n                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 2.0f + 0.5f)\n                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100\n                    + 0.5f);\n            int height3 = (height1 - lp2.bottomMargin - lp3.topMargin) - height2;\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child3,\n                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp3.topMargin + lp3.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child4,\n                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp4.topMargin + lp4.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child5,\n                MeasureSpec.makeMeasureSpec(width5 + lp5.leftMargin + lp5.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp5.topMargin + lp5.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child6,\n                MeasureSpec.makeMeasureSpec(width6 + lp6.leftMargin + lp6.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height3 + lp6.topMargin + lp6.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n\n            int maxTopHeight = Math.max(height1 + lp1.topMargin + lp1.bottomMargin,\n                (height2 + lp2.topMargin + lp2.bottomMargin) * 2);\n\n            int maxBottomHeight = Math.max(height3 + lp4.topMargin + lp4.bottomMargin,\n                Math.max(height3 + lp5.topMargin + lp5.bottomMargin,\n                    height3 + lp6.topMargin + lp6.bottomMargin));\n\n            mainConsumed = maxTopHeight + maxBottomHeight\n                + getVerticalMargin() + getVerticalPadding();\n\n            calculateRect(mainConsumed - getVerticalMargin() - getVerticalPadding(), mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper\n                .getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top,\n                right1, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4), helper);\n\n            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);\n            layoutChildWithMargin(child2, right1, mAreaRect.top, right2,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2),\n                helper);\n\n            int right3 = right1 + orientationHelper.getDecoratedMeasurementInOther(child3);\n            layoutChildWithMargin(child3, right1,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child3),\n                right3, mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4), helper);\n\n            int right4 = mAreaRect.left + orientationHelper.getDecoratedMeasurementInOther(child4);\n            layoutChildWithMargin(child4, mAreaRect.left,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child4),\n                right4,\n                mAreaRect.bottom, helper);\n\n            int right5 = right4 + orientationHelper.getDecoratedMeasurementInOther(child5);\n            layoutChildWithMargin(child5, right4,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child5),\n                right5,\n                mAreaRect.bottom, helper);\n\n            int right6 = right5 + orientationHelper.getDecoratedMeasurementInOther(child6);\n            layoutChildWithMargin(child6, right5,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child6),\n                right6,\n                mAreaRect.bottom, helper);\n        } else {\n            // TODO: horizontal support\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n        return mainConsumed;\n    }\n\n    private int handSeven(LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper,\n        boolean layoutInVertical, int parentWidth, int parentHeight, int parentHPadding, int parentVPadding) {\n        int mainConsumed = 0;\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final View child1 = mChildrenViews[0];\n        final VirtualLayoutManager.LayoutParams lp1 = (VirtualLayoutManager.LayoutParams) child1.getLayoutParams();\n        final View child2 = helper.getReverseLayout() ? mChildrenViews[6] : mChildrenViews[1];\n        final VirtualLayoutManager.LayoutParams lp2 = (VirtualLayoutManager.LayoutParams) child2.getLayoutParams();\n        final View child3 = helper.getReverseLayout() ? mChildrenViews[5] : mChildrenViews[2];\n        final VirtualLayoutManager.LayoutParams lp3 = (VirtualLayoutManager.LayoutParams) child3.getLayoutParams();\n        final View child4 = helper.getReverseLayout() ? mChildrenViews[4] : mChildrenViews[3];\n        final VirtualLayoutManager.LayoutParams lp4 = (VirtualLayoutManager.LayoutParams) child4.getLayoutParams();\n        final View child5 = helper.getReverseLayout() ? mChildrenViews[3] : mChildrenViews[4];\n        final VirtualLayoutManager.LayoutParams lp5 = (VirtualLayoutManager.LayoutParams) child5.getLayoutParams();\n        final View child6 = helper.getReverseLayout() ? mChildrenViews[2] : mChildrenViews[5];\n        final VirtualLayoutManager.LayoutParams lp6 = (VirtualLayoutManager.LayoutParams) child6.getLayoutParams();\n        final View child7 = helper.getReverseLayout() ? mChildrenViews[1] : mChildrenViews[6];\n        final VirtualLayoutManager.LayoutParams lp7 = (VirtualLayoutManager.LayoutParams) child7.getLayoutParams();\n\n        final float weight1 = getViewMainWeight(0);\n        final float weight2 = getViewMainWeight(1);\n        final float weight3 = getViewMainWeight(2);\n        final float weight4 = getViewMainWeight(3);\n        final float weight5 = getViewMainWeight(4);\n        final float weight6 = getViewMainWeight(5);\n        final float weight7 = getViewMainWeight(6);\n\n        if (layoutInVertical) {\n\n            if (!Float.isNaN(mAspectRatio)) {\n                lp1.height = (int) ((parentWidth - parentHPadding) / mAspectRatio);\n            }\n\n            int availableSpace = parentWidth - parentHPadding - lp1.leftMargin - lp1.rightMargin\n                - lp2.leftMargin - lp2.rightMargin\n                - lp3.leftMargin - lp3.rightMargin;\n\n            int width1 = Float.isNaN(weight1) ?\n                (int) (availableSpace / 3.0f + 0.5f)\n                : (int) (availableSpace * weight1 / 100 + 0.5f);\n            int width2 = Float.isNaN(weight2) ? (availableSpace - width1) / 2 :\n                (int) (availableSpace * weight2 / 100 + 0.5f);\n            int width3 = Float.isNaN(weight3) ? width2\n                : (int) (availableSpace * weight3 / 100 + 0.5);\n\n            int width4 = Float.isNaN(weight4) ? width2\n                : (int) (availableSpace * weight4 / 100 + 0.5f);\n            int width5 = Float.isNaN(weight5) ? width2\n                : (int) (availableSpace * weight5 / 100 + 0.5f);\n            int width6 = Float.isNaN(weight6) ? width2\n                : (int) (availableSpace * weight6 / 100 + 0.5f);\n            int width7 = Float.isNaN(weight6) ? width2\n                : (int) (availableSpace * weight7 / 100 + 0.5f);\n\n            helper.measureChildWithMargins(child1,\n                MeasureSpec.makeMeasureSpec(width1 + lp1.leftMargin + lp1.rightMargin,\n                    MeasureSpec.EXACTLY),\n                helper.getChildMeasureSpec(helper.getContentHeight(), lp1.height, true));\n\n            int height1 = child1.getMeasuredHeight();\n            int height2 = Float.isNaN(mRowWeight) ?\n                (int) ((height1 - lp2.bottomMargin - lp3.topMargin) / 3.0f + 0.5f)\n                : (int) ((height1 - lp2.bottomMargin - lp3.topMargin) * mRowWeight / 100\n                    + 0.5f);\n\n            helper.measureChildWithMargins(child2,\n                MeasureSpec.makeMeasureSpec(width2 + lp2.leftMargin + lp2.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp2.topMargin + lp2.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child3,\n                MeasureSpec.makeMeasureSpec(width3 + lp3.leftMargin + lp3.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp3.topMargin + lp3.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child4,\n                MeasureSpec.makeMeasureSpec(width4 + lp4.leftMargin + lp4.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp4.topMargin + lp4.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child5,\n                MeasureSpec.makeMeasureSpec(width5 + lp5.leftMargin + lp5.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp5.topMargin + lp5.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child6,\n                MeasureSpec.makeMeasureSpec(width6 + lp6.leftMargin + lp6.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp6.topMargin + lp6.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n            helper.measureChildWithMargins(child7,\n                MeasureSpec.makeMeasureSpec(width7 + lp7.leftMargin + lp7.rightMargin,\n                    MeasureSpec.EXACTLY),\n                MeasureSpec.makeMeasureSpec(height2 + lp7.topMargin + lp7.bottomMargin,\n                    MeasureSpec.EXACTLY));\n\n\n            int maxRightHeight =\n                Math.max(height2 + lp2.topMargin + lp2.bottomMargin, height2 + lp3.topMargin + lp3.bottomMargin) +\n                    Math.max(height2 + lp4.topMargin + lp4.bottomMargin, height2 + lp5.topMargin + lp5.bottomMargin) +\n                    Math.max(height2 + lp6.topMargin + lp6.bottomMargin, height2 + lp7.topMargin + lp7.bottomMargin);\n\n            int maxHeight = Math.max(height1 + lp1.topMargin + lp1.bottomMargin, maxRightHeight);\n\n            mainConsumed = maxHeight\n                + getVerticalMargin() + getVerticalPadding();\n\n            calculateRect(mainConsumed - getVerticalMargin() - getVerticalPadding(), mAreaRect, layoutState, helper);\n\n            int right1 = mAreaRect.left + orientationHelper.getDecoratedMeasurementInOther(child1);\n            layoutChildWithMargin(child1, mAreaRect.left, mAreaRect.top, right1, mAreaRect.bottom, helper);\n\n            int right2 = right1 + orientationHelper.getDecoratedMeasurementInOther(child2);\n            layoutChildWithMargin(child2, right1,\n                mAreaRect.top,\n                right2,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2), helper);\n\n            int right3 = right2 + orientationHelper.getDecoratedMeasurementInOther(child3);\n            layoutChildWithMargin(child3, right2, mAreaRect.top, right3,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child3), helper);\n\n            int right4 = right1 + orientationHelper.getDecoratedMeasurementInOther(child4);\n            layoutChildWithMargin(child4, right1,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2),\n                right4,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child6), helper);\n\n            int right5 = right4 + orientationHelper.getDecoratedMeasurementInOther(child5);\n            layoutChildWithMargin(child5, right4,\n                mAreaRect.top + orientationHelper.getDecoratedMeasurement(child2),\n                right5,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child7), helper);\n\n            int right6 = right1 + orientationHelper.getDecoratedMeasurementInOther(child6);\n            layoutChildWithMargin(child6, right1,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child6),\n                right6,\n                mAreaRect.bottom, helper);\n\n            layoutChildWithMargin(child7, right6,\n                mAreaRect.bottom - orientationHelper.getDecoratedMeasurement(child7),\n                right6 + orientationHelper.getDecoratedMeasurementInOther(child7),\n                mAreaRect.bottom, helper);\n        } else {\n            // TODO: horizontal support\n        }\n\n        handleStateOnResult(result, mChildrenViews);\n        return mainConsumed;\n    }\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/RangeGridLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport android.support.annotation.NonNull;\nimport android.support.v4.util.ArrayMap;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.RecyclerView.Recycler;\nimport android.support.v7.widget.RecyclerView.State;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.Range;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutParams;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\nimport com.alibaba.android.vlayout.layout.GridLayoutHelper.DefaultSpanSizeLookup;\nimport com.alibaba.android.vlayout.layout.GridLayoutHelper.SpanSizeLookup;\n\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;\n\n/**\n * LayoutHelper provides RangeGridLayoutHelper. The difference with {@link GridLayoutHelper} is that this layoutHelper could has child group logically but implemented as flat.\n *\n * @author longerian\n * @since 1.0.0\n */\npublic class RangeGridLayoutHelper extends BaseLayoutHelper {\n    private static final String TAG = \"RGLayoutHelper\";\n\n    @SuppressWarnings(\"FieldCanBeLocal\")\n    private static boolean DEBUG = false;\n\n    private GridRangeStyle mRangeStyle;\n\n    private int mTotalSize = 0;\n\n    /**\n     * @param spanCount number of columns/rows in grid, must be greater than 0\n     */\n    public RangeGridLayoutHelper(int spanCount) {\n        this(spanCount, -1, -1);\n    }\n\n    /**\n     * @param spanCount number of columns/rows in grid, must be greater than 0\n     * @param itemCount number of items in this layoutHelper\n     */\n    public RangeGridLayoutHelper(int spanCount, int itemCount) {\n        this(spanCount, itemCount, 0);\n    }\n\n    public RangeGridLayoutHelper(int spanCount, int itemCount, int gap) {\n        this(spanCount, itemCount, gap, gap);\n    }\n\n    /**\n     * @param spanCount number of columns/rows in grid, must be greater than 0\n     * @param itemCount number of items in this layoutHelper\n     * @param vGap      vertical gap\n     * @param hGap      horizontal gap\n     */\n    public RangeGridLayoutHelper(int spanCount, int itemCount, int vGap, int hGap) {\n        mRangeStyle = new GridRangeStyle(this);\n        mRangeStyle.setSpanCount(spanCount);\n        mRangeStyle.setVGap(vGap);\n        mRangeStyle.setHGap(hGap);\n        setItemCount(itemCount);\n    }\n\n    /**\n     *\n     * @param start offset relative to its parent\n     * @param end offset relative to its parent\n     * @param rangeStyle new range style\n     */\n    public void addRangeStyle(int start, int end, GridRangeStyle rangeStyle) {\n        mRangeStyle.addChildRangeStyle(start, end, rangeStyle);\n    }\n\n    public GridRangeStyle getRootRangeStyle() {\n        return mRangeStyle;\n    }\n\n    @Override\n    public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin) {\n        super.setMargin(leftMargin, topMargin, rightMargin, bottomMargin);\n        mRangeStyle.setMargin(leftMargin, topMargin, rightMargin, bottomMargin);\n    }\n\n    @Override\n    public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding) {\n        super.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);\n        mRangeStyle.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);\n    }\n\n    public void setWeights(float[] weights) {\n        mRangeStyle.setWeights(weights);\n    }\n\n    public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {\n        mRangeStyle.setSpanSizeLookup(spanSizeLookup);\n    }\n\n    public void setAutoExpand(boolean isAutoExpand) {\n        mRangeStyle.setAutoExpand(isAutoExpand);\n    }\n\n    public void setIgnoreExtra(boolean ignoreExtra) {\n        mRangeStyle.setIgnoreExtra(ignoreExtra);\n    }\n\n\n    /**\n     * {@inheritDoc}\n     * Set SpanCount for grid\n     *\n     * @param spanCount grid column number, must be greater than 0. {@link IllegalArgumentException}\n     *                  will be thrown otherwise\n     */\n    public void setSpanCount(int spanCount) {\n        mRangeStyle.setSpanCount(spanCount);\n    }\n\n    public int getSpanCount() {\n        return mRangeStyle.getSpanCount();\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @param start start position of items handled by this layoutHelper\n     * @param end   end position of items handled by this layoutHelper, if end < start, it will throw {@link IllegalArgumentException}\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        mRangeStyle.setRange(start, end);\n    }\n\n    public void setGap(int gap) {\n        setVGap(gap);\n        setHGap(gap);\n    }\n\n    public void setVGap(int vGap) {\n        mRangeStyle.setVGap(vGap);\n    }\n\n    public void setHGap(int hGap) {\n        mRangeStyle.setHGap(hGap);\n    }\n\n    @Override\n    public void setAspectRatio(float aspectRatio) {\n        mRangeStyle.setAspectRatio(aspectRatio);\n    }\n\n    @Override\n    public float getAspectRatio() {\n        return mRangeStyle.getAspectRatio();\n    }\n\n    @Override\n    public void setBgColor(int bgColor) {\n        mRangeStyle.setBgColor(bgColor);\n    }\n\n    @Override\n    public void setLayoutViewHelper(DefaultLayoutViewHelper layoutViewHelper) {\n        mRangeStyle.setLayoutViewHelper(layoutViewHelper);\n    }\n\n    @Override\n    public void setLayoutViewBindListener(LayoutViewBindListener bindListener) {\n        mRangeStyle.setLayoutViewBindListener(bindListener);\n    }\n\n    @Override\n    public void setLayoutViewUnBindListener(LayoutViewUnBindListener layoutViewUnBindListener) {\n        mRangeStyle.setLayoutViewUnBindListener(layoutViewUnBindListener);\n    }\n\n    @Override\n    public boolean requireLayoutView() {\n        return mRangeStyle.requireLayoutView();\n    }\n\n    @Override\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n        LayoutManagerHelper helper) {\n        mRangeStyle.beforeLayout(recycler, state, helper);\n    }\n\n    //TODO optimize this method\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        boolean isStartLine = false, isEndLine = false;\n        boolean isSecondStartLine = false, isSecondEndLine = false;\n        final int currentPosition = layoutState.getCurrentPosition();\n        GridRangeStyle rangeStyle = mRangeStyle.findRangeStyleByPosition(currentPosition);\n\n        final int itemDirection = layoutState.getItemDirection();\n        final boolean layingOutInPrimaryDirection =\n            itemDirection == LayoutStateWrapper.ITEM_DIRECTION_TAIL;\n\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        if (layoutInVertical) {\n            mTotalSize = helper.getContentWidth() - helper.getPaddingRight() - helper.getPaddingLeft() - rangeStyle\n                .getFamilyHorizontalMargin() - rangeStyle.getFamilyHorizontalPadding();\n            rangeStyle.mSizePerSpan = (int) ((mTotalSize - (\n                rangeStyle.mSpanCount - 1) * rangeStyle.mHGap) * 1.0f / rangeStyle.mSpanCount + 0.5f);\n        } else {\n            mTotalSize = helper.getContentHeight() - helper.getPaddingBottom() - helper.getPaddingTop() - rangeStyle\n                .getFamilyVerticalMargin() - rangeStyle.getFamilyVerticalPadding();\n            rangeStyle.mSizePerSpan = (int) ((mTotalSize - (\n                rangeStyle.mSpanCount - 1) * rangeStyle.mVGap) * 1.0f / rangeStyle.mSpanCount + 0.5f);\n        }\n\n        int count = 0;\n        int consumedSpanCount = 0;\n        int remainingSpan = rangeStyle.mSpanCount;\n\n        rangeStyle.ensureSpanCount();\n\n        if (!layingOutInPrimaryDirection) {\n            // fill the remaining spacing this row\n            int itemSpanIndex = getSpanIndex(rangeStyle.mSpanSizeLookup, rangeStyle.mSpanCount, recycler, state, currentPosition);\n            int itemSpanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, currentPosition);\n\n\n            remainingSpan = itemSpanIndex + itemSpanSize;\n\n            // should find the last element of this row\n            if (itemSpanIndex != rangeStyle.mSpanCount - 1) {\n                int index = layoutState.getCurrentPosition();\n                int revRemainingSpan = rangeStyle.mSpanCount - remainingSpan;\n                while (count < rangeStyle.mSpanCount && revRemainingSpan > 0) {\n                    // go reverse direction to find views fill current row\n                    index -= itemDirection;\n                    if (rangeStyle.isOutOfRange(index)) {\n                        break;\n                    }\n                    final int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, index);\n                    if (spanSize > rangeStyle.mSpanCount) {\n                        throw new IllegalArgumentException(\"Item at position \" + index + \" requires \" +\n                            spanSize + \" spans but RangeGridLayoutHelper has only \" + rangeStyle.mSpanCount\n                            + \" spans.\");\n                    }\n\n                    View view = layoutState.retrieve(recycler, index);\n                    if (view == null) {\n                        break;\n                    }\n\n                    if (!isStartLine) {\n                        isStartLine = helper.getReverseLayout() ? index == mRangeStyle.getRange().getUpper().intValue()\n                            : index == mRangeStyle.getRange().getLower().intValue();\n                    }\n\n\n                    if (!isEndLine) {\n                        isEndLine = helper.getReverseLayout() ? index == mRangeStyle.getRange().getLower().intValue()\n                            : index == mRangeStyle.getRange().getUpper().intValue();\n                    }\n\n\n                    revRemainingSpan -= spanSize;\n                    if (revRemainingSpan < 0) {\n                        break;\n                    }\n\n\n                    consumedSpanCount += spanSize;\n                    rangeStyle.mSet[count] = view;\n                    count++;\n                }\n\n                if (count > 0) {\n                    // reverse array\n                    int s = 0, e = count - 1;\n                    while (s < e) {\n                        View temp = rangeStyle.mSet[s];\n                        rangeStyle.mSet[s] = rangeStyle.mSet[e];\n                        rangeStyle.mSet[e] = temp;\n                        s++;\n                        e--;\n                    }\n                }\n            }\n        }\n\n        while (count < rangeStyle.mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {\n            int pos = layoutState.getCurrentPosition();\n            if (rangeStyle.isOutOfRange(pos)) {\n                if (DEBUG) {\n                    Log.d(TAG, \"pos [\" + pos + \"] is out of range\");\n                }\n                break;\n            }\n\n            final int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, pos);\n            if (spanSize > rangeStyle.mSpanCount) {\n                throw new IllegalArgumentException(\"Item at position \" + pos + \" requires \" +\n                    spanSize + \" spans but GridLayoutManager has only \" + rangeStyle.mSpanCount\n                    + \" spans.\");\n            }\n            remainingSpan -= spanSize;\n            if (remainingSpan < 0) {\n                break; // item did not fit into this row or column\n            }\n\n            if (!isStartLine) {\n                isStartLine = helper.getReverseLayout() ? pos == mRangeStyle.getRange().getUpper().intValue()\n                    : pos == mRangeStyle.getRange().getLower().intValue();\n            }\n            if (!isSecondStartLine) {\n                if (!rangeStyle.equals(mRangeStyle)) {\n                    isSecondStartLine = helper.getReverseLayout() ? pos == rangeStyle.getRange().getUpper()\n                        .intValue() : pos == rangeStyle.getRange().getLower().intValue();\n                }\n            }\n\n            if (!isEndLine) {\n                isEndLine = helper.getReverseLayout() ? pos == mRangeStyle.getRange().getLower().intValue()\n                    : pos == mRangeStyle.getRange().getUpper().intValue();\n            }\n\n            if (!isSecondEndLine) {\n                if (!rangeStyle.equals(mRangeStyle)) {\n                    isSecondEndLine = helper.getReverseLayout() ? pos == rangeStyle.getRange().getLower()\n                        .intValue() : pos == rangeStyle.getRange().getUpper().intValue();\n                    if (DEBUG) {\n                        Log.d(TAG, \"isSecondEndLineLogic:\" + isSecondEndLine + \"  helper.getReverseLayout()=\" + helper.getReverseLayout() + \" pos=\" + pos + \" rangeStyle.getRange().getLower()=\" + rangeStyle.getRange().getLower() + \" rangeStyle.getRange().getUpper()=\" + rangeStyle.getRange().getUpper());\n                    }\n                }\n            }\n\n            View view = layoutState.next(recycler);\n            if (view == null) {\n                break;\n            }\n\n            consumedSpanCount += spanSize;\n            rangeStyle.mSet[count] = view;\n            count++;\n        }\n\n\n        if (count == 0) {\n            return;\n        }\n\n        int maxSize = 0;\n\n\n        // we should assign spans before item decor offsets are calculated\n        assignSpans(rangeStyle, recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection, helper);\n\n        if (remainingSpan > 0 && (count == consumedSpanCount) && rangeStyle.mIsAutoExpand) {\n            //autoExpand only support when each cell occupy one span.\n            if (layoutInVertical) {\n                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mHGap) / count;\n            } else {\n                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mVGap) / count;\n            }\n        } else if (!layingOutInPrimaryDirection && remainingSpan == 0 && (count == consumedSpanCount) && rangeStyle.mIsAutoExpand) {\n            //autoExpand only support when each cell occupy one span.\n            if (layoutInVertical) {\n                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mHGap) / count;\n            } else {\n                rangeStyle.mSizePerSpan = (mTotalSize - (count - 1) * rangeStyle.mVGap) / count;\n            }\n        }\n\n\n        boolean weighted = false;\n        if (rangeStyle.mWeights != null && rangeStyle.mWeights.length > 0) {\n            weighted = true;\n            int totalSpace;\n            if (layoutInVertical) {\n                totalSpace = mTotalSize - (count - 1) * rangeStyle.mHGap;\n            } else {\n                totalSpace = mTotalSize - (count - 1) * rangeStyle.mVGap;\n            }\n\n            // calculate width with weight in percentage\n\n            int eqCnt = 0, remainingSpace = totalSpace;\n            int colCnt = (remainingSpan > 0 && rangeStyle.mIsAutoExpand) ? count : rangeStyle.mSpanCount;\n            for (int i = 0; i < colCnt; i++) {\n                if (i < rangeStyle.mWeights.length && !Float.isNaN(rangeStyle.mWeights[i]) && rangeStyle.mWeights[i] >= 0) {\n                    float weight = rangeStyle.mWeights[i];\n                    rangeStyle.mSpanCols[i] = (int) (weight * 1.0f / 100 * totalSpace + 0.5f);\n                    remainingSpace -= rangeStyle.mSpanCols[i];\n                } else {\n                    eqCnt++;\n                    rangeStyle.mSpanCols[i] = -1;\n                }\n            }\n\n            if (eqCnt > 0) {\n                int eqLength = remainingSpace / eqCnt;\n                for (int i = 0; i < colCnt; i++) {\n                    if (rangeStyle.mSpanCols[i] < 0) {\n                        rangeStyle.mSpanCols[i] = eqLength;\n                    }\n                }\n            }\n        }\n\n\n        for (int i = 0; i < count; i++) {\n            View view = rangeStyle.mSet[i];\n            helper.addChildView(layoutState, view, layingOutInPrimaryDirection ? -1 : 0);\n\n            int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)), spec;\n            if (weighted) {\n                final int index = rangeStyle.mSpanIndices[i];\n                int spanLength = 0;\n                for (int j = 0; j < spanSize; j++) {\n                    spanLength += rangeStyle.mSpanCols[j + index];\n                }\n\n                spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);\n            } else {\n                spec = View.MeasureSpec.makeMeasureSpec(rangeStyle.mSizePerSpan * spanSize +\n                        Math.max(0, spanSize - 1) * (layoutInVertical ? rangeStyle.mHGap : rangeStyle.mVGap),\n                    View.MeasureSpec.EXACTLY);\n            }\n            final LayoutParams lp = (LayoutParams) view.getLayoutParams();\n            if (helper.getOrientation() == VERTICAL) {\n                helper.measureChildWithMargins(view, spec, getMainDirSpec(rangeStyle, lp.height, mTotalSize,\n                    View.MeasureSpec.getSize(spec), lp.mAspectRatio));\n            } else {\n                helper.measureChildWithMargins(view,\n                    getMainDirSpec(rangeStyle, lp.width, mTotalSize, View.MeasureSpec.getSize(spec),\n                        lp.mAspectRatio), View.MeasureSpec.getSize(spec));\n            }\n            final int size = orientationHelper.getDecoratedMeasurement(view);\n            if (size > maxSize) {\n                maxSize = size;\n            }\n        }\n\n        // views that did not measure the maxSize has to be re-measured\n        final int maxMeasureSpec = getMainDirSpec(rangeStyle, maxSize, mTotalSize, 0, Float.NaN);\n        for (int i = 0; i < count; i++) {\n            final View view = rangeStyle.mSet[i];\n            if (orientationHelper.getDecoratedMeasurement(view) != maxSize) {\n                int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view)), spec;\n                if (weighted) {\n                    final int index = rangeStyle.mSpanIndices[i];\n                    int spanLength = 0;\n                    for (int j = 0; j < spanSize; j++) {\n                        spanLength += rangeStyle.mSpanCols[j + index];\n                    }\n\n                    spec = View.MeasureSpec.makeMeasureSpec(Math.max(0, spanLength), View.MeasureSpec.EXACTLY);\n                } else {\n                    spec = View.MeasureSpec.makeMeasureSpec(rangeStyle.mSizePerSpan * spanSize +\n                            Math.max(0, spanSize - 1) * (layoutInVertical ? rangeStyle.mHGap : rangeStyle.mVGap),\n                        View.MeasureSpec.EXACTLY);\n                }\n\n                if (helper.getOrientation() == VERTICAL) {\n                    helper.measureChildWithMargins(view, spec, maxMeasureSpec);\n                } else {\n                    helper.measureChildWithMargins(view, maxMeasureSpec, spec);\n                }\n            }\n        }\n\n        int startSpace = 0, endSpace = 0;\n\n        int secondStartSpace = 0, secondEndSpace = 0;\n        boolean isLayoutEnd = layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_END;\n        final boolean isOverLapMargin = helper.isEnableMarginOverLap();\n\n        if (isStartLine) {\n            startSpace = computeStartSpace(helper, layoutInVertical, isLayoutEnd, isOverLapMargin);\n        }\n        if (isSecondStartLine) {\n            secondStartSpace = (layoutInVertical ? rangeStyle.getMarginTop() + rangeStyle.getPaddingTop()\n                : rangeStyle.getMarginLeft() + rangeStyle.getPaddingLeft());\n        }\n\n        if (isEndLine) {\n            endSpace = layoutInVertical ? mRangeStyle.getMarginBottom() + mRangeStyle.getPaddingBottom()\n                : mRangeStyle.getMarginRight() + mRangeStyle.getPaddingRight();\n        }\n        if (isSecondEndLine) {\n            secondEndSpace = (layoutInVertical ? rangeStyle.getMarginBottom() + rangeStyle.getPaddingBottom()\n                : rangeStyle.getMarginRight() + rangeStyle.getPaddingRight());\n            if (DEBUG) {\n                Log.d(TAG, \"isSecondEndLineLogic:\" + isSecondEndLine + \" pos=\" + currentPosition + \" secondEndSpace=\" + secondEndSpace);\n            }\n        }\n\n\n        result.mConsumed = maxSize + startSpace + endSpace + secondStartSpace + secondEndSpace;\n\n        final boolean layoutStart = layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START;\n        int consumedGap = 0;\n        if (!mLayoutWithAnchor) {\n            if (!layoutStart) {\n                if (!isStartLine) {\n                    if (isSecondStartLine) {\n                        consumedGap = (layoutInVertical ? rangeStyle.mParent.mVGap : rangeStyle.mParent.mHGap);\n                        if (DEBUG) {\n                            Log.d(TAG, \"⬇ \" + currentPosition + \" 1 \" + consumedGap + \" gap\");\n                        }\n                    } else {\n                        consumedGap = (layoutInVertical ? rangeStyle.mVGap : rangeStyle.mHGap);\n                        if (DEBUG) {\n                            Log.d(TAG, \"⬇ \" + currentPosition + \" 2 \" + consumedGap + \" gap\");\n                        }\n                    }\n                }\n            } else {\n                if (!isEndLine) {\n                    if (isSecondEndLine) {\n                        consumedGap = (layoutInVertical ? rangeStyle.mParent.mVGap : rangeStyle.mParent.mHGap);\n                        if (DEBUG) {\n                            Log.d(TAG, \"⬆ \" + currentPosition + \" 3 \" + consumedGap + \" gap\");\n                        }\n                    } else {\n                        consumedGap = (layoutInVertical ? rangeStyle.mVGap : rangeStyle.mHGap);\n                        if (DEBUG) {\n                            Log.d(TAG, \"⬆ \" + currentPosition + \" 4 \" + consumedGap + \" gap\");\n                        }\n                    }\n                }\n            }\n        }\n        result.mConsumed += consumedGap;\n\n        if (result.mConsumed <= 0) {\n            result.mConsumed = 0;\n        }\n\n        int lastUnconsumedSpace = 0;\n        /** layoutView() may be triggered by layoutManager's scrollInternalBy() or onFocusSearchFailed() or onLayoutChildren()\n         *\n         * In case of scrollInternalBy() or onFocusSearchFailed(), layoutState.isRefreshLayout() == false, and layoutState.mOffset = ChildClosestToExpose + alignOffset,\n         * see {@link com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx#updateLayoutStateExpose(int, int, boolean, State)},\n         * this means last line's layout padding or margin is not really consumed, so considering it before layout new line.\n         *\n         * In case of onLayoutChildren(), layoutState.isRefreshLayout() == true, and layoutState.mOffset = anchorInfo.mCoordinate = anchorChild.start + alignOffset,\n         * see {@link com.alibaba.android.vlayout.ExposeLinearLayoutManagerEx#updateAnchorInfoForLayoutExpose(State, AnchorInfo)},\n         * this means last line's layout padding or margin is consumed.\n         **/\n        if (!layoutState.isRefreshLayout()) {\n            if (layoutStart) {\n                int lastLinePosition = currentPosition + 1;\n                if (!isOutOfRange(lastLinePosition)) {\n                    RangeStyle<GridRangeStyle> neighbourRange = mRangeStyle.findRangeStyleByPosition(lastLinePosition);\n                    if (neighbourRange.isFirstPosition(lastLinePosition)) {\n                        lastUnconsumedSpace = layoutInVertical ? neighbourRange.getMarginTop() + neighbourRange.getPaddingTop()\n                            : neighbourRange.getMarginLeft() + neighbourRange.getPaddingLeft();\n                        if (DEBUG) {\n                            Log.d(TAG, \"⬆ \" + currentPosition + \" 1 \" + lastUnconsumedSpace + \" last\");\n                        }\n                    }\n                }\n            } else {\n                int lastLinePosition = currentPosition - 1;\n                if (!isOutOfRange(lastLinePosition)) {\n                    RangeStyle<GridRangeStyle> neighbourRange = mRangeStyle.findRangeStyleByPosition(lastLinePosition);\n                    if (neighbourRange.isLastPosition(lastLinePosition)) {\n                        lastUnconsumedSpace = layoutInVertical ? neighbourRange.getMarginBottom() + neighbourRange.getPaddingBottom()\n                            : neighbourRange.getMarginRight() + neighbourRange.getPaddingRight();\n                        if (DEBUG) {\n                            Log.d(TAG, \"⬇ \" + currentPosition + \" 2 \" + lastUnconsumedSpace + \" last\");\n                        }\n                    }\n                }\n            }\n        }\n\n        if (DEBUG) {\n            Log.d(TAG,\n                    (layoutStart ? \"⬆ \" : \"⬇ \") + currentPosition + \" consumed \" + result.mConsumed + \" startSpace \" + startSpace + \" endSpace \"\n                            + endSpace + \" secondStartSpace \" + secondStartSpace + \" secondEndSpace \" + secondEndSpace + \" lastUnconsumedSpace \" + lastUnconsumedSpace + \" isSecondEndLine=\" + isSecondEndLine);\n        }\n\n        int left = 0, right = 0, top = 0, bottom = 0;\n        if (layoutInVertical) {\n            if (layoutStart) {\n                bottom = layoutState.getOffset() - endSpace - secondEndSpace - (consumedGap) - lastUnconsumedSpace;\n                top = bottom - maxSize;\n            } else {\n                top = layoutState.getOffset() + startSpace + secondStartSpace + (consumedGap) + lastUnconsumedSpace;\n                bottom = top + maxSize;\n            }\n        } else {\n            if (layoutStart) {\n                right = layoutState.getOffset() - endSpace - (consumedGap) - lastUnconsumedSpace;\n                left = right - maxSize;\n            } else {\n                left = layoutState.getOffset() + startSpace + (consumedGap) + lastUnconsumedSpace;\n                right = left + maxSize;\n            }\n        }\n\n        for (int i = 0; i < count; i++) {\n            View view = rangeStyle.mSet[i];\n            final int index = rangeStyle.mSpanIndices[i];\n\n            LayoutParams params = (LayoutParams) view.getLayoutParams();\n            if (layoutInVertical) {\n                if (weighted) {\n                    left = helper.getPaddingLeft() + rangeStyle.getFamilyMarginLeft() + rangeStyle.getFamilyPaddingLeft();\n                    for (int j = 0; j < index; j++) {\n                        left += rangeStyle.mSpanCols[j] + rangeStyle.mHGap;\n                    }\n                } else {\n                    left = helper.getPaddingLeft() + rangeStyle.getFamilyMarginLeft() + rangeStyle\n                        .getFamilyPaddingLeft() + rangeStyle.mSizePerSpan * index + index * rangeStyle.mHGap;\n                }\n\n                right = left + orientationHelper.getDecoratedMeasurementInOther(view);\n            } else {\n\n                if (weighted) {\n                    top = helper.getPaddingTop() + rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop();\n                    for (int j = 0; j < index; j++) {\n                        top += rangeStyle.mSpanCols[j] + rangeStyle.mVGap;\n                    }\n                } else {\n                    top = helper.getPaddingTop() + rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop()\n                        + rangeStyle.mSizePerSpan * index + index * rangeStyle.mVGap;\n                }\n\n                bottom = top + orientationHelper.getDecoratedMeasurementInOther(view);\n            }\n\n            if (DEBUG) {\n                Log.d(TAG, \"layout item in position: \" + params.getViewPosition() + \" with text with SpanIndex: \" + index + \" into (\" +\n                        left + \", \" + top + \", \" + right + \", \" + bottom + \"), topInfo=[layoutState.getOffset()=\" + layoutState.getOffset() + \" startSpace=\" + startSpace + \" secondStartSpace=\" + secondStartSpace + \" consumedGap=\" + consumedGap + \" lastUnconsumedSpace=\" + lastUnconsumedSpace + \"]\");\n            }\n\n            // We calculate everything with View's bounding box (which includes decor and margins)\n            // To calculate correct layout position, we subtract margins.\n            rangeStyle.layoutChild(view, left, top, right, bottom, helper, false);\n\n            // Consume the available space if the view is not removed OR changed\n            if (params.isItemRemoved() || params.isItemChanged()) {\n                result.mIgnoreConsumed = true;\n            }\n\n            result.mFocusable |= view.isFocusable();\n        }\n\n\n        mLayoutWithAnchor = false;\n        Arrays.fill(rangeStyle.mSet, null);\n        Arrays.fill(rangeStyle.mSpanIndices, 0);\n        Arrays.fill(rangeStyle.mSpanCols, 0);\n    }\n\n    @Override\n    public void afterLayout(Recycler recycler, State state, int startPosition, int endPosition, int scrolled,\n        LayoutManagerHelper helper) {\n        mRangeStyle.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n    }\n\n    @Override\n    public void adjustLayout(int startPosition, int endPosition, LayoutManagerHelper helper) {\n        mRangeStyle.adjustLayout(startPosition, endPosition, helper);\n    }\n    \n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd, boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        if (isLayoutEnd) {\n            if (offset == getItemCount() - 1) {\n                return GridRangeStyle.computeEndAlignOffset(mRangeStyle, layoutInVertical);\n            }\n        } else {\n            if (offset == 0) {\n                return GridRangeStyle.computeStartAlignOffset(mRangeStyle, layoutInVertical);\n            }\n        }\n\n        return super.computeAlignOffset(offset, isLayoutEnd, useAnchor, helper);\n    }\n\n    @Override\n    public void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n        mRangeStyle.onClear(helper);\n        mRangeStyle.onInvalidateSpanIndexCache();\n    }\n\n    @Override\n    public void onItemsChanged(LayoutManagerHelper helper) {\n        super.onItemsChanged(helper);\n        mRangeStyle.onInvalidateSpanIndexCache();\n    }\n\n    private static final int MAIN_DIR_SPEC =\n        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);\n\n    private int getMainDirSpec(GridRangeStyle rangeStyle, int dim, int otherSize, int viewSize, float viewAspectRatio) {\n        if (!Float.isNaN(viewAspectRatio) && viewAspectRatio > 0 && viewSize > 0) {\n            return View.MeasureSpec.makeMeasureSpec((int) (viewSize / viewAspectRatio + 0.5f), View.MeasureSpec.EXACTLY);\n        } else if (!Float.isNaN(rangeStyle.mAspectRatio) && rangeStyle.mAspectRatio > 0) {\n            return View.MeasureSpec.makeMeasureSpec((int) (otherSize / rangeStyle.mAspectRatio + 0.5f), View.MeasureSpec.EXACTLY);\n        } else if (dim < 0) {\n            return MAIN_DIR_SPEC;\n        } else {\n            return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);\n        }\n    }\n\n    private boolean mLayoutWithAnchor = false;\n\n    @Override\n    public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        if (state.getItemCount() > 0 ) {\n            GridRangeStyle rangeStyle = mRangeStyle.findRangeStyleByPosition(anchorInfo.position);\n            int span = rangeStyle.mSpanSizeLookup.getCachedSpanIndex(anchorInfo.position, rangeStyle.mSpanCount);\n            if (anchorInfo.layoutFromEnd) {\n                while (span < rangeStyle.mSpanCount - 1 && anchorInfo.position < getRange().getUpper()) {\n                    anchorInfo.position++;\n                    span = rangeStyle.mSpanSizeLookup.getCachedSpanIndex(anchorInfo.position, rangeStyle.mSpanCount);\n                }\n            } else {\n                while (span > 0 && anchorInfo.position > 0) {\n                    anchorInfo.position--;\n                    span = rangeStyle.mSpanSizeLookup.getCachedSpanIndex(anchorInfo.position, rangeStyle.mSpanCount);\n                }\n            }\n\n            mLayoutWithAnchor = true;\n\n/*\n            if (anchorInfo.position == getRange().getLower() || anchorInfo.position == getRange().getUpper()) {\n                return;\n            }\n\n            boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n            if (anchorInfo.layoutFromEnd) {\n                anchorInfo.coordinate += layoutInVertical ? mVGap : mHGap;\n            } else {\n                anchorInfo.coordinate -= layoutInVertical ? mVGap : mHGap;\n            }\n */\n\n        }\n    }\n\n\n    private int getSpanIndex(SpanSizeLookup spanSizeLookup, int spanCount, RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {\n        if (!state.isPreLayout()) {\n            return spanSizeLookup.getCachedSpanIndex(pos, spanCount);\n        }\n\n        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);\n        if (adapterPosition == -1) {\n            return 0;\n        }\n        return spanSizeLookup.getCachedSpanIndex(adapterPosition, spanCount);\n    }\n\n\n    private int getSpanSize(SpanSizeLookup spanSizeLookup, RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {\n        if (!state.isPreLayout()) {\n            return spanSizeLookup.getSpanSize(pos);\n        }\n\n        final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);\n        if (adapterPosition == -1) {\n            return 0;\n        }\n\n        return spanSizeLookup.getSpanSize(adapterPosition);\n    }\n\n    private void assignSpans(GridRangeStyle rangeStyle, RecyclerView.Recycler recycler, RecyclerView.State state, int count,\n        int consumedSpanCount, boolean layingOutInPrimaryDirection, LayoutManagerHelper helper) {\n        int span, spanDiff, start, end, diff;\n        // make sure we traverse from min position to max position\n        if (layingOutInPrimaryDirection) {\n            start = 0;\n            end = count;\n            diff = 1;\n        } else {\n            start = count - 1;\n            end = -1;\n            diff = -1;\n        }\n\n        if (helper.getOrientation() == VERTICAL && helper.isDoLayoutRTL()) { // start from last span\n            span = consumedSpanCount - 1;\n            spanDiff = -1;\n        } else {\n            span = 0;\n            spanDiff = 1;\n        }\n\n        for (int i = start; i != end; i += diff) {\n            View view = rangeStyle.mSet[i];\n            int spanSize = getSpanSize(rangeStyle.mSpanSizeLookup, recycler, state, helper.getPosition(view));\n            if (spanDiff == -1 && spanSize > 1) {\n                rangeStyle.mSpanIndices[i] = span - (spanSize - 1);\n            } else {\n                rangeStyle.mSpanIndices[i] = span;\n            }\n            span += spanDiff * spanSize;\n        }\n    }\n\n    public int getBorderStartSpace(LayoutManagerHelper helper) {\n        int start = getRange().getLower().intValue();\n        RangeStyle rangeStyle = mRangeStyle.findRangeStyleByPosition(start);\n        if (helper.getOrientation() == VERTICAL) {\n            return rangeStyle.getFamilyMarginTop() + rangeStyle.getFamilyPaddingTop();\n        } else {\n            return rangeStyle.getFamilyMarginLeft() + rangeStyle.getFamilyPaddingLeft();\n        }\n    }\n\n    public int getBorderEndSpace(LayoutManagerHelper helper) {\n        int end = getRange().getUpper().intValue();\n        RangeStyle rangeStyle = mRangeStyle.findRangeStyleByPosition(end);\n        if (helper.getOrientation() == VERTICAL) {\n            return rangeStyle.getFamilyMarginBottom() + rangeStyle.getFamilyPaddingBottom();\n        } else {\n            return rangeStyle.getFamilyMarginRight() + rangeStyle.getFamilyPaddingRight();\n        }\n    }\n\n    public static class GridRangeStyle extends RangeStyle<GridRangeStyle> {\n\n        private float mAspectRatio = Float.NaN;\n\n        private int mSpanCount = 4;\n\n        @SuppressWarnings(\"FieldCanBeLocal\")\n        private int mSizePerSpan = 0;\n\n        private boolean mIsAutoExpand = true;\n\n        private boolean mIgnoreExtra = false;\n\n        @NonNull\n        private SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();\n\n        private int mVGap = 0;\n\n        private int mHGap = 0;\n\n        private float[] mWeights = new float[0];\n\n        private View[] mSet;\n\n        /**\n         * store index of each span\n         */\n        private int[] mSpanIndices;\n\n        /**\n         * store size of each span when {@link #mWeights} is not empty\n         */\n        private int[] mSpanCols;\n\n\n\n        public GridRangeStyle(RangeGridLayoutHelper layoutHelper) {\n            super(layoutHelper);\n            mSpanSizeLookup.setSpanIndexCacheEnabled(true);\n        }\n\n        public GridRangeStyle() {\n            mSpanSizeLookup.setSpanIndexCacheEnabled(true);\n        }\n\n\n        //TODO find style itr\n        public GridRangeStyle findRangeStyleByPosition(int position) {\n            return findRangeStyle(this, position);\n        }\n\n        private GridRangeStyle findRangeStyle(GridRangeStyle rangeStyle, int position){\n            for (Map.Entry<Range<Integer>, GridRangeStyle> entry : rangeStyle.mChildren.entrySet()) {\n                GridRangeStyle childRangeStyle = entry.getValue();\n                Range range = entry.getKey();\n                if (!childRangeStyle.isChildrenEmpty()){\n                    return findRangeStyle(childRangeStyle, position);\n                } else if (range.contains(position)) {\n                    return childRangeStyle;\n                }\n            }\n            return rangeStyle;\n        }\n\n        public GridRangeStyle findSiblingStyleByPosition(int position) {\n            GridRangeStyle rangeStyle = null;\n            if (mParent != null) {\n                HashMap<Range<Integer>, GridRangeStyle> siblings = mParent.mChildren;\n                for (Map.Entry<Range<Integer>, GridRangeStyle> entry : siblings.entrySet()) {\n                    Range range = entry.getKey();\n                    if (range.contains(position)) {\n                        GridRangeStyle childRangeStyle = entry.getValue();\n                        if (!childRangeStyle.equals(this)) {\n                            rangeStyle = childRangeStyle;\n                        }\n                        break;\n                    }\n                }\n            }\n            return rangeStyle;\n        }\n\n        public void onInvalidateSpanIndexCache() {\n            mSpanSizeLookup.invalidateSpanIndexCache();\n            for (Map.Entry<Range<Integer>, GridRangeStyle> entry : mChildren.entrySet()) {\n                GridRangeStyle rangeStyle = entry.getValue();\n                rangeStyle.onInvalidateSpanIndexCache();\n            }\n        }\n\n        public static int computeEndAlignOffset(GridRangeStyle rangeStyle, boolean layoutInVertical) {\n            int offset = layoutInVertical ? rangeStyle.mMarginBottom + rangeStyle.mPaddingBottom : rangeStyle.mMarginRight + rangeStyle.mPaddingRight;\n            int endPosition = rangeStyle.getRange().getUpper().intValue();\n            for (Map.Entry<Range<Integer>, GridRangeStyle> entry : rangeStyle.mChildren.entrySet()) {\n                GridRangeStyle childRangeStyle = entry.getValue();\n                if (!childRangeStyle.isChildrenEmpty()){\n                    offset += computeEndAlignOffset(childRangeStyle, layoutInVertical);\n                }else if (childRangeStyle.mRange.getUpper().intValue() == endPosition) {\n                    offset += (layoutInVertical ? childRangeStyle.mMarginBottom + childRangeStyle.mPaddingBottom\n                        : childRangeStyle.mMarginRight + childRangeStyle.mPaddingRight);\n                    break;\n                }\n            }\n            return offset;\n        }\n\n        public static int computeStartAlignOffset(GridRangeStyle rangeStyle, boolean layoutInVertical) {\n            int offset = layoutInVertical ? -rangeStyle.mMarginTop - rangeStyle.mPaddingTop : -rangeStyle.mMarginLeft - rangeStyle.mPaddingLeft;\n            int startPosition = rangeStyle.getRange().getLower().intValue();\n            for (Map.Entry<Range<Integer>, GridRangeStyle> entry : rangeStyle.mChildren.entrySet()) {\n                GridRangeStyle childRangeStyle = entry.getValue();\n                if (!childRangeStyle.isChildrenEmpty()){\n                    //FIXME may compute the wrong start space here\n                    offset += computeStartAlignOffset(childRangeStyle, layoutInVertical);\n                }else if (childRangeStyle.mRange.getLower().intValue() == startPosition) {\n                    offset += (layoutInVertical ? -childRangeStyle.mMarginTop - childRangeStyle.mPaddingTop\n                        : -childRangeStyle.mMarginLeft - childRangeStyle.mPaddingLeft);\n                    break;\n                }\n            }\n            return offset;\n        }\n\n        public void setAspectRatio(float aspectRatio) {\n            this.mAspectRatio = aspectRatio;\n        }\n\n        public float getAspectRatio() {\n            return mAspectRatio;\n        }\n\n\n        @Override\n        public void setRange(int start, int end) {\n            super.setRange(start, end);\n            mSpanSizeLookup.setStartPosition(start);\n            mSpanSizeLookup.invalidateSpanIndexCache();\n        }\n\n        public void setGap(int gap) {\n            setVGap(gap);\n            setHGap(gap);\n        }\n\n        public void setVGap(int vGap) {\n            if (vGap < 0) {\n                vGap = 0;\n            }\n            this.mVGap = vGap;\n        }\n\n        public void setHGap(int hGap) {\n            if (hGap < 0) {\n                hGap = 0;\n            }\n            this.mHGap = hGap;\n        }\n\n        public void setWeights(float[] weights) {\n            if (weights != null) {\n                this.mWeights = Arrays.copyOf(weights, weights.length);\n            } else {\n                this.mWeights = new float[0];\n            }\n        }\n\n        public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {\n            if (spanSizeLookup != null) {\n                // TODO: handle reverse layout?\n                spanSizeLookup.setStartPosition(mSpanSizeLookup.getStartPosition());\n\n                this.mSpanSizeLookup = spanSizeLookup;\n            }\n        }\n\n        public void setAutoExpand(boolean isAutoExpand) {\n            this.mIsAutoExpand = isAutoExpand;\n        }\n\n        public void setIgnoreExtra(boolean ignoreExtra) {\n            this.mIgnoreExtra = ignoreExtra;\n        }\n\n\n        /**\n         * {@inheritDoc}\n         * Set SpanCount for grid\n         *\n         * @param spanCount grid column number, must be greater than 0. {@link IllegalArgumentException}\n         *                  will be thrown otherwise\n         */\n        public void setSpanCount(int spanCount) {\n            if (spanCount == mSpanCount) {\n                return;\n            }\n            if (spanCount < 1) {\n                throw new IllegalArgumentException(\"Span count should be at least 1. Provided \"\n                    + spanCount);\n            }\n            mSpanCount = spanCount;\n            mSpanSizeLookup.invalidateSpanIndexCache();\n\n            ensureSpanCount();\n        }\n\n        public int getSpanCount() {\n            return mSpanCount;\n        }\n\n        private void ensureSpanCount() {\n\n            if (mSet == null || mSet.length != mSpanCount) {\n                mSet = new View[mSpanCount];\n            }\n\n            if (mSpanIndices == null || mSpanIndices.length != mSpanCount) {\n                mSpanIndices = new int[mSpanCount];\n            }\n\n            if (mSpanCols == null || mSpanCols.length != mSpanCount) {\n                mSpanCols = new int[mSpanCount];\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/RangeStyle.java",
    "content": "package com.alibaba.android.vlayout.layout;\n\nimport java.lang.reflect.Array;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.Range;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.layout.BaseLayoutHelper.DefaultLayoutViewHelper;\nimport com.alibaba.android.vlayout.layout.BaseLayoutHelper.LayoutViewBindListener;\nimport com.alibaba.android.vlayout.layout.BaseLayoutHelper.LayoutViewUnBindListener;\n\nimport android.graphics.Rect;\nimport android.support.annotation.NonNull;\nimport android.support.v4.util.ArrayMap;\nimport android.support.v4.util.SimpleArrayMap;\nimport android.support.v7.widget.OrientationHelper;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\n\n/**\n * Created by longerian on 2017/5/10.\n *\n * @author longerian\n * @date 2017/05/10\n */\n\npublic class RangeStyle<T extends RangeStyle> {\n\n    private static final boolean DEBUG = false;\n\n    private static final String TAG = \"RangeStyle\";\n\n    protected BaseLayoutHelper mLayoutHelper;\n\n    protected T mParent;\n\n    private int mOriginStartOffset = 0;\n\n    private int mOriginEndOffset = 0;\n\n    protected Range<Integer> mRange;\n\n    //TODO update data structure\n    protected HashMap<Range<Integer>, T> mChildren = new HashMap<>();\n\n    protected int mPaddingLeft;\n\n    protected int mPaddingRight;\n\n    protected int mPaddingTop;\n\n    protected int mPaddingBottom;\n\n    protected int mMarginLeft;\n\n    protected int mMarginRight;\n\n    protected int mMarginTop;\n\n    protected int mMarginBottom;\n\n    protected Rect mLayoutRegion = new Rect();\n\n    private View mLayoutView;\n\n    private int mBgColor;\n\n    private LayoutViewUnBindListener mLayoutViewUnBindListener;\n\n    private LayoutViewBindListener mLayoutViewBindListener;\n\n    public RangeStyle(BaseLayoutHelper layoutHelper) {\n        mLayoutHelper = layoutHelper;\n    }\n\n    public RangeStyle() {\n    }\n\n    public void addChildRangeStyle(int start, int end, T rangeStyle) {\n        if (start <= end && rangeStyle != null) {\n            rangeStyle.setParent(this);\n            rangeStyle.setOriginStartOffset(start);\n            rangeStyle.setOriginEndOffset(end);\n            rangeStyle.setRange(start, end);\n            mChildren.put(rangeStyle.getRange(), rangeStyle);\n        }\n    }\n\n    public void setParent(T rangeStyle) {\n        this.mParent = rangeStyle;\n    }\n\n    /**\n     * set paddings for this style\n     * @param leftPadding left padding\n     * @param topPadding top padding\n     * @param rightPadding right padding\n     * @param bottomPadding bottom padding\n     */\n    public void setPadding(int leftPadding, int topPadding, int rightPadding, int bottomPadding) {\n        mPaddingLeft = leftPadding;\n        mPaddingRight = rightPadding;\n        mPaddingTop = topPadding;\n        mPaddingBottom = bottomPadding;\n    }\n\n    /**\n     * Set margins for this style\n     *\n     * @param leftMargin left margin\n     * @param topMargin top margin\n     * @param rightMargin right margin\n     * @param bottomMargin bottom margin\n     */\n    public void setMargin(int leftMargin, int topMargin, int rightMargin, int bottomMargin) {\n        this.mMarginLeft = leftMargin;\n        this.mMarginTop = topMargin;\n        this.mMarginRight = rightMargin;\n        this.mMarginBottom = bottomMargin;\n    }\n\n    /**\n     * Get total margin in horizontal dimension\n     *\n     * @return\n     */\n    protected int getHorizontalMargin() {\n        return mMarginLeft + mMarginRight;\n    }\n\n    /**\n     * Get total margin in vertical dimension\n     *\n     * @return\n     */\n    protected int getVerticalMargin() {\n        return mMarginTop + mMarginBottom;\n    }\n\n    /**\n     * Get total padding in horizontal dimension\n     * @return\n     */\n    protected int getHorizontalPadding() {\n        return mPaddingLeft + mPaddingRight;\n    }\n\n    /**\n     * Get total padding in vertical dimension\n     * @return\n     */\n    protected int getVerticalPadding() {\n        return mPaddingTop + mPaddingBottom;\n    }\n\n    public int getPaddingLeft() {\n        return mPaddingLeft;\n    }\n\n    public int getPaddingRight() {\n        return mPaddingRight;\n    }\n\n    public int getPaddingTop() {\n        return mPaddingTop;\n    }\n\n    public int getPaddingBottom() {\n        return mPaddingBottom;\n    }\n\n    public int getMarginLeft() {\n        return mMarginLeft;\n    }\n\n    public int getMarginRight() {\n        return mMarginRight;\n    }\n\n    public int getMarginTop() {\n        return mMarginTop;\n    }\n\n    public int getMarginBottom() {\n        return mMarginBottom;\n    }\n\n    public void setPaddingLeft(int paddingLeft) {\n        mPaddingLeft = paddingLeft;\n    }\n\n    public void setPaddingRight(int paddingRight) {\n        mPaddingRight = paddingRight;\n    }\n\n    public void setPaddingTop(int paddingTop) {\n        mPaddingTop = paddingTop;\n    }\n\n    public void setPaddingBottom(int paddingBottom) {\n        mPaddingBottom = paddingBottom;\n    }\n\n    public void setMarginLeft(int marginLeft) {\n        mMarginLeft = marginLeft;\n    }\n\n    public void setMarginRight(int marginRight) {\n        mMarginRight = marginRight;\n    }\n\n    public void setMarginTop(int marginTop) {\n        mMarginTop = marginTop;\n    }\n\n    public void setMarginBottom(int marginBottom) {\n        mMarginBottom = marginBottom;\n    }\n\n    /**\n     * Get total horizontal margin include its ancestor's and itself's.\n     * @return\n     */\n    public int getFamilyHorizontalMargin() {\n        return (mParent != null ? mParent.getFamilyHorizontalMargin() : 0) + getHorizontalMargin();\n    }\n\n    /**\n     * Get total vertical margin include its ancestor's and itself's.\n     * @return\n     */\n    public int getFamilyVerticalMargin() {\n        return (mParent != null ? mParent.getFamilyVerticalMargin() : 0) + getVerticalMargin();\n    }\n\n    /**\n     * Get total horizontal padding include its ancestor's and itself's.\n     * @return\n     */\n    public int getFamilyHorizontalPadding() {\n        return (mParent != null ? mParent.getFamilyHorizontalPadding() : 0) + getHorizontalPadding();\n    }\n\n    /**\n     * Get total vertical padding include its ancestor's and itself's.\n     * @return\n     */\n    public int getFamilyVerticalPadding() {\n        return (mParent != null ? mParent.getFamilyVerticalPadding() : 0) + getVerticalPadding();\n    }\n\n    public int getFamilyPaddingLeft() {\n        return (mParent != null ? mParent.getFamilyPaddingLeft() : 0) + mPaddingLeft;\n    }\n\n    public int getFamilyPaddingRight() {\n        return (mParent != null ? mParent.getFamilyPaddingRight() : 0) + mPaddingRight;\n    }\n\n    public int getFamilyPaddingTop() {\n        return (mParent != null ? mParent.getFamilyPaddingTop() : 0) + mPaddingTop;\n    }\n\n    public int getFamilyPaddingBottom() {\n        return (mParent != null ? mParent.getFamilyPaddingBottom() : 0) + mPaddingBottom;\n    }\n\n    public int getFamilyMarginLeft() {\n        return (mParent != null ? mParent.getFamilyMarginLeft() : 0) + mMarginLeft;\n    }\n\n    public int getFamilyMarginRight() {\n        return (mParent != null ? mParent.getFamilyMarginRight() : 0) + mMarginRight;\n    }\n\n    public int getFamilyMarginTop() {\n        return (mParent != null ? mParent.getFamilyMarginTop() : 0) + mMarginTop;\n    }\n\n    public int getFamilyMarginBottom() {\n        return (mParent != null ? mParent.getFamilyMarginBottom() : 0) + mMarginBottom;\n    }\n\n    /**\n     * Get total horizontal margin of its ancestor's.\n     * @return\n     */\n    public int getAncestorHorizontalMargin() {\n        return (mParent != null ? mParent.getAncestorHorizontalMargin() + mParent.getHorizontalMargin() : 0);\n    }\n\n    /**\n     * Get total vertical margin of its ancestor's.\n     * @return\n     */\n    public int getAncestorVerticalMargin() {\n        return (mParent != null ? mParent.getAncestorVerticalMargin() + mParent.getVerticalMargin(): 0);\n    }\n\n    /**\n     * Get total horizontal padding of its ancestor's.\n     * @return\n     */\n    public int getAncestorHorizontalPadding() {\n        return (mParent != null ? mParent.getAncestorHorizontalPadding() + mParent.getHorizontalPadding() : 0);\n    }\n\n    /**\n     * Get total vertical padding of its ancestor's.\n     * @return\n     */\n    public int getAncestorVerticalPadding() {\n        return (mParent != null ? mParent.getAncestorVerticalPadding() + mParent.getVerticalPadding() : 0);\n    }\n\n    public int getAncestorPaddingLeft() {\n        return (mParent != null ? mParent.getAncestorPaddingLeft() + mParent.getPaddingLeft() : 0);\n    }\n\n    public int getAncestorPaddingRight() {\n        return (mParent != null ? mParent.getAncestorPaddingRight() + mParent.getPaddingRight() : 0);\n    }\n\n    public int getAncestorPaddingTop() {\n        return (mParent != null ? mParent.getAncestorPaddingTop() + mParent.getPaddingTop() : 0);\n    }\n\n    public int getAncestorPaddingBottom() {\n        return (mParent != null ? mParent.getAncestorPaddingBottom() + mParent.getPaddingBottom() : 0);\n    }\n\n    public int getAncestorMarginLeft() {\n        return (mParent != null ? mParent.getAncestorMarginLeft() + mParent.getMarginLeft() : 0);\n    }\n\n    public int getAncestorMarginRight() {\n        return (mParent != null ? mParent.getAncestorMarginRight() + mParent.getMarginRight() : 0);\n    }\n\n    public int getAncestorMarginTop() {\n        return (mParent != null ? mParent.getAncestorMarginTop() + mParent.getMarginTop() : 0);\n    }\n\n    public int getAncestorMarginBottom() {\n        return (mParent != null ? mParent.getAncestorMarginBottom() + mParent.getMarginBottom() : 0);\n    }\n\n    public int getOriginStartOffset() {\n        return mOriginStartOffset;\n    }\n\n    public int getOriginEndOffset() {\n        return mOriginEndOffset;\n    }\n\n    public void setOriginStartOffset(int originStartOffset) {\n        mOriginStartOffset = originStartOffset;\n    }\n\n    public void setOriginEndOffset(int originEndOffset) {\n        mOriginEndOffset = originEndOffset;\n    }\n\n    public Range<Integer> getRange() {\n        return mRange;\n    }\n\n    public BaseLayoutHelper getLayoutHelper() {\n        if (mLayoutHelper != null) {\n            return mLayoutHelper;\n        }\n        if (mParent != null) {\n            return mParent.getLayoutHelper();\n        }\n        return null;\n    }\n\n    public boolean isChildrenEmpty() {\n        return mChildren.isEmpty();\n    }\n\n    public boolean isRoot() {\n        return mParent == null;\n    }\n\n    public boolean isOutOfRange(int position) {\n        return mRange != null ? !mRange.contains(position) : true;\n    }\n\n    public boolean isFirstPosition(int position) {\n        return mRange != null ? mRange.getLower().intValue() == position : false;\n    }\n\n    public boolean isLastPosition(int position) {\n        return mRange != null ? mRange.getUpper().intValue() == position : false;\n    }\n\n    /**\n     * @param start offset relative to its parent\n     * @param end offset relative to its parent\n     */\n    public void setRange(int start, int end) {\n        mRange = Range.create(start, end);\n        if (!mChildren.isEmpty()) {\n            HashMap<Range<Integer>, T> newMap = new HashMap<>();\n            for (Map.Entry<Range<Integer>, T> entry : mChildren.entrySet()) {\n                T rangeStyle = entry.getValue();\n                int newStart = rangeStyle.getOriginStartOffset() + start;\n                int newEnd = rangeStyle.getOriginEndOffset() + start;\n                Range<Integer> newRange = Range.create(newStart, newEnd);\n                newMap.put(newRange, rangeStyle);\n                rangeStyle.setRange(newStart, newEnd);\n            }\n            mChildren.clear();\n            mChildren.putAll(newMap);\n        }\n    }\n\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n        LayoutManagerHelper helper) {\n        if (!isChildrenEmpty()) {\n            for (Map.Entry<Range<Integer>, T> entry : mChildren.entrySet()) {\n                RangeStyle childRangeStyle = entry.getValue();\n                childRangeStyle.beforeLayout(recycler, state, helper);\n            }\n        }\n        if (requireLayoutView()) {\n            if (mLayoutView != null) {\n                // helper.detachChildView(mLayoutView);\n            }\n        } else {\n            // if no layoutView is required, remove it\n            if (mLayoutView != null) {\n                if (mLayoutViewUnBindListener != null) {\n                    mLayoutViewUnBindListener.onUnbind(mLayoutView, getLayoutHelper());\n                }\n                helper.removeChildView(mLayoutView);\n                mLayoutView = null;\n            }\n        }\n\n    }\n\n    private boolean isValidScrolled(int scrolled) {\n        return scrolled != Integer.MAX_VALUE && scrolled != Integer.MIN_VALUE;\n    }\n\n    public void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state,\n        int startPosition, int endPosition, int scrolled,\n        LayoutManagerHelper helper) {\n\n        if (!isChildrenEmpty()) {\n            for (Map.Entry<Range<Integer>, T> entry : mChildren.entrySet()) {\n                RangeStyle childRangeStyle = entry.getValue();\n                childRangeStyle.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n            }\n        }\n        if (DEBUG) {\n            Log.d(TAG, \"call afterLayout() on \" + this.getClass().getSimpleName());\n        }\n\n\n        if (requireLayoutView()) {\n            if (isValidScrolled(scrolled) && mLayoutView != null) {\n                // initial layout do reset\n                mLayoutRegion.union(mLayoutView.getLeft(), mLayoutView.getTop(), mLayoutView.getRight(), mLayoutView.getBottom());\n            }\n\n\n            if (!mLayoutRegion.isEmpty()) {\n                if (isValidScrolled(scrolled)) {\n                    if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n                        mLayoutRegion.offset(0, -scrolled);\n                    } else {\n                        mLayoutRegion.offset(-scrolled, 0);\n                    }\n                }\n\n                unionChildRegion(this);\n\n                int contentWidth = helper.getContentWidth();\n                int contentHeight = helper.getContentHeight();\n                if (helper.getOrientation() == VirtualLayoutManager.VERTICAL ?\n                    mLayoutRegion.intersects(0, -contentHeight / 4, contentWidth, contentHeight + contentHeight / 4) :\n                    mLayoutRegion.intersects(-contentWidth / 4, 0, contentWidth + contentWidth / 4, contentHeight)) {\n\n                    if (mLayoutView == null) {\n                        mLayoutView = helper.generateLayoutView();\n                        helper.addBackgroundView(mLayoutView, true);\n                    }\n                    //finally fix layoutRegion's height and with here to avoid visual blank\n                    if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n                        mLayoutRegion.left = helper.getPaddingLeft() + getFamilyMarginLeft()\n                            + getAncestorPaddingLeft();\n                        mLayoutRegion.right = helper.getContentWidth() - helper.getPaddingRight()\n                            - getFamilyMarginRight() - getAncestorPaddingRight();\n                    } else {\n                        mLayoutRegion.top = helper.getPaddingTop() + getFamilyMarginTop() + getAncestorPaddingTop();\n                        mLayoutRegion.bottom = helper.getContentWidth() - helper.getPaddingBottom()\n                            - getFamilyMarginBottom() - getAncestorPaddingBottom();\n                    }\n                    bindLayoutView(mLayoutView);\n                    hideLayoutViews(helper);\n                    return;\n                } else {\n                    mLayoutRegion.set(0, 0, 0, 0);\n                    if (mLayoutView != null) {\n                        mLayoutView.layout(0, 0, 0, 0);\n                    }\n                    hideLayoutViews(helper);\n                }\n            }\n        }\n        hideLayoutViews(helper);\n        if (isRoot()) {\n            removeChildViews(helper, this);\n        }\n    }\n\n    private void unionChildRegion(RangeStyle<T> rangeStyle) {\n        if (!rangeStyle.isChildrenEmpty()) {\n            for (Map.Entry<Range<Integer>, T> entry : rangeStyle.mChildren.entrySet()) {\n                RangeStyle childRangeStyle = entry.getValue();\n                unionChildRegion(childRangeStyle);\n                if (childRangeStyle.mLayoutView != null) {\n                    rangeStyle.mLayoutRegion.union(childRangeStyle.mLayoutView.getLeft(), childRangeStyle.mLayoutView.getTop(),\n                            childRangeStyle.mLayoutView.getRight(), childRangeStyle.mLayoutView.getBottom());\n                }\n            }\n        }\n    }\n\n    private void removeChildViews(LayoutManagerHelper helper, RangeStyle<T> rangeStyle) {\n        if (!rangeStyle.isChildrenEmpty()) {\n            for (Map.Entry<Range<Integer>, T> entry : rangeStyle.mChildren.entrySet()) {\n                RangeStyle childRangeStyle = entry.getValue();\n                removeChildViews(helper, childRangeStyle);\n            }\n        }\n\n        if (rangeStyle.mLayoutView != null) {\n            if (rangeStyle.mLayoutViewUnBindListener != null) {\n                rangeStyle.mLayoutViewUnBindListener.onUnbind(rangeStyle.mLayoutView, getLayoutHelper());\n            }\n            helper.removeChildView(rangeStyle.mLayoutView);\n            rangeStyle.mLayoutView = null;\n        }\n    }\n\n    public void adjustLayout(int startPosition, int endPosition, LayoutManagerHelper helper) {\n        if (!isChildrenEmpty()) {\n            for (Map.Entry<Range<Integer>, T> entry : mChildren.entrySet()) {\n                RangeStyle rangeStyle = entry.getValue();\n                rangeStyle.adjustLayout(startPosition, endPosition, helper);\n            }\n        }\n        if (requireLayoutView()) {\n            View refer = null;\n            Rect tempRect = new Rect();\n            final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n            for (int i = 0; i < helper.getChildCount(); i++) {\n                refer = helper.getChildAt(i);\n                int anchorPos = helper.getPosition(refer);\n                if (getRange().contains(anchorPos)) {\n                    if (refer.getVisibility() == View.GONE) {\n                        tempRect.setEmpty();\n                    } else {\n                        final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)\n                            refer.getLayoutParams();\n                        if (helper.getOrientation() == VirtualLayoutManager.VERTICAL) {\n                            tempRect.union(helper.getDecoratedLeft(refer) - params.leftMargin,\n                                orientationHelper.getDecoratedStart(refer),\n                                helper.getDecoratedRight(refer) + params.rightMargin,\n                                orientationHelper.getDecoratedEnd(refer));\n                        } else {\n                            tempRect.union(orientationHelper.getDecoratedStart(refer),\n                                helper.getDecoratedTop(refer) - params.topMargin, orientationHelper.getDecoratedEnd(refer),\n                                helper.getDecoratedBottom(refer) + params.bottomMargin);\n                        }\n                    }\n                }\n            }\n            if (!tempRect.isEmpty()) {\n                mLayoutRegion.set(tempRect.left - mPaddingLeft, tempRect.top - mPaddingTop,\n                    tempRect.right + mPaddingRight, tempRect.bottom + mPaddingBottom);\n            } else {\n                mLayoutRegion.setEmpty();\n            }\n            if (mLayoutView != null) {\n                mLayoutView.layout(mLayoutRegion.left, mLayoutRegion.top, mLayoutRegion.right, mLayoutRegion.bottom);\n            }\n        }\n    }\n\n    private void hideLayoutViews(LayoutManagerHelper helper) {\n        if (isRoot()) {\n            hideChildLayoutViews(helper, this);\n\n            if (mLayoutView != null) {\n                helper.hideView(mLayoutView);\n            }\n        }\n    }\n\n    private void hideChildLayoutViews(LayoutManagerHelper helper, RangeStyle<T> rangeStyle) {\n        for (Map.Entry<Range<Integer>, T> entry : rangeStyle.mChildren.entrySet()) {\n            RangeStyle childRangeStyle = entry.getValue();\n            if (!childRangeStyle.isChildrenEmpty()) {\n                hideChildLayoutViews(helper, childRangeStyle);\n            }\n\n            if (childRangeStyle.mLayoutView != null) {\n                helper.hideView(childRangeStyle.mLayoutView);\n            }\n        }\n    }\n\n    public boolean requireLayoutView() {\n        boolean self = mBgColor != 0 || mLayoutViewBindListener != null;\n        if (!isChildrenEmpty()) {\n            self |= requireChildLayoutView(this);\n        }\n        return self;\n    }\n\n    private boolean requireChildLayoutView(RangeStyle<T> rangeStyle) {\n        boolean self = rangeStyle.mBgColor != 0 || rangeStyle.mLayoutViewBindListener != null;\n\n        for (Map.Entry<Range<Integer>, T> entry : rangeStyle.mChildren.entrySet()) {\n            RangeStyle childRangeStyle = entry.getValue();\n            if (!childRangeStyle.isChildrenEmpty()) {\n                self |= requireChildLayoutView(childRangeStyle);\n            } else {\n                return childRangeStyle.requireLayoutView();\n            }\n        }\n        return self;\n    }\n\n    public void bindLayoutView(@NonNull final View layoutView) {\n        layoutView.measure(View.MeasureSpec.makeMeasureSpec(mLayoutRegion.width(), View.MeasureSpec.EXACTLY),\n            View.MeasureSpec.makeMeasureSpec(mLayoutRegion.height(), View.MeasureSpec.EXACTLY));\n        layoutView.layout(mLayoutRegion.left, mLayoutRegion.top, mLayoutRegion.right, mLayoutRegion.bottom);\n        layoutView.setBackgroundColor(mBgColor);\n\n        if (mLayoutViewBindListener != null) {\n            mLayoutViewBindListener.onBind(layoutView, getLayoutHelper());\n        }\n\n        // reset region rectangle\n        mLayoutRegion.set(0, 0, 0, 0);\n    }\n\n    public void setLayoutViewHelper(DefaultLayoutViewHelper layoutViewHelper) {\n        mLayoutViewBindListener = layoutViewHelper;\n        mLayoutViewUnBindListener = layoutViewHelper;\n    }\n\n    public void setLayoutViewBindListener(LayoutViewBindListener bindListener) {\n        mLayoutViewBindListener = bindListener;\n    }\n\n    public void setLayoutViewUnBindListener(\n        LayoutViewUnBindListener layoutViewUnBindListener) {\n        mLayoutViewUnBindListener = layoutViewUnBindListener;\n    }\n\n    public void setBgColor(int bgColor) {\n        this.mBgColor = bgColor;\n    }\n\n    public void onClear(LayoutManagerHelper helper) {\n        clearChild(helper, this);\n    }\n\n    private void clearChild(LayoutManagerHelper helper, RangeStyle<T> rangeStyle) {\n        if (rangeStyle.mLayoutView != null) {\n            if (rangeStyle.mLayoutViewUnBindListener != null) {\n                rangeStyle.mLayoutViewUnBindListener.onUnbind(rangeStyle.mLayoutView, getLayoutHelper());\n            }\n            helper.removeChildView(rangeStyle.mLayoutView);\n            rangeStyle.mLayoutView = null;\n        }\n\n        if (rangeStyle.mChildren.isEmpty()) {\n            return;\n        }\n\n        for (Map.Entry<Range<Integer>, T> entry : rangeStyle.mChildren.entrySet()) {\n            RangeStyle childRangeStyle = entry.getValue();\n            clearChild(helper, childRangeStyle);\n        }\n    }\n\n    public void onClearChildMap() {\n        mChildren.clear();\n    }\n\n    public void layoutChild(final View child, int left, int top, int right, int bottom, @NonNull LayoutManagerHelper helper, boolean addLayoutRegionWithMargin) {\n        helper.layoutChildWithMargins(child, left, top, right, bottom);\n        fillLayoutRegion(left, top, right, bottom, addLayoutRegionWithMargin);\n    }\n\n    protected void fillLayoutRegion(int left, int top, int right, int bottom, boolean addLayoutRegionWithMargin) {\n        if (addLayoutRegionWithMargin) {\n            mLayoutRegion\n                .union(left - mPaddingLeft - mMarginLeft, top - mPaddingTop - mMarginTop,\n                    right + mPaddingRight + mMarginRight,\n                    bottom + mPaddingBottom + mMarginBottom);\n        } else {\n            mLayoutRegion.union(left - mPaddingLeft, top - mPaddingTop, right + mPaddingRight,\n                bottom + mPaddingBottom);\n        }\n        if (mParent != null) {\n            mParent.fillLayoutRegion(left - mPaddingLeft - mMarginLeft, top - mPaddingTop - mMarginLeft, right + mPaddingRight + mMarginRight,\n                bottom + mPaddingBottom + mMarginBottom, addLayoutRegionWithMargin);\n        }\n    }\n\n    private static class RangeMap<T> {\n\n        private final static int CAPACITY = 64;\n\n        private Class<T> mClass;\n\n        private int lastIndex = -1;\n\n        private int[] mOffsetMap = new int[CAPACITY];\n\n        private T[] mCardMap = (T[])Array.newInstance(mClass, CAPACITY);\n\n        public RangeMap(Class<T> type) {\n            this.mClass = type;\n        }\n\n        public void addChild(int startOffset, int endOffset, T t) {\n            int index = lastIndex + 1;\n            if (index < mCardMap.length) {\n                mCardMap[index] = t;\n            } else {\n                int oldLength = mCardMap.length;\n                T[] newCardMap = (T[])Array.newInstance(mClass, oldLength * 2);\n                System.arraycopy(mCardMap, 0, newCardMap, 0, oldLength);\n                mCardMap = newCardMap;\n                mCardMap[oldLength] = t;\n                index = oldLength;\n\n                oldLength = mOffsetMap.length;\n                int[] newOffsetMap = new int[oldLength * 2];\n                System.arraycopy(mOffsetMap, 0, newOffsetMap, 0, oldLength);\n                mOffsetMap = newOffsetMap;\n            }\n            lastIndex = index;\n            for (int i = startOffset; i <= endOffset; i++) {\n                mOffsetMap[i] = index;\n            }\n        }\n\n        public T getChild(int offset) {\n            return mCardMap[mOffsetMap[offset]];\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/ScrollFixLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\n\n/**\n * Absolute layout which only shows after scrolling to its' position,\n * it'll layout View based on leftMargin/topMargin/rightMargin/bottomMargin.\n *\n * @author villadora\n * @since 1.0.0\n */\npublic class ScrollFixLayoutHelper extends FixLayoutHelper {\n\n    private static final String TAG = \"ScrollFixLayoutHelper\";\n\n\n    public static final int SHOW_ALWAYS = 0;\n    public static final int SHOW_ON_ENTER = 1;\n    public static final int SHOW_ON_LEAVE = 2;\n\n\n    private int mShowType = SHOW_ALWAYS;\n\n    public ScrollFixLayoutHelper(int x, int y) {\n        this(TOP_LEFT, x, y);\n    }\n\n    public ScrollFixLayoutHelper(int alignType, int x, int y) {\n        super(alignType, x, y);\n    }\n\n    public void setShowType(int showType) {\n        this.mShowType = showType;\n    }\n\n    public int getShowType() {\n        return mShowType;\n    }\n\n    @Override\n    protected boolean shouldBeDraw(LayoutManagerHelper helper, int startPosition, int endPosition, int scrolled) {\n        switch (mShowType) {\n            case SHOW_ON_ENTER:\n                // when previous item is entering\n                return endPosition >= getRange().getLower() - 1;\n            case SHOW_ON_LEAVE:\n                // show on leave from top\n                // when next item is the first one in screen\n                return startPosition >= getRange().getLower() + 1;\n            case SHOW_ALWAYS:\n            default:\n                // default is always\n                return true;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/SingleLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\n\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * LayoutHelper contains only one view\n */\npublic class SingleLayoutHelper extends ColumnLayoutHelper {\n\n    private static final String TAG = \"SingleLayoutHelper\";\n\n    private int mPos = -1;\n\n    public SingleLayoutHelper() {\n        setItemCount(1);\n    }\n\n\n    @Override\n    public void setItemCount(int itemCount) {\n        if (itemCount > 0)\n            super.setItemCount(1);\n        else\n            super.setItemCount(0);\n    }\n\n    /**\n     * {@inheritDoc}\n     * <p/>\n     * Only start is used, use should not use this measured\n     *\n     * @param start position of items handled by this layoutHelper\n     * @param end   will be ignored by {@link SingleLayoutHelper}\n     */\n    @Override\n    public void onRangeChange(int start, int end) {\n        this.mPos = start;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, VirtualLayoutManager.LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        View view = layoutState.next(recycler);\n\n        if (view == null) {\n            result.mFinished = true;\n            return;\n        }\n\n\n        helper.addChildView(layoutState, view);\n        final VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        int parentWidth = helper.getContentWidth() - helper.getPaddingLeft() - helper\n                .getPaddingRight() - getHorizontalMargin() - getHorizontalPadding();\n        int parentHeight = helper.getContentHeight() - helper.getPaddingTop() - helper\n                .getPaddingBottom() - getVerticalMargin() - getVerticalPadding();\n\n        if (!Float.isNaN(mAspectRatio)) {\n            if (layoutInVertical) {\n                parentHeight = (int) (parentWidth / mAspectRatio + 0.5f);\n            } else {\n                parentWidth = (int) (parentHeight * mAspectRatio + 0.5f);\n            }\n        }\n\n        if (layoutInVertical) {\n            final int widthSpec = helper.getChildMeasureSpec(parentWidth,\n                     Float.isNaN(mAspectRatio) ? params.width : parentWidth, !layoutInVertical && Float.isNaN(mAspectRatio));\n            final int heightSpec = helper.getChildMeasureSpec(parentHeight,\n                    Float.isNaN(params.mAspectRatio) ? (Float.isNaN(mAspectRatio) ? params.height : parentHeight) : (int) (\n                            parentWidth / params.mAspectRatio + 0.5f), layoutInVertical && Float.isNaN(mAspectRatio));\n\n            // do measurement\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        } else {\n            final int widthSpec = helper.getChildMeasureSpec(parentWidth,\n                    Float.isNaN(params.mAspectRatio) ? (Float.isNaN(mAspectRatio) ? params.width : parentWidth) : (int) (\n                            parentHeight * params.mAspectRatio + 0.5f), !layoutInVertical && Float.isNaN(mAspectRatio));\n            final int heightSpec = helper.getChildMeasureSpec(parentHeight,\n                     Float.isNaN(mAspectRatio) ? params.height : parentHeight, layoutInVertical && Float.isNaN(mAspectRatio));\n\n            // do measurement\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        }\n\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        result.mConsumed = orientationHelper.getDecoratedMeasurement(view);\n\n        // do layout\n        int left, top, right, bottom;\n        if (layoutInVertical) {\n            int viewWidth = orientationHelper.getDecoratedMeasurementInOther(view);\n            int available = parentWidth - viewWidth;\n            if (available < 0) {\n                available = 0;\n            }\n\n            left = mMarginLeft + mPaddingLeft + helper.getPaddingLeft() + available / 2;\n            right = helper.getContentWidth() - mMarginRight - mPaddingRight - helper.getPaddingRight() - available / 2;\n\n\n            if (layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START) {\n                bottom = layoutState.getOffset() - mMarginBottom - mPaddingBottom;\n                top = bottom - result.mConsumed;\n            } else {\n                top = layoutState.getOffset() + mMarginTop + mPaddingTop;\n                bottom = top + result.mConsumed;\n            }\n        } else {\n            int viewHeight = orientationHelper.getDecoratedMeasurementInOther(view);\n            int available = parentHeight - viewHeight;\n            if (available < 0) {\n                available = 0;\n            }\n\n            top = helper.getPaddingTop() + mMarginTop + mPaddingTop + available / 2;\n            bottom = helper.getContentHeight() - -mMarginBottom - mPaddingBottom - helper.getPaddingBottom() - available / 2;\n\n            if (layoutState.getLayoutDirection() == VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START) {\n                right = layoutState.getOffset() - mMarginRight - mPaddingRight;\n                left = right - result.mConsumed;\n            } else {\n                left = layoutState.getOffset() + mMarginLeft + mPaddingLeft;\n                right = left + result.mConsumed;\n            }\n        }\n\n        if (layoutInVertical) {\n            result.mConsumed += getVerticalMargin() + getVerticalPadding();\n        } else {\n            result.mConsumed += getHorizontalMargin() + getHorizontalPadding();\n        }\n\n        layoutChildWithMargin(view, left, top, right, bottom, helper);\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/StaggeredGridLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.BitSet;\nimport java.util.List;\n\nimport com.alibaba.android.vlayout.BuildConfig;\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.Range;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.RecyclerView.LayoutParams;\nimport android.util.Log;\nimport android.view.View;\n\nimport static android.support.v7.widget.LinearLayoutManager.INVALID_OFFSET;\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.HORIZONTAL;\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper.LAYOUT_END;\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper.LAYOUT_START;\nimport static com.alibaba.android.vlayout.VirtualLayoutManager.VERTICAL;\n\n/**\n * LayoutHelper provides waterfall.\n *\n * @author villadora\n * @since 1.1.0\n */\npublic class StaggeredGridLayoutHelper extends BaseLayoutHelper {\n\n    private static final String TAG = \"Staggered\";\n\n    private static final String LOOKUP_BUNDLE_KEY = \"StaggeredGridLayoutHelper_LazySpanLookup\";\n\n    private static final int INVALID_SPAN_ID = Integer.MIN_VALUE;\n    static final int INVALID_LINE = Integer.MIN_VALUE;\n\n    private int mNumLanes = 0;\n\n    private Span[] mSpans;\n\n    private int mHGap = 0;\n\n    private int mVGap = 0;\n\n\n    // length specs\n    private int mColLength = 0;\n    private int mEachGap = 0;\n    private int mLastGap = 0;\n\n\n    private BitSet mRemainingSpans = null;\n\n    private LazySpanLookup mLazySpanLookup = new LazySpanLookup();\n\n    private List<View> prelayoutViewList = new ArrayList<>();\n\n    private boolean mLayoutWithAnchor;\n\n    private int anchorPosition;\n\n    private WeakReference<VirtualLayoutManager> mLayoutManager = null;\n\n    private final Runnable checkForGapsRunnable = new Runnable() {\n        @Override\n        public void run() {\n            checkForGaps();\n        }\n    };\n\n    public StaggeredGridLayoutHelper() {\n        this(1, 0);\n    }\n\n    public StaggeredGridLayoutHelper(int lanes) {\n        this(lanes, 0);\n    }\n\n    public StaggeredGridLayoutHelper(int lanes, int gap) {\n        setLane(lanes);\n        setGap(gap);\n    }\n\n    public void setGap(int gap) {\n        setHGap(gap);\n        setVGap(gap);\n    }\n\n    public void setHGap(int hGap) {\n        this.mHGap = hGap;\n    }\n\n    public int getHGap() {\n        return this.mHGap;\n    }\n\n    public void setVGap(int vGap) {\n        this.mVGap = vGap;\n    }\n\n    public int getVGap() {\n        return this.mVGap;\n    }\n\n    public void setLane(int lane) {\n        this.mNumLanes = lane;\n        ensureLanes();\n    }\n\n    public int getLane() {\n        return this.mNumLanes;\n    }\n\n    /**\n     * @return the width of the lane\n     */\n    public int getColLength() {\n        return this.mColLength;\n    }\n\n    private void ensureLanes() {\n        if (mSpans == null || mSpans.length != mNumLanes || mRemainingSpans == null) {\n            mRemainingSpans = new BitSet(mNumLanes);\n            mSpans = new Span[mNumLanes];\n            for (int i = 0; i < mNumLanes; i++) {\n                mSpans[i] = new Span(i);\n            }\n        }\n    }\n\n    @Override\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutManagerHelper helper) {\n        super.beforeLayout(recycler, state, helper);\n\n        int availableWidth;\n        if (helper.getOrientation() == VERTICAL) {\n            availableWidth = helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight() - getHorizontalMargin() - getHorizontalPadding();\n        } else {\n            availableWidth = helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom() - getVerticalMargin() - getVerticalPadding();\n        }\n        mColLength = (int) ((availableWidth - mHGap * (mNumLanes - 1)) / mNumLanes + 0.5);\n        int totalGaps = availableWidth - mColLength * mNumLanes;\n\n        if (mNumLanes <= 1) {\n            mEachGap = mLastGap = 0;\n        } else if (mNumLanes == 2) {\n            mEachGap = totalGaps;\n            mLastGap = totalGaps;\n        } else {\n            mEachGap = mLastGap = helper.getOrientation() == VERTICAL ? mHGap : mVGap;\n        }\n\n        if (mLayoutManager == null || mLayoutManager.get() == null || mLayoutManager.get() != helper) {\n            if (helper instanceof VirtualLayoutManager) {\n                mLayoutManager = new WeakReference<VirtualLayoutManager>((VirtualLayoutManager) helper);\n            }\n        }\n    }\n\n    @Override\n    public void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state, int startPosition, int endPosition, int scrolled, LayoutManagerHelper helper) {\n        super.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n        mLayoutWithAnchor = false;\n        if (startPosition > getRange().getUpper() || endPosition < getRange().getLower()) {\n            //do not in visible screen, skip\n            return;\n        }\n        if (!state.isPreLayout() && helper.getChildCount() > 0) {\n            // call after doing layout, to check whether there is a gap between staggered layout and other layouts\n            ViewCompat.postOnAnimation(helper.getChildAt(0), checkForGapsRunnable);\n        }\n    }\n\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state,\n                            LayoutStateWrapper layoutState, LayoutChunkResult result,\n                            LayoutManagerHelper helper) {\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        ensureLanes();\n\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        final OrientationHelperEx secondaryOrientationHelper = helper.getSecondaryOrientationHelper();\n        final boolean isOverLapMargin = helper.isEnableMarginOverLap();\n\n        mRemainingSpans.set(0, mNumLanes, true);\n\n        final int targetLine;\n        final int recycleLine;\n\n        // Line of the furthest row.\n        if (layoutState.getLayoutDirection() == LAYOUT_END) {\n            // ignore padding for recycler\n            recycleLine = layoutState.getOffset() + layoutState.getAvailable();\n            targetLine = recycleLine + layoutState.getExtra() + orientationHelper.getEndPadding();\n        } else { // LAYOUT_START\n            // ignore padding for recycler\n            recycleLine = layoutState.getOffset() - layoutState.getAvailable();\n            targetLine = recycleLine - layoutState.getExtra() - orientationHelper.getStartAfterPadding();\n        }\n\n        updateAllRemainingSpans(layoutState.getLayoutDirection(), targetLine, orientationHelper);\n\n        final int defaultNewViewLine = layoutState.getOffset();\n\n        prelayoutViewList.clear();\n        while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty() && !isOutOfRange(layoutState.getCurrentPosition())) {\n            boolean isStartLine = false, isEndLine = false;\n            int currentPosition = layoutState.getCurrentPosition();\n            View view = layoutState.next(recycler);\n\n            if (view == null) {\n                break;\n            }\n\n            VirtualLayoutManager.LayoutParams lp = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n\n            // find the span to put the view\n            final int position = lp.getViewPosition();\n            final int spanIndex = mLazySpanLookup.getSpan(position);\n            Span currentSpan;\n            boolean assignSpan = spanIndex == INVALID_SPAN_ID;\n            if (assignSpan) {\n                currentSpan = getNextSpan(defaultNewViewLine, layoutState, helper);\n                mLazySpanLookup.setSpan(position, currentSpan);\n            } else {\n                currentSpan = mSpans[spanIndex];\n            }\n            // handle margin for start/end line\n            isStartLine = position - getRange().getLower() < mNumLanes;\n            isEndLine = getRange().getUpper() - position < mNumLanes; //fix the end line condition, edit by longerian\n\n            if (layoutState.isPreLayout()) {\n                prelayoutViewList.add(view);\n            }\n\n            helper.addChildView(layoutState, view);\n\n            if (layoutInVertical) {\n                int widthSpec = helper.getChildMeasureSpec(mColLength, lp.width, false);\n                int heightSpec = helper.getChildMeasureSpec(orientationHelper.getTotalSpace(),\n                        Float.isNaN(lp.mAspectRatio) ? lp.height : (int) (\n                                View.MeasureSpec.getSize(widthSpec) / lp.mAspectRatio + 0.5f), true);\n                helper.measureChildWithMargins(view, widthSpec, heightSpec);\n            } else {\n                int heightSpec = helper.getChildMeasureSpec(mColLength, lp.height, false);\n                int widthSpec = helper.getChildMeasureSpec(orientationHelper.getTotalSpace(),\n                        Float.isNaN(lp.mAspectRatio) ? lp.width : (int) (\n                                View.MeasureSpec.getSize(heightSpec) * lp.mAspectRatio + 0.5f), true);\n                helper.measureChildWithMargins(view, widthSpec, heightSpec);\n            }\n\n\n            int start;\n            int end;\n\n            if (layoutState.getLayoutDirection() == LAYOUT_END) {\n                start = currentSpan.getEndLine(defaultNewViewLine, orientationHelper);\n\n                if (isStartLine) {\n                    start += computeStartSpace(helper, layoutInVertical, true, isOverLapMargin);\n                } else {\n                    if (mLayoutWithAnchor) {\n                        if (Math.abs(currentPosition - anchorPosition) < mNumLanes) {\n                            //do not add extra gaps here\n                        } else {\n                            start += (layoutInVertical ? mVGap : mHGap);\n                        }\n                    } else {\n                        start += (layoutInVertical ? mVGap : mHGap);\n                    }\n                }\n                end = start + orientationHelper.getDecoratedMeasurement(view);\n            } else {\n                if (isEndLine) {\n                    end = currentSpan.getStartLine(defaultNewViewLine, orientationHelper) - (layoutInVertical ?\n                            mMarginBottom + mPaddingRight : mMarginRight + mPaddingRight);\n                    //Log.d(TAG, \"endLine \" + position + \" \" + end);\n                } else {\n                    end = currentSpan.getStartLine(defaultNewViewLine, orientationHelper) - (layoutInVertical ? mVGap : mHGap);\n                    //Log.d(TAG, \"normalEndLine \" + position + \" \" + end);\n                }\n                start = end - orientationHelper.getDecoratedMeasurement(view);\n            }\n\n            // lp.mSpan = currentSpan;\n            if (layoutState.getLayoutDirection() == LAYOUT_END) {\n                currentSpan.appendToSpan(view, orientationHelper);\n            } else {\n                currentSpan.prependToSpan(view, orientationHelper);\n            }\n\n            // left, right in vertical layout\n            int otherStart =\n                    ((currentSpan.mIndex == mNumLanes - 1) ?\n                            currentSpan.mIndex * (mColLength + mEachGap) - mEachGap + mLastGap\n                            : currentSpan.mIndex * (mColLength + mEachGap)) +\n                            secondaryOrientationHelper.getStartAfterPadding();\n\n            if (layoutInVertical) {\n                otherStart += mMarginLeft + mPaddingLeft;\n            } else {\n                otherStart += mMarginTop + mPaddingTop;\n            }\n\n            int otherEnd = otherStart + orientationHelper.getDecoratedMeasurementInOther(view);\n\n            if (layoutInVertical) {\n                layoutChildWithMargin(view, otherStart, start, otherEnd, end, helper);\n            } else {\n                layoutChildWithMargin(view, start, otherStart, end, otherEnd, helper);\n            }\n\n            updateRemainingSpans(currentSpan, layoutState.getLayoutDirection(), targetLine, orientationHelper);\n\n            recycle(recycler, layoutState, currentSpan, recycleLine, helper);\n\n            handleStateOnResult(result, view);\n        }\n\n        if (isOutOfRange(layoutState.getCurrentPosition()) && mSpans != null) {\n            // reach the end of layout, cache the gap\n            // TODO: how to retain gap\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    if (span.mCachedStart != INVALID_LINE) {\n                        span.mLastEdgeStart = span.mCachedStart;\n                    }\n                }\n            } else {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    if (span.mCachedEnd != INVALID_LINE) {\n                        span.mLastEdgeEnd = span.mCachedEnd;\n                    }\n                }\n            }\n        }\n        if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n            if (!isOutOfRange(layoutState.getCurrentPosition()) && layoutState.hasMore(state)) {\n                final int maxStart = getMaxStart(orientationHelper.getStartAfterPadding(), orientationHelper);\n                result.mConsumed = layoutState.getOffset() - maxStart;\n            } else {\n                final int minStart = getMinStart(orientationHelper.getEndAfterPadding(), orientationHelper);\n                result.mConsumed = layoutState.getOffset() - minStart + (layoutInVertical ? mMarginTop + mPaddingTop : mMarginLeft + mPaddingLeft);\n            }\n        } else {\n            if (!isOutOfRange(layoutState.getCurrentPosition()) && layoutState.hasMore(state)) {\n                final int minEnd = getMinEnd(orientationHelper.getEndAfterPadding(), orientationHelper);\n                result.mConsumed = minEnd - layoutState.getOffset();\n            } else {\n                final int maxEnd = getMaxEnd(orientationHelper.getEndAfterPadding(), orientationHelper);\n                result.mConsumed = maxEnd - layoutState.getOffset() + (layoutInVertical ? mMarginBottom + mPaddingBottom : mMarginRight + mPaddingRight);\n            }\n\n        }\n\n        recycleForPreLayout(recycler, layoutState, helper);\n    }\n\n    private void recycleForPreLayout(RecyclerView.Recycler recycler, LayoutStateWrapper layoutState, LayoutManagerHelper helper) {\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        for (int i = prelayoutViewList.size() - 1; i >= 0; i--) {\n            View child = prelayoutViewList.get(i);\n            if (child != null && orientationHelper.getDecoratedStart(child) > orientationHelper.getEndAfterPadding()) {\n                LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                int position = lp.getViewPosition();\n                Span span = findSpan(position, child, false);\n                if (span != null) {\n                    span.popEnd(orientationHelper);\n                }\n                helper.removeChildView(child);\n                recycler.recycleView(child);\n            } else {\n                LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                int position = lp.getViewPosition();\n                Span span = findSpan(position, child, false);\n                if (span != null) {\n                    span.popEnd(orientationHelper);\n                }\n                helper.removeChildView(child);\n                recycler.recycleView(child);\n                break;\n            }\n        }\n    }\n\n    @Override\n    public void onScrollStateChanged(int state, int startPosition,\n                                     int endPosition, LayoutManagerHelper helper) {\n        if (startPosition > getRange().getUpper() || endPosition < getRange().getLower()) {\n            return;\n        }\n\n        if (state == RecyclerView.SCROLL_STATE_IDLE) {\n            checkForGaps();\n        }\n    }\n\n\n    @Override\n    public int computeAlignOffset(int offset, boolean isLayoutEnd,\n                                  boolean useAnchor, LayoutManagerHelper helper) {\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        final View child = helper.findViewByPosition(offset + getRange().getLower());\n\n        if (child == null) {\n            return 0;\n        }\n        ensureLanes();\n        if (layoutInVertical) {\n            // in middle nothing need to do\n            if (isLayoutEnd) {\n                if (offset == getItemCount() - 1) {\n                    //the last item may not have the largest bottom, so calculate gaps between last items in every lane\n                    return mMarginBottom + mPaddingBottom + (getMaxEnd(orientationHelper.getDecoratedEnd(child),\n                            orientationHelper) - orientationHelper.getDecoratedEnd(child));\n                } else if (!useAnchor) {\n                    final int minEnd = getMinEnd(orientationHelper.getDecoratedStart(child), orientationHelper);\n                    return minEnd - orientationHelper.getDecoratedEnd(child);\n                }\n            } else {\n                if (offset == 0) {\n                    //the first item may not have the smallest top, so calculate gaps between first items in every lane\n                    return -mMarginTop - mPaddingTop - (orientationHelper.getDecoratedStart(child) - getMinStart(\n                            orientationHelper.getDecoratedStart(child), orientationHelper));\n                } else if (!useAnchor) {\n                    final int maxStart = getMaxStart(orientationHelper.getDecoratedEnd(child), orientationHelper);\n                    return maxStart - orientationHelper.getDecoratedStart(child);\n                }\n            }\n\n        } else {\n\n        }\n\n        return 0;\n    }\n\n\n    @Override\n    public void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n        mLazySpanLookup.clear();\n        mSpans = null;\n        mLayoutManager = null;\n    }\n\n    /**\n     * check whether there are gaps that need to be fixed\n     */\n\n    private void checkForGaps() {\n        if (mLayoutManager == null) {\n            return;\n        }\n\n        final VirtualLayoutManager layoutManager = mLayoutManager.get();\n\n        if (layoutManager == null || layoutManager.getChildCount() == 0) {\n            return;\n        }\n\n        final Range<Integer> range = getRange();\n\n        // align position, which should check gap for\n        final int minPos, maxPos, alignPos;\n        if (layoutManager.getReverseLayout()) {\n            minPos = layoutManager.findLastVisibleItemPosition();\n            maxPos = layoutManager.findFirstVisibleItemPosition();\n            alignPos = range.getUpper() - 1;\n        } else {\n            minPos = layoutManager.findFirstVisibleItemPosition();\n            maxPos = layoutManager.findLastCompletelyVisibleItemPosition();\n            alignPos = range.getLower();\n        }\n\n\n        final OrientationHelperEx orientationHelper = layoutManager.getMainOrientationHelper();\n        final int childCount = layoutManager.getChildCount();\n        int viewAnchor = Integer.MIN_VALUE;\n        int alignLine = Integer.MIN_VALUE;\n\n        // find view anchor and get align line, the views should be aligned to alignLine\n        if (layoutManager.getReverseLayout()) {\n            for (int i = childCount - 1; i >= 0; i--) {\n                View view = layoutManager.getChildAt(i);\n                int position = layoutManager.getPosition(view);\n                if (position == alignPos) {\n                    viewAnchor = position;\n                    if (i == childCount - 1) {\n                        // if last child, alignLine is the end of child\n                        alignLine = orientationHelper.getDecoratedEnd(view);\n                    } else {\n                        // if not, alignLine is the start of next child\n                        View child = layoutManager.getChildAt(i + 1);\n                        int aPos = layoutManager.getPosition(child);\n                        if (aPos == position - 1) {\n                            // if position is sequence, which means the next child is not hidden one\n                            alignLine = orientationHelper.getDecoratedStart(child) - layoutManager.obtainExtraMargin(child, false)\n                                    + layoutManager.obtainExtraMargin(view, true);\n                        } else {\n                            // if next child is hidden one, use end of current view\n                            alignLine = orientationHelper.getDecoratedEnd(view);\n                        }\n                    }\n                    break;\n                }\n            }\n        } else {\n            for (int i = 0; i < childCount; i++) {\n                View view = layoutManager.getChildAt(i);\n                int position = layoutManager.getPosition(view);\n                if (position == alignPos) {\n                    viewAnchor = position;\n                    if (i == 0) {\n                        // TODO: there is problem\n                        // if first child, alignLine is the start\n                        alignLine = orientationHelper.getDecoratedStart(view);\n                    } else {\n                        // if not, alignLine is the end of previous child\n                        View child = layoutManager.getChildAt(i - 1);\n                        alignLine = orientationHelper.getDecoratedEnd(child) + layoutManager.obtainExtraMargin(child, true, false)\n                                - layoutManager.obtainExtraMargin(view, false, false);\n                        int viewStart = orientationHelper.getDecoratedStart(view);\n                        if (alignLine == viewStart) {\n                            //actually not gap here skip;\n                            viewAnchor = Integer.MIN_VALUE;\n                        } else {\n                            int nextPosition = layoutManager.getPosition(child);\n                            if (nextPosition != alignPos - 1) {\n                                //may has sticky layout, add extra space occur by stickyLayoutHelper\n                                LayoutHelper layoutHelper = layoutManager.findLayoutHelperByPosition(alignPos - 1);\n                                if (layoutHelper != null && layoutHelper instanceof StickyLayoutHelper) {\n                                    if (layoutHelper.getFixedView() != null) {\n                                        alignLine += layoutHelper.getFixedView().getMeasuredHeight();\n                                    }\n                                }\n                            } else {\n                                LayoutHelper layoutHelper = layoutManager.findLayoutHelperByPosition(nextPosition);\n                                layoutHelper.getRange();\n                            }\n                        }\n                    }\n                    break;\n                }\n            }\n        }\n\n        if (viewAnchor == Integer.MIN_VALUE) {\n            // if not find view anchor, break\n            return;\n        }\n\n        View gapView = hasGapsToFix(layoutManager, viewAnchor, alignLine);\n        if (gapView != null) {\n            //FIXME do not clear loopup, may cause lane error while scroll\n            //mLazySpanLookup.clear();\n\n            if (mSpans != null) {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    span.setLine(alignLine);\n                }\n            }\n\n            layoutManager.requestSimpleAnimationsInNextLayout();\n            layoutManager.requestLayout();\n        }\n    }\n\n    /**\n     * Checks for gaps if we've reached to the top of the list.\n     * <p/>\n     * Intermediate gaps created by full span items are tracked via mLaidOutInvalidFullSpan field.\n     */\n    private View hasGapsToFix(VirtualLayoutManager layoutManager, final int position, final int alignLine) {\n        View view = layoutManager.findViewByPosition(position);\n\n        if (view == null) {\n            return null;\n        }\n\n\n        BitSet mSpansToCheck = new BitSet(mNumLanes);\n        mSpansToCheck.set(0, mNumLanes, true);\n\n        if (mSpans != null) {\n            for (int i = 0, size = mSpans.length; i < size; i++) {\n                Span span = mSpans[i];\n                if (span.mViews.size() != 0 && checkSpanForGap(span, layoutManager, alignLine)) {\n                    return layoutManager.getReverseLayout() ? span.mViews.get(span.mViews.size() - 1) : span.mViews.get(0);\n                }\n            }\n        }\n\n        // everything looks good\n        return null;\n    }\n\n\n    private boolean checkSpanForGap(Span span, VirtualLayoutManager layoutManager, int line) {\n        OrientationHelperEx orientationHelper = layoutManager.getMainOrientationHelper();\n        if (layoutManager.getReverseLayout()) {\n            if (span.getEndLine(orientationHelper) < line) {\n                return true;\n            }\n        } else if (span.getStartLine(orientationHelper) > line) {\n            return true;\n        }\n        return false;\n    }\n\n\n    private void recycle(RecyclerView.Recycler recycler, LayoutStateWrapper layoutState,\n                         Span updatedSpan, int recycleLine, LayoutManagerHelper helper) {\n        OrientationHelperEx orientation = helper.getMainOrientationHelper();\n        if (layoutState.getLayoutDirection() == LAYOUT_START) {\n            // calculate recycle line\n            int maxStart = getMaxStart(updatedSpan.getStartLine(orientation), orientation);\n            recycleFromEnd(recycler, Math.max(recycleLine, maxStart) +\n                    (orientation.getEnd() - orientation.getStartAfterPadding()), helper);\n        } else {\n            // calculate recycle line\n            int minEnd = getMinEnd(updatedSpan.getEndLine(orientation), orientation);\n            recycleFromStart(recycler, Math.min(recycleLine, minEnd) -\n                    (orientation.getEnd() - orientation.getStartAfterPadding()), helper);\n        }\n    }\n\n    private void recycleFromStart(RecyclerView.Recycler recycler, int line, LayoutManagerHelper helper) {\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        boolean changed = true;\n        while (helper.getChildCount() > 0 && changed) {\n            View child = helper.getChildAt(0);\n            if (child != null && orientationHelper.getDecoratedEnd(child) < line) {\n                LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                int position = lp.getViewPosition();\n                Span span = findSpan(position, child, true);\n                if (span != null) {\n                    span.popStart(orientationHelper);\n                    helper.removeChildView(child);\n                    recycler.recycleView(child);\n                } else {\n                    changed = false;\n                }\n            } else {\n                return;// done\n            }\n        }\n    }\n\n    private void recycleFromEnd(RecyclerView.Recycler recycler, int line, LayoutManagerHelper helper) {\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        final int childCount = helper.getChildCount();\n        int i;\n        for (i = childCount - 1; i >= 0; i--) {\n            View child = helper.getChildAt(i);\n            if (child != null && orientationHelper.getDecoratedStart(child) > line) {\n                LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                int position = lp.getViewPosition();\n                Span span = findSpan(position, child, false);\n                if (span != null) {\n                    span.popEnd(orientationHelper);\n                    helper.removeChildView(child);\n                    recycler.recycleView(child);\n                }\n            } else {\n                return;// done\n            }\n        }\n    }\n\n\n    private Span findSpan(int position, View child, boolean isStart) {\n        int span = mLazySpanLookup.getSpan(position);\n        if (mSpans == null){\n            return null;\n        }\n        if (span >= 0 && span < mSpans.length) {\n            Span sp = mSpans[span];\n            if (isStart && sp.findStart(child)) {\n                return sp;\n            } else if (!isStart && sp.findEnd(child)) {\n                return sp;\n            }\n        }\n\n        for (int i = 0; i < mSpans.length; i++) {\n            if (i == span) {\n                continue;\n            }\n\n            Span sp = mSpans[i];\n            if (isStart && sp.findStart(child)) {\n                return sp;\n            } else if (!isStart && sp.findEnd(child)) {\n                return sp;\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public boolean isRecyclable(int childPos, int startIndex, int endIndex, LayoutManagerHelper helper, boolean fromStart) {\n        // startIndex == endIndex already be ignored in VirtualLayoutManager.recycleChildren\n        final boolean recyclable = super.isRecyclable(childPos, startIndex, endIndex, helper, fromStart);\n\n        if (recyclable) {\n            View child = helper.findViewByPosition(childPos);\n\n            if (child != null) {\n                final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n                LayoutParams lp = (LayoutParams) child.getLayoutParams();\n                int position = lp.getViewPosition();\n\n                if (helper.getReverseLayout()) {\n                    if (fromStart) {\n                        // recycle from end\n                        Span span = findSpan(position, child, true);\n                        if (span != null) {\n                            span.popEnd(orientationHelper);\n                        }\n                    } else {\n                        // recycle from start\n                        Span span = findSpan(position, child, false);\n                        if (span != null) {\n                            span.popStart(orientationHelper);\n                        }\n                    }\n                } else {\n                    if (fromStart) {\n                        // recycle from start\n                        Span span = findSpan(position, child, true);\n                        if (span != null) {\n                            span.popStart(orientationHelper);\n                        }\n                    } else {\n                        // recycle from end\n                        Span span = findSpan(position, child, false);\n                        if (span != null) {\n                            span.popEnd(orientationHelper);\n                        }\n                    }\n                }\n            }\n        }\n\n        return recyclable;\n    }\n\n    private void updateAllRemainingSpans(int layoutDir, int targetLine, OrientationHelperEx helper) {\n        for (int i = 0; i < mNumLanes; i++) {\n            if (mSpans[i].mViews.isEmpty()) {\n                continue;\n            }\n            updateRemainingSpans(mSpans[i], layoutDir, targetLine, helper);\n        }\n    }\n\n    private void updateRemainingSpans(Span span, int layoutDir, int targetLine, OrientationHelperEx helper) {\n        final int deletedSize = span.getDeletedSize();\n        if (layoutDir == LayoutStateWrapper.LAYOUT_START) {\n            final int line = span.getStartLine(helper);\n            if (line + deletedSize < targetLine) {\n                mRemainingSpans.set(span.mIndex, false);\n            }\n        } else {\n            final int line = span.getEndLine(helper);\n            if (line - deletedSize > targetLine) {\n                mRemainingSpans.set(span.mIndex, false);\n            }\n        }\n    }\n\n    /**\n     * Finds the span for the next view.\n     */\n    private Span getNextSpan(int defaultLine, LayoutStateWrapper layoutState, LayoutManagerHelper helper) {\n        OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        boolean preferLastSpan = false;\n\n        if (helper.getOrientation() == HORIZONTAL) {\n            preferLastSpan = (layoutState.getLayoutDirection() == LAYOUT_START) != helper.getReverseLayout();\n        } else {\n            preferLastSpan = ((layoutState.getLayoutDirection() == LAYOUT_START) == helper.getReverseLayout()) == helper.isDoLayoutRTL();\n        }\n\n\n        final int startIndex, endIndex, diff;\n        if (preferLastSpan) {\n            startIndex = mNumLanes - 1;\n            endIndex = -1;\n            diff = -1;\n        } else {\n            startIndex = 0;\n            endIndex = mNumLanes;\n            diff = 1;\n        }\n        if (layoutState.getLayoutDirection() == LAYOUT_END) {\n            Span min = null;\n            int minLine = Integer.MAX_VALUE;\n            for (int i = startIndex; i != endIndex; i += diff) {\n                final Span other = mSpans[i];\n                int otherLine = other.getEndLine(defaultLine, orientationHelper);\n                if (BuildConfig.DEBUG) {\n                    Log.d(TAG, \"end starIndex \" + i + \" end otherLine \" + otherLine);\n                }\n                if (otherLine < minLine) {\n                    min = other;\n                    minLine = otherLine;\n                }\n                if (BuildConfig.DEBUG) {\n                    Log.d(TAG, \"end min \" + min + \" end minLine \" + minLine);\n                }\n            }\n            if (BuildConfig.DEBUG) {\n                Log.d(TAG, \"final end min \" + min + \" final end minLine \" + minLine);\n            }\n            return min;\n        } else {\n            Span max = null;\n            int maxLine = Integer.MIN_VALUE;\n            for (int i = startIndex; i != endIndex; i += diff) {\n                final Span other = mSpans[i];\n                int otherLine = other.getStartLine(defaultLine, orientationHelper);\n                if (BuildConfig.DEBUG) {\n                    Log.d(TAG, \"start starIndex \" + i + \" start otherLine \" + otherLine);\n                }\n                if (otherLine > maxLine) {\n                    max = other;\n                    maxLine = otherLine;\n                }\n                if (BuildConfig.DEBUG) {\n                    Log.d(TAG, \"start max \" + max + \" start maxLine \" + maxLine);\n                }\n            }\n            if (BuildConfig.DEBUG) {\n                Log.d(TAG, \"final start max \" + max + \" final start maxLine \" + maxLine);\n            }\n            return max;\n        }\n    }\n\n\n    private int getMaxStart(int defaultValue, OrientationHelperEx helper) {\n        int maxStart = mSpans[0].getStartLine(defaultValue, helper);\n        for (int i = 1; i < mNumLanes; i++) {\n            final int spanStart = mSpans[i].getStartLine(defaultValue, helper);\n            if (spanStart > maxStart) {\n                maxStart = spanStart;\n            }\n        }\n        return maxStart;\n    }\n\n    private int getMinStart(int defaultValue, OrientationHelperEx helper) {\n        int minStart = mSpans[0].getStartLine(defaultValue, helper);\n        for (int i = 1; i < mNumLanes; i++) {\n            final int spanStart = mSpans[i].getStartLine(defaultValue, helper);\n            if (spanStart < minStart) {\n                minStart = spanStart;\n            }\n        }\n        return minStart;\n    }\n\n    private int getMaxEnd(int defaultValue, OrientationHelperEx helper) {\n        int maxEnd = mSpans[0].getEndLine(defaultValue, helper);\n        for (int i = 1; i < mNumLanes; i++) {\n            final int spanEnd = mSpans[i].getEndLine(defaultValue, helper);\n            if (spanEnd > maxEnd) {\n                maxEnd = spanEnd;\n            }\n        }\n        return maxEnd;\n    }\n\n    private int getMinEnd(int defaultValue, OrientationHelperEx helper) {\n        int minEnd = mSpans[0].getEndLine(defaultValue, helper);\n        for (int i = 1; i < mNumLanes; i++) {\n            final int spanEnd = mSpans[i].getEndLine(defaultValue, helper);\n            if (spanEnd < minEnd) {\n                minEnd = spanEnd;\n            }\n        }\n        return minEnd;\n    }\n\n\n    @Override\n    public void onRefreshLayout(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        super.onRefreshLayout(state, anchorInfo, helper);\n        ensureLanes();\n\n        if (isOutOfRange(anchorInfo.position)) {\n            if (BuildConfig.DEBUG) {\n                Log.d(TAG, \"onRefreshLayout span.clear()\");\n            }\n            if (mSpans != null) {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    span.clear();\n                }\n            }\n        }\n    }\n\n    @Override\n    public void checkAnchorInfo(RecyclerView.State state, VirtualLayoutManager.AnchorInfoWrapper anchorInfo, LayoutManagerHelper helper) {\n        super.checkAnchorInfo(state, anchorInfo, helper);\n        ensureLanes();\n\n        final Range<Integer> range = getRange();\n        if (anchorInfo.layoutFromEnd) {\n            if (anchorInfo.position < range.getLower() + mNumLanes - 1) {\n                anchorInfo.position = Math.min(range.getLower() + mNumLanes - 1, range.getUpper());\n            }\n        } else {\n            if (anchorInfo.position > range.getUpper() - (mNumLanes - 1)) {\n                anchorInfo.position = Math.max(range.getLower(), range.getUpper() - (mNumLanes - 1));\n            }\n        }\n\n\n        View reference = helper.findViewByPosition(anchorInfo.position);\n\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        int mainGap = layoutInVertical ? mVGap : mHGap;\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        if (reference == null) {\n            if (BuildConfig.DEBUG) {\n                Log.d(TAG, \"checkAnchorInfo span.clear()\");\n            }\n            if (mSpans != null) {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    span.clear();\n                    span.setLine(anchorInfo.coordinate);\n                }\n            }\n        } else {\n            int anchorPos = anchorInfo.layoutFromEnd ? Integer.MIN_VALUE : Integer.MAX_VALUE;\n            if (mSpans != null) {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    if (!span.mViews.isEmpty()) {\n                        if (anchorInfo.layoutFromEnd) {\n                            View view = span.mViews.get(span.mViews.size() - 1);\n                            anchorPos = Math.max(anchorPos, helper.getPosition(view));\n                        } else {\n                            View view = span.mViews.get(0);\n                            anchorPos = Math.min(anchorPos, helper.getPosition(view));\n                        }\n                    }\n                }\n            }\n\n            int offset = INVALID_OFFSET;\n            if (!isOutOfRange(anchorPos)) {\n                boolean isStartLine = anchorPos == range.getLower();\n                View view = helper.findViewByPosition(anchorPos);\n\n                if (view != null) {\n                    if (anchorInfo.layoutFromEnd) {\n                        anchorInfo.position = anchorPos;\n                        final int endRef = orientationHelper.getDecoratedEnd(reference);\n                        if (endRef < anchorInfo.coordinate) {\n                            offset = anchorInfo.coordinate - endRef;\n                            offset += (isStartLine ? 0 : mainGap);\n                            anchorInfo.coordinate = orientationHelper.getDecoratedEnd(view) + offset;\n                        } else {\n                            offset = (isStartLine ? 0 : mainGap);\n                            anchorInfo.coordinate = orientationHelper.getDecoratedEnd(view) + offset;\n                        }\n\n                    } else {\n                        anchorInfo.position = anchorPos;\n                        final int startRef = orientationHelper.getDecoratedStart(reference);\n                        if (startRef > anchorInfo.coordinate) {\n                            // move align up\n                            offset = anchorInfo.coordinate - startRef;\n                            offset -= (isStartLine ? 0 : mainGap);\n                            anchorInfo.coordinate = orientationHelper.getDecoratedStart(view) + offset;\n                        } else {\n                            offset = -(isStartLine ? 0 : mainGap);\n                            anchorInfo.coordinate = orientationHelper.getDecoratedStart(view) + offset;\n                        }\n                    }\n                }\n            } else {\n                anchorPosition = anchorInfo.position;\n                mLayoutWithAnchor = true;\n            }\n\n            if (BuildConfig.DEBUG) {\n                Log.d(TAG, \"checkAnchorInfo span.cacheReferenceLineAndClear()\");\n            }\n            if (mSpans != null) {\n                for (int i = 0, size = mSpans.length; i < size; i++) {\n                    Span span = mSpans[i];\n                    span.cacheReferenceLineAndClear(helper.getReverseLayout() ^ anchorInfo.layoutFromEnd, offset, orientationHelper);\n                }\n            }\n        }\n    }\n\n\n    @Override\n    public void onItemsChanged(LayoutManagerHelper helper) {\n//        mLazySpanLookup.clear();\n    }\n\n    @Override\n    public void onSaveState(Bundle bundle) {\n        super.onSaveState(bundle);\n        bundle.putIntArray(LOOKUP_BUNDLE_KEY, mLazySpanLookup.mData);\n        // TODO: store span info\n    }\n\n    @Override\n    public void onRestoreInstanceState(Bundle bundle) {\n        super.onRestoreInstanceState(bundle);\n        mLazySpanLookup.mData = bundle.getIntArray(LOOKUP_BUNDLE_KEY);\n    }\n\n\n    @Override\n    public void onOffsetChildrenVertical(int dy, LayoutManagerHelper helper) {\n        super.onOffsetChildrenVertical(dy, helper);\n        if (helper.getOrientation() == VERTICAL && mSpans != null) {\n            for (int i = 0, size = mSpans.length; i < size; i++) {\n                Span span = mSpans[i];\n                span.onOffset(dy);\n            }\n        }\n    }\n\n    @Override\n    public void onOffsetChildrenHorizontal(int dx, LayoutManagerHelper helper) {\n        super.onOffsetChildrenHorizontal(dx, helper);\n        if (helper.getOrientation() == HORIZONTAL && mSpans != null) {\n            for (int i = 0, size = mSpans.length; i < size; i++) {\n                Span span = mSpans[i];\n                span.onOffset(dx);\n            }\n        }\n    }\n\n\n    // Package scoped to access from tests.\n    static class Span {\n\n        static final int INVALID_OFFSET = Integer.MIN_VALUE;\n        private ArrayList<View> mViews = new ArrayList<View>();\n        int mCachedStart = INVALID_LINE;\n        int mCachedEnd = INVALID_LINE;\n        int mDeletedSize = 0;\n        final int mIndex;\n        int mLastEdgeStart = INVALID_LINE;\n        int mLastEdgeEnd = INVALID_LINE;\n\n        private Span(int index) {\n            mIndex = index;\n        }\n\n        void calculateCachedStart(@NonNull OrientationHelperEx helper) {\n            if (mViews.size() == 0) {\n                mCachedStart = INVALID_LINE;\n            } else {\n                final View startView = mViews.get(0);\n                mCachedStart = helper.getDecoratedStart(startView);\n            }\n        }\n\n        int getStartLine(OrientationHelperEx helper) {\n            return getStartLine(INVALID_LINE, helper);\n        }\n\n        // Use this one when default value does not make sense and not having a value means a bug.\n        int getStartLine(int defaultValue, OrientationHelperEx helper) {\n            if (mCachedStart != INVALID_LINE) {\n                return mCachedStart;\n            }\n\n            if (defaultValue != INVALID_LINE && mViews.size() == 0) {\n                if (mLastEdgeEnd != INVALID_LINE) {\n                    return mLastEdgeEnd;\n                }\n                return defaultValue;\n            }\n\n            calculateCachedStart(helper);\n            return mCachedStart;\n        }\n\n        void calculateCachedEnd(OrientationHelperEx helper) {\n            if (mViews.size() == 0) {\n                mCachedEnd = INVALID_LINE;\n            } else {\n                final View endView = mViews.get(mViews.size() - 1);\n                mCachedEnd = helper.getDecoratedEnd(endView);\n            }\n        }\n\n        int getEndLine(OrientationHelperEx helper) {\n            return getEndLine(INVALID_LINE, helper);\n        }\n\n        // Use this one when default value does not make sense and not having a value means a bug.\n        int getEndLine(int defaultValue, OrientationHelperEx helper) {\n            if (mCachedEnd != INVALID_LINE) {\n                return mCachedEnd;\n            }\n\n            if (defaultValue != INVALID_LINE && mViews.size() == 0) {\n                if (mLastEdgeStart != INVALID_LINE) {\n                    return mLastEdgeStart;\n                }\n                return defaultValue;\n            }\n\n            calculateCachedEnd(helper);\n            return mCachedEnd;\n        }\n\n        void prependToSpan(View view, OrientationHelperEx helper) {\n            LayoutParams lp = getLayoutParams(view);\n            mViews.add(0, view);\n            mCachedStart = INVALID_LINE;\n            if (mViews.size() == 1) {\n                mCachedEnd = INVALID_LINE;\n            }\n            if (lp.isItemRemoved() || lp.isItemChanged()) {\n                mDeletedSize += helper.getDecoratedMeasurement(view);\n            }\n        }\n\n        void appendToSpan(View view, OrientationHelperEx helper) {\n            LayoutParams lp = getLayoutParams(view);\n            mViews.add(view);\n            mCachedEnd = INVALID_LINE;\n            if (mViews.size() == 1) {\n                mCachedStart = INVALID_LINE;\n            }\n            if (lp.isItemRemoved() || lp.isItemChanged()) {\n                mDeletedSize += helper.getDecoratedMeasurement(view);\n            }\n        }\n\n        // Useful method to preserve positions on a re-layout.\n        void cacheReferenceLineAndClear(boolean reverseLayout, int offset, OrientationHelperEx helper) {\n            int reference;\n            if (reverseLayout) {\n                reference = getEndLine(helper);\n            } else {\n                reference = getStartLine(helper);\n            }\n            clear();\n            if (reference == INVALID_LINE) {\n                return;\n            }\n            if ((reverseLayout && reference < helper.getEndAfterPadding()) ||\n                    (!reverseLayout && reference > helper.getStartAfterPadding())) {\n                // return;\n            }\n            if (offset != INVALID_OFFSET) {\n                reference += offset;\n            }\n            mCachedStart = mCachedEnd = reference;\n            mLastEdgeStart = mLastEdgeEnd = INVALID_LINE;\n        }\n\n        void clear() {\n            mViews.clear();\n            invalidateCache();\n            mDeletedSize = 0;\n        }\n\n        void invalidateCache() {\n            mCachedStart = INVALID_LINE;\n            mCachedEnd = INVALID_LINE;\n            mLastEdgeEnd = INVALID_LINE;\n            mLastEdgeStart = INVALID_LINE;\n        }\n\n        void setLine(int line) {\n            mCachedEnd = mCachedStart = line;\n            mLastEdgeStart = mLastEdgeEnd = INVALID_LINE;\n        }\n\n        void popEnd(OrientationHelperEx helper) {\n            final int size = mViews.size();\n            View end = mViews.remove(size - 1);\n            final LayoutParams lp = getLayoutParams(end);\n            if (lp.isItemRemoved() || lp.isItemChanged()) {\n                mDeletedSize -= helper.getDecoratedMeasurement(end);\n            }\n            if (size == 1) {\n                mCachedStart = INVALID_LINE;\n            }\n            mCachedEnd = INVALID_LINE;\n        }\n\n        boolean findEnd(View view) {\n            final int size = mViews.size();\n            if (size > 0) {\n                return mViews.get(size - 1) == view;\n            }\n            return false;\n        }\n\n\n        void popStart(OrientationHelperEx helper) {\n            View start = mViews.remove(0);\n            final LayoutParams lp = getLayoutParams(start);\n            if (mViews.size() == 0) {\n                mCachedEnd = INVALID_LINE;\n            }\n            if (lp.isItemRemoved() || lp.isItemChanged()) {\n                mDeletedSize -= helper.getDecoratedMeasurement(start);\n            }\n            mCachedStart = INVALID_LINE;\n        }\n\n\n        boolean findStart(View view) {\n            final int size = mViews.size();\n            if (size > 0) {\n                return mViews.get(0) == view;\n            }\n            return false;\n        }\n\n\n        public int getDeletedSize() {\n            return mDeletedSize;\n        }\n\n        LayoutParams getLayoutParams(View view) {\n            return (LayoutParams) view.getLayoutParams();\n        }\n\n        void onOffset(int dt) {\n            if (mLastEdgeStart != INVALID_LINE) {\n                mLastEdgeStart += dt;\n            }\n\n            if (mCachedStart != INVALID_LINE) {\n                mCachedStart += dt;\n            }\n\n            if (mLastEdgeEnd != INVALID_LINE) {\n                mLastEdgeEnd += dt;\n            }\n\n            if (mCachedEnd != INVALID_LINE) {\n                mCachedEnd += dt;\n            }\n        }\n\n        // normalized offset is how much this span can scroll\n        int getNormalizedOffset(int dt, int targetStart, int targetEnd, OrientationHelperEx helper) {\n            if (mViews.size() == 0) {\n                return 0;\n            }\n            if (dt < 0) {\n                final int endSpace = getEndLine(0, helper) - targetEnd;\n                if (endSpace <= 0) {\n                    return 0;\n                }\n                return -dt > endSpace ? -endSpace : dt;\n            } else {\n                final int startSpace = targetStart - getStartLine(0, helper);\n                if (startSpace <= 0) {\n                    return 0;\n                }\n                return startSpace < dt ? startSpace : dt;\n            }\n        }\n\n        /**\n         * Returns if there is no child between start-end lines\n         *\n         * @param start The start line\n         * @param end   The end line\n         * @return true if a new child can be added between start and end\n         */\n        boolean isEmpty(int start, int end, OrientationHelperEx orientationHelper) {\n            final int count = mViews.size();\n            for (int i = 0; i < count; i++) {\n                final View view = mViews.get(i);\n                if (orientationHelper.getDecoratedStart(view) < end &&\n                        orientationHelper.getDecoratedEnd(view) > start) {\n                    return false;\n                }\n            }\n            return true;\n        }\n    }\n\n\n    /**\n     * An array of mappings from adapter position to span.\n     * This only grows when a write happens and it grows up to the size of the adapter.\n     */\n    static class LazySpanLookup {\n\n        private static final int MIN_SIZE = 10;\n        int[] mData;\n\n        /**\n         * returns end position for invalidation.\n         */\n        int invalidateAfter(int position) {\n            if (mData == null) {\n                return RecyclerView.NO_POSITION;\n            }\n            if (position >= mData.length) {\n                return RecyclerView.NO_POSITION;\n            }\n\n            Arrays.fill(mData, position, mData.length, INVALID_SPAN_ID);\n            return mData.length;\n        }\n\n        int getSpan(int position) {\n            if (mData == null || position >= mData.length || position < 0) {\n                return INVALID_SPAN_ID;\n            } else {\n                return mData[position];\n            }\n        }\n\n        void setSpan(int position, Span span) {\n            ensureSize(position);\n            mData[position] = span.mIndex;\n        }\n\n        int sizeForPosition(int position) {\n            int len = mData.length;\n            while (len <= position) {\n                len *= 2;\n            }\n            return len;\n        }\n\n        void ensureSize(int position) {\n            if (mData == null) {\n                mData = new int[Math.max(position, MIN_SIZE) + 1];\n                Arrays.fill(mData, INVALID_SPAN_ID);\n            } else if (position >= mData.length) {\n                int[] old = mData;\n                mData = new int[sizeForPosition(position)];\n                System.arraycopy(old, 0, mData, 0, old.length);\n                Arrays.fill(mData, old.length, mData.length, INVALID_SPAN_ID);\n            }\n        }\n\n        void clear() {\n            if (mData != null) {\n                Arrays.fill(mData, INVALID_SPAN_ID);\n            }\n        }\n\n        void offsetForRemoval(int positionStart, int itemCount) {\n            if (mData == null || positionStart >= mData.length) {\n                return;\n            }\n            ensureSize(positionStart + itemCount);\n            System.arraycopy(mData, positionStart + itemCount, mData, positionStart,\n                    mData.length - positionStart - itemCount);\n            Arrays.fill(mData, mData.length - itemCount, mData.length,\n                    INVALID_SPAN_ID);\n        }\n\n\n        void offsetForAddition(int positionStart, int itemCount) {\n            if (mData == null || positionStart >= mData.length) {\n                return;\n            }\n            ensureSize(positionStart + itemCount);\n            System.arraycopy(mData, positionStart, mData, positionStart + itemCount,\n                    mData.length - positionStart - itemCount);\n            Arrays.fill(mData, positionStart, positionStart + itemCount,\n                    INVALID_SPAN_ID);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "vlayout/src/main/java/com/alibaba/android/vlayout/layout/StickyLayoutHelper.java",
    "content": "/*\n * MIT License\n *\n * Copyright (c) 2016 Alibaba Group\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\npackage com.alibaba.android.vlayout.layout;\n\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.alibaba.android.vlayout.LayoutHelper;\nimport com.alibaba.android.vlayout.LayoutManagerHelper;\nimport com.alibaba.android.vlayout.OrientationHelperEx;\nimport com.alibaba.android.vlayout.VirtualLayoutManager;\nimport com.alibaba.android.vlayout.VirtualLayoutManager.LayoutStateWrapper;\n\nimport java.util.List;\n\nimport static android.support.v7.widget.LinearLayoutManager.VERTICAL;\n\n\n/**\n * Layout which allow item sticky to start/end\n *\n * @author villadora\n * @since 1.0.0\n */\npublic class StickyLayoutHelper extends FixAreaLayoutHelper {\n    public interface StickyListener {\n        void onSticky(int pos, View view);\n\n        void onUnSticky(int pos, View view);\n    }\n\n    public interface Stackable {\n        boolean enable();\n    }\n\n    private static final String TAG = \"StickyStartLayoutHelper\";\n\n    private int mPos = -1;\n\n    private boolean mStickyStart = true;\n\n    private int mOffset = 0;\n\n    private View mFixView = null;\n\n    private boolean mDoNormalHandle = false;\n\n    private boolean isLastStatusSticking = false;\n\n    private StickyListener stickyListener;\n    private Stackable mStackable;\n\n    public StickyLayoutHelper() {\n        this(true);\n    }\n\n    public StickyLayoutHelper(boolean stickyStart) {\n        this.mStickyStart = stickyStart;\n        setItemCount(1);\n    }\n\n    public void setStickyStart(boolean stickyStart) {\n        this.mStickyStart = stickyStart;\n    }\n\n    public void setOffset(int offset) {\n        this.mOffset = offset;\n    }\n\n    public boolean isStickyNow() {\n        return !mDoNormalHandle && mFixView != null;\n    }\n\n    @Override\n    public void setItemCount(int itemCount) {\n        if (itemCount > 0) {\n            super.setItemCount(1);\n        } else {\n            super.setItemCount(0);\n        }\n    }\n\n    @Override\n    public void onRangeChange(int start, int end) {\n        mPos = start;\n    }\n\n    @Override\n    public void layoutViews(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutStateWrapper layoutState, LayoutChunkResult result, LayoutManagerHelper helper) {\n        // reach the end of this layout\n        if (isOutOfRange(layoutState.getCurrentPosition())) {\n            return;\n        }\n\n        // find view in currentPosition\n        View view = mFixView;\n        if (view == null) {\n            view = layoutState.next(recycler);\n        } else {\n            layoutState.skipCurrentPosition();\n        }\n        if (view == null) {\n            result.mFinished = true;\n            return;\n        }\n\n\n        doMeasure(view, helper);\n\n\n        // do layout\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n        result.mConsumed = orientationHelper.getDecoratedMeasurement(view);\n\n        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();\n        // Do normal measure&layout phase by default\n        mDoNormalHandle = true;\n\n        final int remainingSpace = layoutState.getAvailable() - result.mConsumed + layoutState.getExtra();\n\n        int left, top, right, bottom;\n        if (helper.getOrientation() == VERTICAL) {\n            // not support RTL now\n            if (helper.isDoLayoutRTL()) {\n                right = helper.getContentWidth() - helper.getPaddingRight() - mMarginRight;\n                left = right - orientationHelper.getDecoratedMeasurementInOther(view);\n            } else {\n                left = helper.getPaddingLeft() + mMarginLeft;\n                right = left + orientationHelper.getDecoratedMeasurementInOther(view);\n            }\n\n            // whether this layout pass is layout to start or to end\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                // fill start, from bottom to top\n                bottom = layoutState.getOffset() - mMarginBottom;\n                top = layoutState.getOffset() - result.mConsumed;\n            } else {\n                // fill end, from top to bottom\n                if (mStickyStart) {\n                    top = layoutState.getOffset() + mMarginTop;\n                    bottom = layoutState.getOffset() + result.mConsumed;\n                } else {\n                    bottom = orientationHelper.getEndAfterPadding() - mMarginBottom - mOffset - mAdjuster.bottom;\n                    top = bottom - result.mConsumed;\n                }\n            }\n\n\n            if (helper.getReverseLayout() || !mStickyStart) {\n                if ((remainingSpace < (mOffset + mAdjuster.bottom) && layoutState.getItemDirection() == LayoutStateWrapper.ITEM_DIRECTION_TAIL)\n                        || (bottom > mMarginBottom + mOffset + mAdjuster.bottom)) {\n                    mDoNormalHandle = false;\n                    mFixView = view;\n\n                    bottom = orientationHelper.getEndAfterPadding() - mMarginBottom - mOffset - mAdjuster.bottom;\n                    top = bottom - result.mConsumed;\n                }\n            } else {\n                // should not use 0\n                if ((remainingSpace < (mOffset + mAdjuster.top) && layoutState.getItemDirection() == LayoutStateWrapper.ITEM_DIRECTION_HEAD)\n                        || (top < mMarginTop + mOffset + mAdjuster.top)) {\n                    mDoNormalHandle = false;\n                    mFixView = view;\n                    top = orientationHelper.getStartAfterPadding() + mMarginTop + mOffset + mAdjuster.top;\n                    bottom = top + result.mConsumed;\n                } else {\n                    if (VirtualLayoutManager.sDebuggable) {\n                        Log.i(\"Sticky\", \"remainingSpace: \" + remainingSpace + \"    offset: \" + mOffset);\n                    }\n                }\n            }\n\n        } else {\n            top = helper.getPaddingTop();\n            bottom = top + orientationHelper.getDecoratedMeasurementInOther(view) + mMarginTop;\n\n            if (layoutState.getLayoutDirection() == LayoutStateWrapper.LAYOUT_START) {\n                right = layoutState.getOffset() - mMarginRight;\n                left = layoutState.getOffset() - result.mConsumed;\n            } else {\n                left = layoutState.getOffset() + mMarginLeft;\n                right = layoutState.getOffset() + result.mConsumed;\n            }\n            if (helper.getReverseLayout() || !mStickyStart) {\n                if (remainingSpace < mOffset + mAdjuster.right) {\n                    mDoNormalHandle = false;\n                    mFixView = view;\n\n                    right = orientationHelper.getEndAfterPadding() - mOffset - mAdjuster.right;\n                    left = right - result.mConsumed;\n                }\n            } else {\n                if (remainingSpace < mOffset + mAdjuster.left) {\n                    mDoNormalHandle = false;\n                    mFixView = view;\n                    left = orientationHelper.getStartAfterPadding() + mOffset + mAdjuster.left;\n                    right = result.mConsumed;\n                }\n            }\n\n        }\n\n\n        layoutChildWithMargin(view, left, top, right, bottom, helper);\n\n        result.mConsumed += (layoutInVertical ? getVerticalMargin() : getHorizontalMargin());\n\n        if (state.isPreLayout()) {\n            mDoNormalHandle = true;\n        }\n\n        if (mDoNormalHandle) {\n            helper.addChildView(layoutState, view);\n            handleStateOnResult(result, view);\n        } else {\n            // result.mConsumed += mOffset;\n        }\n    }\n\n\n    @Override\n    public void beforeLayout(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutManagerHelper helper) {\n        super.beforeLayout(recycler, state, helper);\n\n\n        if (mFixView != null && helper.isViewHolderUpdated(mFixView)) {\n            // recycle view for later usage\n            helper.removeChildView(mFixView);\n            recycler.recycleView(mFixView);\n            mFixView = null;\n        }\n\n        mDoNormalHandle = false;\n    }\n\n\n    @Override\n    public boolean requireLayoutView() {\n        return false;\n    }\n\n    @Override\n    public void afterLayout(RecyclerView.Recycler recycler, RecyclerView.State state, int startPosition, int endPosition, int scrolled,\n                            LayoutManagerHelper helper) {\n        super.afterLayout(recycler, state, startPosition, endPosition, scrolled, helper);\n\n        // disabled if mPos is negative number\n        if (mPos < 0) {\n            return;\n        }\n\n        final OrientationHelperEx orientationHelper = helper.getMainOrientationHelper();\n\n        // not normal flow,\n        if (!mDoNormalHandle && mPos >= startPosition && mPos <= endPosition) {\n            fixLayoutStateFromAbnormal2Normal(orientationHelper, recycler, startPosition, endPosition, helper);\n        }\n\n        if (mDoNormalHandle || state.isPreLayout()) {\n            if (!state.isPreLayout()) {\n                // TODO: sticky only support one item now\n\n            }\n            if (mFixView != null) {\n                helper.removeChildView(mFixView);\n            } else {\n                // mDoNormalHandle == true && mFixView == null\n                return;\n            }\n        }\n\n        View stickyView = mFixView;\n        // Not in normal flow\n        if (!mDoNormalHandle && mFixView != null) {\n            // already capture in layoutViews phase\n            // if it's not shown on screen\n            if (mFixView.getParent() == null) {\n                helper.addFixedView(mFixView);\n            } else {\n                fixLayoutStateInCase1(orientationHelper, recycler, startPosition, endPosition, helper);\n            }\n        } else {\n            fixLayoutStateInCase2(orientationHelper, recycler, startPosition, endPosition, helper);\n        }\n\n        if (stickyListener != null) {\n            if (isLastStatusSticking && !isStickyNow()) {\n                stickyListener.onUnSticky(mPos, stickyView);\n                isLastStatusSticking = false;\n            } else if (!isLastStatusSticking && isStickyNow()) {\n                stickyListener.onSticky(mPos, mFixView);\n                isLastStatusSticking = true;\n            }\n        }\n    }\n\n    private void fixLayoutStateFromAbnormal2Normal(OrientationHelperEx orientationHelper, RecyclerView.Recycler recycler, int startPosition, int endPosition,\n                                                   LayoutManagerHelper helper) {\n        //fix status, from abnormal to normal\n        if (VirtualLayoutManager.sDebuggable) {\n            Log.i(TAG, \"abnormal pos: \" + mPos + \" start: \" + startPosition + \" end: \" + endPosition);\n        }\n\n        if (mFixView != null) {\n            int top, bottom;\n            View refer = null;\n            if (mStickyStart) {\n                for (int i = helper.getChildCount() - 1; i >= 0; i--) {\n                    refer = helper.getChildAt(i);\n                    int anchorPos = helper.getPosition(refer);\n                    if (anchorPos < mPos) { // TODO: when view size is larger than totalSpace!\n                        top = orientationHelper.getDecoratedEnd(refer);\n                        LayoutHelper layoutHelper = helper.findLayoutHelperByPosition(anchorPos);\n                        if (layoutHelper instanceof RangeGridLayoutHelper) {\n                            top = top + ((RangeGridLayoutHelper) layoutHelper).getBorderEndSpace(helper);\n                        } else if (layoutHelper instanceof MarginLayoutHelper) {\n                            top = top + ((MarginLayoutHelper) layoutHelper).getMarginBottom() + ((MarginLayoutHelper) layoutHelper).getPaddingBottom();\n                        }\n                        if (top >= mOffset + mAdjuster.top) {\n                            mDoNormalHandle = true;\n                        }\n\n                        break;\n                    }\n                }\n\n\n            } else {\n                for (int i = 0; i < helper.getChildCount(); i++) {\n                    refer = helper.getChildAt(i);\n                    int anchorPos = helper.getPosition(refer);\n                    if (anchorPos > mPos) {\n                        bottom = orientationHelper.getDecoratedStart(refer);\n                        LayoutHelper layoutHelper = helper.findLayoutHelperByPosition(anchorPos);\n                        if (layoutHelper instanceof RangeGridLayoutHelper) {\n                            bottom = bottom - ((RangeGridLayoutHelper) layoutHelper).getBorderStartSpace(helper);\n                        } else if (layoutHelper instanceof MarginLayoutHelper) {\n                            bottom = bottom - ((MarginLayoutHelper) layoutHelper).getMarginTop() - ((MarginLayoutHelper) layoutHelper).getPaddingTop();\n                        }\n                        if (bottom >= mOffset + mAdjuster.bottom) {\n                            mDoNormalHandle = true;\n                        }\n                        break;\n                    }\n                }\n            }\n        }\n    }\n\n    private void fixLayoutStateInCase1(OrientationHelperEx orientationHelper, RecyclerView.Recycler recycler, int startPosition, int endPosition,\n                                       LayoutManagerHelper helper) {\n        // considering the case when last layoutHelper has margin bottom\n        // 1. normal flow to abnormal flow; 2. abnormal flow to normal flow\n        if ((mStickyStart && endPosition >= mPos) || (!mStickyStart && startPosition <= mPos)) {\n            int consumed = orientationHelper.getDecoratedMeasurement(mFixView);\n            boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n            int extraTopOffset = getExtraTopOffset(helper);\n            final int startAdjust = layoutInVertical ? mAdjuster.top + extraTopOffset : mAdjuster.left;\n            final int endAdjust = layoutInVertical ? mAdjuster.bottom : mAdjuster.right;\n\n            int left = 0, top = 0, right = 0, bottom = 0;\n            int index = -1;\n            if (layoutInVertical) {\n                // not support RTL now\n                if (helper.isDoLayoutRTL()) {\n                    right = helper.getContentWidth() - helper.getPaddingRight();\n                    left = right - orientationHelper.getDecoratedMeasurementInOther(mFixView);\n                } else {\n                    left = helper.getPaddingLeft();\n                    right = left + orientationHelper.getDecoratedMeasurementInOther(mFixView);\n                }\n\n                View refer = null;\n                if (mStickyStart) {\n                    for (int i = helper.getChildCount() - 1; i >= 0; i--) {\n                        refer = helper.getChildAt(i);\n                        int anchorPos = helper.getPosition(refer);\n                        if (anchorPos < mPos) {\n                            top = orientationHelper.getDecoratedEnd(refer);\n                            LayoutHelper layoutHelper = helper.findLayoutHelperByPosition(anchorPos);\n                            if (layoutHelper instanceof RangeGridLayoutHelper) {\n                                top = top + ((RangeGridLayoutHelper) layoutHelper).getBorderEndSpace(helper);\n                            } else if (layoutHelper instanceof MarginLayoutHelper) {\n                                top = top + ((MarginLayoutHelper) layoutHelper).getMarginBottom() + ((MarginLayoutHelper) layoutHelper).getPaddingBottom();\n                            }\n                            bottom = top + consumed;\n                            index = i;\n                            mDoNormalHandle = true;\n                            break;\n                        }\n                    }\n\n                } else {\n                    for (int i = 0; i < helper.getChildCount(); i++) {\n                        refer = helper.getChildAt(i);\n                        int anchorPos = helper.getPosition(refer);\n                        if (anchorPos > mPos) { // TODO: when view size is larger than totalSpace!\n                            bottom = orientationHelper.getDecoratedStart(refer);\n                            LayoutHelper layoutHelper = helper.findLayoutHelperByPosition(anchorPos);\n                            if (layoutHelper instanceof RangeGridLayoutHelper) {\n                                bottom = bottom - ((RangeGridLayoutHelper) layoutHelper).getBorderStartSpace(helper);\n                            } else if (layoutHelper instanceof MarginLayoutHelper) {\n                                bottom = bottom - ((MarginLayoutHelper) layoutHelper).getMarginTop() - ((MarginLayoutHelper) layoutHelper).getPaddingTop();\n                            }\n                            top = bottom - consumed;\n                            index = i + 1;\n                            mDoNormalHandle = true;\n                            break;\n                        }\n                    }\n                }\n\n                if (refer == null || index < 0) {\n                    // can not find normal view for insert\n                    mDoNormalHandle = false;\n                }\n\n                if (helper.getReverseLayout() || !mStickyStart) {\n                    if (bottom > orientationHelper.getEndAfterPadding() - mOffset - endAdjust) {\n                        mDoNormalHandle = false;\n                    }\n                } else {\n                    if (top < orientationHelper.getStartAfterPadding() + mOffset + startAdjust) {\n                        mDoNormalHandle = false;\n                    }\n                }\n\n                if (!mDoNormalHandle) {\n                    if (helper.getReverseLayout() || !mStickyStart) {\n                        bottom = orientationHelper.getEndAfterPadding() - mOffset - endAdjust;\n                        top = bottom - consumed;\n                    } else {\n                        top = orientationHelper.getStartAfterPadding() + mOffset + startAdjust;\n                        bottom = top + consumed;\n                    }\n                }\n\n            } else {\n                top = helper.getPaddingTop();\n                bottom = top + orientationHelper.getDecoratedMeasurementInOther(mFixView);\n\n                if (mDoNormalHandle) {\n                    View refer = null;\n                    if (mStickyStart) {\n                        for (int i = helper.getChildCount() - 1; i >= 0; i--) {\n                            refer = helper.getChildAt(i);\n                            int anchorPos = helper.getPosition(refer);\n                            if (anchorPos < mPos) { // TODO: when view size is larger than totalSpace!\n                                left = orientationHelper.getDecoratedEnd(refer);\n                                right = left + consumed;\n                                break;\n                            }\n                        }\n                    } else {\n                        for (int i = 0; i < helper.getChildCount(); i++) {\n                            refer = helper.getChildAt(i);\n                            int anchorPos = helper.getPosition(refer);\n                            if (anchorPos > mPos) {\n                                right = orientationHelper.getDecoratedStart(refer);\n                                left = right - consumed;\n                                break;\n                            }\n                        }\n                    }\n                } else if (helper.getReverseLayout() || !mStickyStart) {\n                    right = orientationHelper.getEndAfterPadding() - mOffset - endAdjust;\n                    left = right - consumed;\n                } else {\n                    left = orientationHelper.getStartAfterPadding() + mOffset + startAdjust;\n                    right = left + consumed;\n                }\n\n            }\n\n            layoutChildWithMargin(mFixView, left, top, right, bottom, helper);\n\n            if (mDoNormalHandle) {\n                // offset\n                if (index >= 0) {\n                    if (mFixView.getParent() == null) {\n                        helper.addChildView(mFixView, index);\n                    }\n                    mFixView = null;\n                }\n            } else {\n                helper.showView(mFixView);\n                helper.addFixedView(mFixView);\n            }\n        } else {\n            helper.removeChildView(mFixView);\n            helper.recycleView(mFixView);\n            mFixView = null;\n        }\n    }\n\n    private void fixLayoutStateInCase2(OrientationHelperEx orientationHelper, RecyclerView.Recycler recycler, int startPosition, int endPosition,\n                                       LayoutManagerHelper helper) {\n        // 1. normal flow to abnormal flow; 2. abnormal flow to normal flow\n        // (mDoNormalHandle && mFixView != null) || (!mDoNormalHandle && mFixView == null)\n        View eView = mFixView;\n        if (eView == null) {\n            // !mDoNormalHandle && mFixView == null, find existing view\n            eView = helper.findViewByPosition(mPos);\n        }\n\n        boolean normalHandle = false;\n        boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n        int extraTopOffset = getExtraTopOffset(helper);\n        final int startAdjust = layoutInVertical ? mAdjuster.top + extraTopOffset : mAdjuster.left;\n        final int endAdjust = layoutInVertical ? mAdjuster.bottom : mAdjuster.right;\n        if ((mStickyStart && endPosition >= mPos) || (!mStickyStart && startPosition <= mPos)) {\n\n            if (eView == null) {\n                // TODO? why do condition here?\n                if (mOffset + (mStickyStart ? startAdjust : endAdjust) >= 0) {\n                    normalHandle = true;\n                }\n                mFixView = recycler.getViewForPosition(mPos);\n                doMeasure(mFixView, helper);\n            } else if (mStickyStart && orientationHelper.getDecoratedStart(eView) >= orientationHelper.getStartAfterPadding() + mOffset + startAdjust) {\n                // normal\n                normalHandle = true;\n                mFixView = eView;\n            } else if (!mStickyStart && orientationHelper.getDecoratedEnd(eView) <= orientationHelper.getEndAfterPadding() - mOffset - endAdjust) {\n                // normal\n                normalHandle = true;\n                mFixView = eView;\n            } else {\n                // abnormal\n                // TODO: reuse views\n                // mFixView = recycler.getViewForPosition(mPos);\n                mFixView = eView;\n            }\n        }\n\n\n        if (mFixView != null) {\n            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) mFixView.getLayoutParams();\n\n            if (params.isItemRemoved()) {\n                // item is removed\n                return;\n            }\n\n            // when do measure in after layout no need to consider scrolled\n            // doMeasure(mFixView, helper);\n\n            // do layout\n\n            int consumed = orientationHelper.getDecoratedMeasurement(mFixView);\n\n\n            int left = 0, top = 0, right = 0, bottom = 0;\n            int index = -1;\n            if (layoutInVertical) {\n                // not support RTL now\n                if (helper.isDoLayoutRTL()) {\n                    right = helper.getContentWidth() - helper.getPaddingRight();\n                    left = right - orientationHelper.getDecoratedMeasurementInOther(mFixView);\n                } else {\n                    left = helper.getPaddingLeft();\n                    right = left + orientationHelper.getDecoratedMeasurementInOther(mFixView);\n                }\n\n                if (normalHandle) {\n                    View refer = null;\n                    if (mStickyStart) {\n                        for (int i = helper.getChildCount() - 1; i >= 0; i--) {\n                            refer = helper.getChildAt(i);\n                            int anchorPos = helper.getPosition(refer);\n                            if (anchorPos < mPos) { // TODO: when view size is larger than totalSpace!\n                                top = orientationHelper.getDecoratedEnd(refer);\n                                LayoutHelper layoutHelper = helper.findLayoutHelperByPosition(anchorPos);\n                                if (layoutHelper instanceof RangeGridLayoutHelper) {\n                                    top = top + ((RangeGridLayoutHelper) layoutHelper).getBorderEndSpace(helper);\n                                } else if (layoutHelper instanceof MarginLayoutHelper) {\n                                    top = top + ((MarginLayoutHelper) layoutHelper).getMarginBottom() + ((MarginLayoutHelper) layoutHelper).getPaddingBottom();\n                                }\n                                bottom = top + consumed;\n                                index = i + 1;\n                                break;\n                            }\n                        }\n                    } else {\n                        for (int i = 0; i < helper.getChildCount(); i++) {\n                            refer = helper.getChildAt(i);\n                            int anchorPos = helper.getPosition(refer);\n                            if (anchorPos > mPos) {\n                                bottom = orientationHelper.getDecoratedStart(refer);\n                                LayoutHelper layoutHelper = helper.findLayoutHelperByPosition(anchorPos);\n                                if (layoutHelper instanceof RangeGridLayoutHelper) {\n                                    bottom = bottom - ((RangeGridLayoutHelper) layoutHelper).getBorderStartSpace(helper);\n                                } else if (layoutHelper instanceof MarginLayoutHelper) {\n                                    bottom = bottom - ((MarginLayoutHelper) layoutHelper).getMarginTop() - ((MarginLayoutHelper) layoutHelper).getPaddingTop();\n                                }\n                                top = bottom - consumed;\n                                index = i;\n                                break;\n                            }\n                        }\n                    }\n\n                    if (refer == null || index < 0) {\n                        // can not find normal view for insert\n                        normalHandle = false;\n                    }\n\n                    if (helper.getReverseLayout() || !mStickyStart) {\n                        if (bottom > orientationHelper.getEndAfterPadding() - mOffset - endAdjust) {\n                            normalHandle = false;\n                        }\n                    } else {\n                        if (top < orientationHelper.getStartAfterPadding() + mOffset + startAdjust) {\n                            normalHandle = false;\n                        }\n                    }\n\n                }\n\n                if (!normalHandle) {\n                    if (helper.getReverseLayout() || !mStickyStart) {\n                        bottom = orientationHelper.getEndAfterPadding() - mOffset - endAdjust;\n                        top = bottom - consumed;\n                    } else {\n                        top = orientationHelper.getStartAfterPadding() + mOffset + startAdjust;\n                        bottom = top + consumed;\n                    }\n                }\n\n            } else {\n                top = helper.getPaddingTop();\n                bottom = top + orientationHelper.getDecoratedMeasurementInOther(mFixView);\n\n                if (normalHandle) {\n                    View refer = null;\n                    if (mStickyStart) {\n                        for (int i = helper.getChildCount() - 1; i >= 0; i--) {\n                            refer = helper.getChildAt(i);\n                            int anchorPos = helper.getPosition(refer);\n                            if (anchorPos < mPos) { // TODO: when view size is larger than totalSpace!\n                                left = orientationHelper.getDecoratedEnd(refer);\n                                right = left + consumed;\n                                break;\n                            }\n                        }\n                    } else {\n                        for (int i = 0; i < helper.getChildCount(); i++) {\n                            refer = helper.getChildAt(i);\n                            int anchorPos = helper.getPosition(refer);\n                            if (anchorPos > mPos) {\n                                right = orientationHelper.getDecoratedStart(refer);\n                                left = right - consumed;\n                                break;\n                            }\n                        }\n                    }\n                } else if (helper.getReverseLayout() || !mStickyStart) {\n                    right = orientationHelper.getEndAfterPadding() - mOffset - endAdjust;\n                    left = right - consumed;\n                } else {\n                    left = orientationHelper.getStartAfterPadding() + mOffset + startAdjust;\n                    right = left + consumed;\n                }\n\n            }\n\n            layoutChildWithMargin(mFixView, left, top, right, bottom, helper);\n\n            if (normalHandle) {\n                // offset\n                if (index >= 0) {\n                    if (mFixView.getParent() == null) {\n                        helper.addChildView(mFixView, index);\n                    }\n                    mFixView = null;\n                }\n            } else {\n                helper.addFixedView(mFixView);\n            }\n\n        }\n        mDoNormalHandle = normalHandle;\n    }\n\n\n    @Nullable\n    @Override\n    public View getFixedView() {\n        return mFixView;\n    }\n\n    @Override\n    public void onClear(LayoutManagerHelper helper) {\n        super.onClear(helper);\n        if (mFixView != null) {\n            helper.recycleView(mFixView);\n            helper.removeChildView(mFixView);\n            mFixView = null;\n        }\n    }\n\n    private void doMeasure(View view, LayoutManagerHelper helper) {\n        final VirtualLayoutManager.LayoutParams params = (VirtualLayoutManager.LayoutParams) view.getLayoutParams();\n        final boolean layoutInVertical = helper.getOrientation() == VERTICAL;\n\n        int widthSize = helper.getContentWidth() - helper.getPaddingLeft() - helper.getPaddingRight() - getHorizontalMargin();\n        int heightSize = helper.getContentHeight() - helper.getPaddingTop() - helper.getPaddingBottom() - getVerticalMargin();\n        float viewAspectRatio = params.mAspectRatio;\n\n        if (layoutInVertical) {\n            final int widthSpec = helper.getChildMeasureSpec(widthSize, params.width, false);\n            int heightSpec;\n            if (!Float.isNaN(viewAspectRatio) && viewAspectRatio > 0) {\n                heightSpec = View.MeasureSpec.makeMeasureSpec((int) (widthSize / viewAspectRatio + 0.5f), View.MeasureSpec.EXACTLY);\n            } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n                heightSpec = View.MeasureSpec.makeMeasureSpec((int) (widthSize / mAspectRatio + 0.5), View.MeasureSpec.EXACTLY);\n            } else {\n                heightSpec = helper.getChildMeasureSpec(heightSize, params.height, true);\n            }\n\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        } else {\n            final int heightSpec = helper.getChildMeasureSpec(heightSize, params.height, false);\n            int widthSpec;\n            if (!Float.isNaN(viewAspectRatio) && viewAspectRatio > 0) {\n                widthSpec = View.MeasureSpec.makeMeasureSpec((int) (heightSize * viewAspectRatio + 0.5), View.MeasureSpec.EXACTLY);\n            } else if (!Float.isNaN(mAspectRatio) && mAspectRatio > 0) {\n                widthSpec = View.MeasureSpec.makeMeasureSpec((int) (heightSize * mAspectRatio + 0.5), View.MeasureSpec.EXACTLY);\n            } else {\n                widthSpec = helper.getChildMeasureSpec(widthSize, params.width, true);\n            }\n\n            helper.measureChildWithMargins(view, widthSpec, heightSpec);\n        }\n\n    }\n\n    public void setStickyListener(StickyListener stickyListener) {\n        this.stickyListener = stickyListener;\n    }\n\n    public void setStackable(Stackable stackable) {\n        mStackable = stackable;\n    }\n\n    /**\n     *\n     * get extra top offset when there are multi-sticky views at the top of current view\n     * in order to avoid views overlap\n     * the offset value is equal to total sticky views' height.\n     * @param helper\n     */\n    private int getExtraTopOffset(LayoutManagerHelper helper) {\n        if (mStackable == null || !mStackable.enable()) {\n            return 0;\n        }\n\n        int offset = 0;\n        if (helper instanceof VirtualLayoutManager){\n            List<LayoutHelper> helperList = ((VirtualLayoutManager) helper).getLayoutHelpers();\n            for (LayoutHelper helperItem : helperList) {\n                if (helperItem.isFixLayout()) {\n                    if (helperItem.getRange().getUpper() < this.getRange().getLower()) {\n                        View view = helperItem.getFixedView();\n                        if (view != null) {\n                            offset += view.getHeight();\n                        }\n                    }\n                }\n            }\n\n        }\n\n        return offset;\n    }\n}\n\n"
  },
  {
    "path": "vlayout/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"tag_layout_helper_bg\" type=\"id\" />\n</resources>"
  }
]