├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_adaptive_back.png │ │ │ │ └── ic_launcher_adaptive_fore.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_adaptive_back.png │ │ │ │ └── ic_launcher_adaptive_fore.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── flap_xcz.jpg │ │ │ │ ├── flap_ic_bl.png │ │ │ │ ├── flap_ic_ft.png │ │ │ │ ├── flap_ic_hlg.png │ │ │ │ ├── flap_ic_jd.png │ │ │ │ ├── flap_ic_lm.png │ │ │ │ ├── flap_ic_lx.png │ │ │ │ ├── flap_ic_ng.png │ │ │ │ ├── flap_ic_pg.png │ │ │ │ ├── flap_ic_qj.png │ │ │ │ ├── flap_ic_slb.png │ │ │ │ ├── flap_ic_xg.png │ │ │ │ ├── flap_ic_zs.png │ │ │ │ ├── flap_ic_baicai.png │ │ │ │ ├── flap_ic_baozi.png │ │ │ │ ├── flap_ic_orange.png │ │ │ │ ├── flap_ic_taozi.png │ │ │ │ └── ic_upward_black.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_adaptive_back.png │ │ │ │ └── ic_launcher_adaptive_fore.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_adaptive_back.png │ │ │ │ └── ic_launcher_adaptive_fore.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_adaptive_back.png │ │ │ │ └── ic_launcher_adaptive_fore.png │ │ │ ├── menu │ │ │ │ ├── multitype.xml │ │ │ │ ├── item_decorations.xml │ │ │ │ ├── home.xml │ │ │ │ ├── viewpager2.xml │ │ │ │ ├── pool.xml │ │ │ │ ├── preload.xml │ │ │ │ ├── flap.xml │ │ │ │ ├── animation.xml │ │ │ │ └── activity_main_drawer.xml │ │ │ ├── drawable │ │ │ │ ├── linear_item_decoration.xml │ │ │ │ └── side_nav_bar.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ └── ic_launcher.xml │ │ │ ├── layout │ │ │ │ ├── component_zero_height.xml │ │ │ │ ├── footer_layout.xml │ │ │ │ ├── header_layout.xml │ │ │ │ ├── component_banner_image.xml │ │ │ │ ├── component_simple_image.xml │ │ │ │ ├── component_test_click.xml │ │ │ │ ├── fragment_view_pager2.xml │ │ │ │ ├── header_layout_image.xml │ │ │ │ ├── activity_prefetch.xml │ │ │ │ ├── component_banner.xml │ │ │ │ ├── flap_item_generic_type.xml │ │ │ │ ├── flap_item_vb.xml │ │ │ │ ├── flap_component_simple_text.xml │ │ │ │ ├── flap_item_custom_type.xml │ │ │ │ ├── content_main2.xml │ │ │ │ ├── flap_item_simple_databinding.xml │ │ │ │ ├── skeleton_layout2.xml │ │ │ │ ├── activity_main2.xml │ │ │ │ ├── fragment_case_flap_rv.xml │ │ │ │ ├── component_full_feature.xml │ │ │ │ ├── fragment_base_case_flap_rv.xml │ │ │ │ ├── app_bar_main.xml │ │ │ │ ├── fragment_base_case.xml │ │ │ │ ├── nav_header_main.xml │ │ │ │ ├── skeleton_layout3.xml │ │ │ │ ├── skeleton_layout.xml │ │ │ │ ├── component_diff.xml │ │ │ │ └── component_test_all_feature.xml │ │ │ ├── values │ │ │ │ ├── themes.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── drawables.xml │ │ │ │ └── strings.xml │ │ │ ├── drawable-v21 │ │ │ │ ├── ic_menu_slideshow.xml │ │ │ │ ├── ic_menu_gallery.xml │ │ │ │ └── ic_menu_camera.xml │ │ │ ├── values-v19 │ │ │ │ └── styles.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ │ └── me │ │ │ │ └── yifeiyuan │ │ │ │ └── flapdev │ │ │ │ ├── IMenuView.kt │ │ │ │ ├── MainViewModel.kt │ │ │ │ ├── Scrollable.kt │ │ │ │ ├── Ext.kt │ │ │ │ ├── Logger.kt │ │ │ │ ├── components │ │ │ │ ├── ZeroHeightComponent.kt │ │ │ │ ├── TestClickComponent.kt │ │ │ │ ├── DataBindingComponent.kt │ │ │ │ ├── CustomViewTypeComponent.kt │ │ │ │ ├── SimpleImageComponent.kt │ │ │ │ ├── ViewBindingComponent.kt │ │ │ │ ├── Banner.kt │ │ │ │ ├── SimpleTextComponent.kt │ │ │ │ ├── TestConfig.kt │ │ │ │ ├── TestAdapterApiComponent.kt │ │ │ │ └── DiffComponent.kt │ │ │ │ ├── Services.kt │ │ │ │ ├── StickyHeaders.java │ │ │ │ ├── MyLayoutManager.kt │ │ │ │ ├── GitHubDemoFragment.kt │ │ │ │ ├── testcases │ │ │ │ ├── FlapDifferAdapterTestcase.kt │ │ │ │ ├── PreloadTestcase.kt │ │ │ │ └── ComponentPoolTestcase.kt │ │ │ │ └── SingleScrollDirection.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flapdev │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flapdev │ │ └── ExampleInstrumentedTest.java └── proguard-rules.pro ├── flap-paging ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── paging │ │ │ └── FlapPagingDataAdapter.kt │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── paging │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flap │ │ └── paging │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── flap ├── .gitignore ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ ├── ids.xml │ │ │ │ ├── attrs.xml │ │ │ │ └── shimmer_attrs.xml │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ ├── FlapRuntime.kt │ │ │ ├── service │ │ │ ├── AdapterService.kt │ │ │ ├── AdapterServiceManager.kt │ │ │ └── IAdapterServiceManager.kt │ │ │ ├── widget │ │ │ ├── FlapStickyHeaders.kt │ │ │ ├── ParallaxHeaderEffect.kt │ │ │ ├── FlapGridLayoutManager.kt │ │ │ ├── FlapLinearLayoutManager.kt │ │ │ ├── FlapStaggeredGridLayoutManager.kt │ │ │ └── IndexedLayoutState.java │ │ │ ├── hook │ │ │ ├── AdapterHookManager.kt │ │ │ ├── IAdapterHookManager.kt │ │ │ ├── AdapterHook.kt │ │ │ └── ApmHook.kt │ │ │ ├── delegate │ │ │ ├── AdapterDelegateManager.kt │ │ │ ├── IAdapterDelegateManager.kt │ │ │ ├── FallbackAdapterDelegate.kt │ │ │ └── AdapterDelegate.kt │ │ │ ├── event │ │ │ ├── EventObserver.kt │ │ │ └── Event.kt │ │ │ ├── ext │ │ │ ├── ExtraParamsProvider.kt │ │ │ ├── OnAdapterDataChangedObserver.kt │ │ │ ├── ItemClicksHelper.kt │ │ │ ├── ComponentBinder.kt │ │ │ └── EmptyViewHelper.kt │ │ │ ├── FlapRegistry.kt │ │ │ ├── ViewTypeGenerator.kt │ │ │ ├── decoration │ │ │ ├── SizedColorDrawable.kt │ │ │ └── LinearSpaceItemDecoration.kt │ │ │ ├── FlapDebug.kt │ │ │ ├── ComponentConfig.kt │ │ │ ├── differ │ │ │ └── IDiffer.kt │ │ │ ├── FlapAdapterDelegation.kt │ │ │ ├── FlapInitializer.kt │ │ │ ├── skeleton │ │ │ └── SkeletonAdapter.kt │ │ │ └── pool │ │ │ └── ComponentPool.kt │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flap │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── flap-animation ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── animation │ │ │ ├── AlphaInAdapterAnimation.kt │ │ │ ├── SlideInRightAdapterAnimation.kt │ │ │ ├── SlideInLeftAdapterAnimation.kt │ │ │ ├── SlideInBottomAdapterAnimation.kt │ │ │ ├── ScaleInAdapterAnimation.kt │ │ │ └── BaseAdapterAnimation.kt │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── animation │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flap │ │ └── animation │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── flap-annotations ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── me │ └── yifeiyuan │ └── flap │ └── annotations │ └── Delegate.java ├── flap-compiler ├── .gitignore └── build.gradle ├── flap-dsl-databinding ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── dsl │ │ │ └── databinding │ │ │ └── AdapterDelegateDataBindingDsl.kt │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── dsl │ │ │ └── databinding │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flap │ │ └── dsl │ │ └── databinding │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── flap-dsl-viewbinding ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── dsl │ │ │ └── viewbinding │ │ │ └── AdapterDelegateViewBindingDsl.kt │ ├── test │ │ └── java │ │ │ └── me │ │ │ └── yifeiyuan │ │ │ └── flap │ │ │ └── dsl │ │ │ └── viewbinding │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flap │ │ └── dsl │ │ └── viewbinding │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── flap-gradle-plugin ├── .gitignore ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── gradle-plugins │ │ │ └── me.yifeiyuan.flap.plugin.properties │ │ └── java │ │ └── me │ │ └── yifeiyuan │ │ └── flap │ │ └── plugin │ │ ├── FlapPlugin.java │ │ └── Log.java └── build.gradle ├── PULL_REQUEST_TEMPLATE.md ├── assets ├── flap-simple-showcase.png └── flap_dingding_group.JPG ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── config.gradle ├── .gitignore ├── settings.gradle ├── .travis.yml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── CONTRIBUTING.md ├── gradle.properties ├── gradlew.bat └── README_EN.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /flap-paging/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /flap/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /flap-animation/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /flap-animation/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flap-paging/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flap-annotations/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /flap-compiler/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /flap-dsl-databinding/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /flap-dsl-viewbinding/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /flap-gradle-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /flap-dsl-databinding/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /flap-dsl-viewbinding/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 这个 PR 是做什么的?(What's the PR for? ) 2 | -------------------------------------------------------------------------------- /flap/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/flap-simple-showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/assets/flap-simple-showcase.png -------------------------------------------------------------------------------- /assets/flap_dingding_group.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/assets/flap_dingding_group.JPG -------------------------------------------------------------------------------- /flap/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | flap 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | 3 | flapMinSdkVersion = 17 4 | flapTargetSdkVersion = 29 5 | flapCompileSdkVersion = 29 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_xcz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_xcz.jpg -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_bl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_bl.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_ft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_ft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_hlg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_hlg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_jd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_jd.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_lm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_lm.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_lx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_lx.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_ng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_ng.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_pg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_pg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_qj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_qj.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_slb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_slb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_xg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_xg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_zs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_zs.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_baicai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_baicai.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_baozi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_baozi.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_orange.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/flap_ic_taozi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/flap_ic_taozi.png -------------------------------------------------------------------------------- /flap-gradle-plugin/src/main/resources/META-INF/gradle-plugins/me.yifeiyuan.flap.plugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=me.yifeiyuan.flap.plugin.FlapPlugin -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_upward_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/drawable-xxhdpi/ic_upward_black.png -------------------------------------------------------------------------------- /app/src/main/res/menu/multitype.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/menu/item_decorations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /flap-dsl-databinding/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /flap-dsl-viewbinding/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/FlapRuntime.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2022/12/4. 5 | */ 6 | 7 | interface FlapRuntime -------------------------------------------------------------------------------- /flap/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanCheen/Flap/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/IMenuView.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2022/10/11. 5 | */ 6 | interface IMenuView { 7 | fun showMenu() 8 | } -------------------------------------------------------------------------------- /flap-paging/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /flap-animation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | /** 6 | * Created by 程序亦非猿 on 2022/1/27. 7 | */ 8 | class MainViewModel : ViewModel() { 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/Scrollable.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2022/3/8. 5 | */ 6 | interface Scrollable { 7 | 8 | fun scrollToTop() 9 | 10 | fun scrollToBottom() 11 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/service/AdapterService.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.service 2 | 3 | /** 4 | * 5 | * @see IAdapterServiceManager 6 | * 7 | * Created by 程序亦非猿 on 2022/8/16. 8 | * @since 3.0.3 9 | */ 10 | interface AdapterService -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea 9 | .DS_Store 10 | /build 11 | /captures 12 | .externalNativeBuild 13 | ./repos 14 | *.hprof -------------------------------------------------------------------------------- /app/src/main/res/menu/home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/linear_item_decoration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/widget/FlapStickyHeaders.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.widget 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2022/10/17. 5 | * 6 | * @since 3.1.8 7 | */ 8 | interface FlapStickyHeaders { 9 | fun isStickyHeader(position: Int): Boolean 10 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':flap-paging' 2 | include ':flap-animation' 3 | include ':flap-dsl-databinding' 4 | include ':flap-dsl-viewbinding' 5 | include ':app' 6 | include ':flap' 7 | //include ':flap-annotations' 8 | //include ':flap-compiler' 9 | //include ':flap-gradle-plugin' 10 | -------------------------------------------------------------------------------- /flap-annotations/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | 5 | group = 'me.yifeiyuan' 6 | 7 | dependencies { 8 | implementation fileTree(dir: 'libs', include: ['*.jar']) 9 | } 10 | 11 | sourceCompatibility = "7" 12 | targetCompatibility = "7" -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/hook/AdapterHookManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.hook 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2022/9/5. 5 | * 6 | * @since 3.1.0 7 | */ 8 | class AdapterHookManager : IAdapterHookManager { 9 | 10 | override val adapterHooks: MutableList = mutableListOf() 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/Ext.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | import android.content.Context 4 | 5 | /** 6 | * Created by 程序亦非猿 on 2022/8/19. 7 | */ 8 | 9 | fun Context.toPixel(dip: Int): Int { 10 | val scale: Float = resources.displayMetrics.scaledDensity 11 | return (dip * scale + 0.5).toInt() 12 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/delegate/AdapterDelegateManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.delegate 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2022/9/5. 5 | * 6 | * @since 3.1.0 7 | */ 8 | internal class AdapterDelegateManager : IAdapterDelegateManager { 9 | override val adapterDelegates: MutableList> = mutableListOf() 10 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/viewpager2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/component_zero_height.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/menu/pool.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/Logger.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | import android.util.Log 4 | 5 | /** 6 | * Created by 程序亦非猿 on 2022/7/17. 7 | */ 8 | object Logger { 9 | 10 | private const val TAG = "FLogger" 11 | 12 | fun d(tag: String, msg: String) { 13 | Log.d("$TAG-$tag", msg) 14 | } 15 | 16 | fun d(msg: String) { 17 | Log.d(TAG, msg) 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 8dp 6 | 176dp 7 | 16dp 8 | 12dp 9 | -------------------------------------------------------------------------------- /flap-paging/src/test/java/me/yifeiyuan/flap/paging/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.paging 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /flap-animation/src/test/java/me/yifeiyuan/flap/animation/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/preload.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | -------------------------------------------------------------------------------- /flap/src/test/java/me/yifeiyuan/flap/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/test/java/me/yifeiyuan/flapdev/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /flap-dsl-databinding/src/test/java/me/yifeiyuan/flap/dsl/databinding/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.dsl.databinding 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /flap-dsl-viewbinding/src/test/java/me/yifeiyuan/flap/dsl/viewbinding/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.dsl.viewbinding 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/event/EventObserver.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.event 2 | 3 | /** 4 | * 5 | * @see Event 6 | * 7 | * Created by 程序亦非猿 on 2021/11/1. 8 | * @since 3.0.0 9 | */ 10 | interface EventObserver { 11 | fun onEvent(event: Event<*>) 12 | } 13 | 14 | /** 15 | * 包装成函数调用 16 | */ 17 | internal class EventObserverWrapper(val block: (Event) -> Unit) : EventObserver { 18 | override fun onEvent(event: Event<*>) { 19 | block(event as Event) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/values-v19/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/ext/ExtraParamsProvider.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.ext 2 | 3 | /** 4 | * Created by 程序亦非猿 on 2021/11/1. 5 | * 6 | * @since 3.0.0 7 | */ 8 | interface ExtraParamsProvider { 9 | fun getParam(key: String): Any? 10 | } 11 | 12 | /** 13 | * @since 3.0.4 14 | */ 15 | internal class ExtraParamsProviderWrapper(private val block: (key: String) -> Any?) : ExtraParamsProvider { 16 | override fun getParam(key: String): Any? { 17 | return block(key) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/service/AdapterServiceManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.service 2 | 3 | /** 4 | * 5 | * 提供注册和发现 AdapterService 的能力 6 | * 7 | * @see me.yifeiyuan.flap.Flap.registerAdapterService 8 | * @see me.yifeiyuan.flap.Flap.getAdapterService 9 | * 10 | * Created by 程序亦非猿 on 2022/8/16. 11 | * @since 3.0.3 12 | */ 13 | internal class AdapterServiceManager : IAdapterServiceManager { 14 | 15 | override val adapterServices: MutableMap, AdapterService> = mutableMapOf() 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/footer_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/header_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/Services.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | import android.util.Log 4 | import me.yifeiyuan.flap.service.AdapterService 5 | 6 | /** 7 | * Created by 程序亦非猿 on 2022/8/16. 8 | */ 9 | 10 | private const val TAG = "Services" 11 | 12 | class LogService : AdapterService { 13 | 14 | fun log(message: String){ 15 | Log.d(TAG, "log() called with: message = $message") 16 | } 17 | 18 | fun testResult():String{ 19 | return "TestService.testResult is called!" 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/component_banner_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/component_simple_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/FlapRegistry.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | import androidx.annotation.RestrictTo 4 | import me.yifeiyuan.flap.delegate.IAdapterDelegateManager 5 | import me.yifeiyuan.flap.hook.IAdapterHookManager 6 | import me.yifeiyuan.flap.service.IAdapterServiceManager 7 | 8 | /** 9 | * 统一注册中心接口抽象 10 | * 11 | * Created by 程序亦非猿 on 2022/11/23. 12 | * @since 3.3.0 13 | */ 14 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 15 | interface FlapRegistry : IAdapterHookManager, IAdapterDelegateManager, IAdapterServiceManager { 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/component_test_click.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_view_pager2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /flap-gradle-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'maven-publish' 3 | 4 | apply plugin: 'com.github.dcendents.android-maven' 5 | 6 | group = 'me.yifeiyuan' 7 | 8 | dependencies { 9 | implementation fileTree(dir: 'libs', include: ['*.jar']) 10 | implementation gradleApi() 11 | implementation localGroovy() 12 | implementation 'com.android.tools.build:gradle:3.2.1' //transform api 需要 13 | 14 | implementation 'org.ow2.asm:asm-all:5.2' 15 | } 16 | 17 | sourceCompatibility = "1.8" 18 | targetCompatibility = "1.8" 19 | 20 | repositories { 21 | mavenCentral() 22 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | android: 7 | components: 8 | - platform-tools 9 | - tools 10 | - build-tools-28.0.3 11 | - android-28 12 | - extra-android-support 13 | - extra-google-m2repository 14 | - extra-android-m2repository 15 | 16 | licenses: 17 | - 'android-sdk-license-.+' 18 | 19 | before_install: 20 | - chmod +x gradlew 21 | - yes | sdkmanager "platforms;android-28" 22 | - yes | sdkmanager "platforms;android-27" 23 | 24 | install: 25 | - sdkmanager --list || true 26 | 27 | script: 28 | - ./gradlew build -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/TestClickComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import me.yifeiyuan.flap.dsl.adapterDelegate 4 | import me.yifeiyuan.flap.ext.bindTextView 5 | import me.yifeiyuan.flapdev.R 6 | 7 | /** 8 | * 9 | * Created by 程序亦非猿 on 2022/7/27. 10 | */ 11 | 12 | class TestClickModel(var content: String) 13 | 14 | fun createTestClickDelegate() = adapterDelegate(R.layout.component_test_click) { 15 | onBind { model, position, payloads -> 16 | bindTextView(R.id.clicks){ 17 | text = model.content 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/header_layout_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_prefetch.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /flap-compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | 5 | group = 'me.yifeiyuan' 6 | 7 | dependencies { 8 | implementation fileTree(dir: 'libs', include: ['*.jar']) 9 | implementation project(':flap-annotations') 10 | implementation 'com.squareup:javapoet:1.11.1' 11 | // implementation 'com.squareup:kotlinpoet:1.10.1' 12 | api 'com.google.auto.service:auto-service:1.0-rc7' 13 | annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7' 14 | implementation 'com.google.guava:guava:21.0' 15 | } 16 | 17 | sourceCompatibility = "7" 18 | targetCompatibility = "7" -------------------------------------------------------------------------------- /flap-animation/src/main/java/me/yifeiyuan/flap/animation/AlphaInAdapterAnimation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.ObjectAnimator 5 | import android.view.View 6 | import me.yifeiyuan.flap.Component 7 | 8 | /** 9 | * Created by 程序亦非猿 on 2022/8/29. 10 | * 11 | * @since 3.0.7 12 | */ 13 | class AlphaInAdapterAnimation(private var startAlpha: Float = 0f) : BaseAdapterAnimation() { 14 | 15 | override fun createAnimator(view: View, component: Component<*>, data: Any, position: Int): Animator { 16 | return ObjectAnimator.ofFloat(view, "alpha", startAlpha, 1f) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /flap-animation/src/main/java/me/yifeiyuan/flap/animation/SlideInRightAdapterAnimation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.ObjectAnimator 5 | import android.view.View 6 | import me.yifeiyuan.flap.Component 7 | 8 | /** 9 | * 从 right 滑入 10 | * Created by 程序亦非猿 on 2022/8/29. 11 | * @since 3.0.7 12 | */ 13 | class SlideInRightAdapterAnimation : BaseAdapterAnimation() { 14 | override fun createAnimator(view: View, component: Component<*>, data: Any, position: Int): Animator { 15 | return ObjectAnimator.ofFloat(view, "translationX", view.rootView.width.toFloat(), 0f) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /flap-animation/src/main/java/me/yifeiyuan/flap/animation/SlideInLeftAdapterAnimation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.ObjectAnimator 5 | import android.view.View 6 | import me.yifeiyuan.flap.Component 7 | 8 | /** 9 | * 从 left 滑入 10 | * Created by 程序亦非猿 on 2022/8/29. 11 | * 12 | * @since 3.0.7 13 | */ 14 | class SlideInLeftAdapterAnimation : BaseAdapterAnimation() { 15 | override fun createAnimator(view: View, component: Component<*>, data: Any, position: Int): Animator { 16 | return ObjectAnimator.ofFloat(view, "translationX", -view.rootView.width.toFloat(), 0f) 17 | } 18 | } -------------------------------------------------------------------------------- /flap-animation/src/main/java/me/yifeiyuan/flap/animation/SlideInBottomAdapterAnimation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.ObjectAnimator 5 | import android.view.View 6 | import me.yifeiyuan.flap.Component 7 | 8 | /** 9 | * 从 bottom 滑入 10 | * Created by 程序亦非猿 on 2022/8/29. 11 | * 12 | * @since 3.0.7 13 | */ 14 | class SlideInBottomAdapterAnimation : BaseAdapterAnimation() { 15 | override fun createAnimator(view: View, component: Component<*>, data: Any, position: Int): Animator { 16 | return ObjectAnimator.ofFloat(view, "translationY", view.measuredHeight.toFloat(), 0f) 17 | } 18 | } -------------------------------------------------------------------------------- /flap-gradle-plugin/src/main/java/me/yifeiyuan/flap/plugin/FlapPlugin.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.plugin; 2 | 3 | import com.android.build.gradle.AppExtension; 4 | 5 | import org.gradle.api.Plugin; 6 | import org.gradle.api.Project; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Created by 程序亦非猿 on 2020/9/8. 11 | */ 12 | class FlapPlugin implements Plugin { 13 | @Override 14 | public void apply(@NotNull Project project) { 15 | Log.setup(project); 16 | AppExtension appExtension = project.getExtensions().getByType(AppExtension.class); 17 | appExtension.registerTransform(new FlapTransform(project)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/component_banner.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug]" 5 | labels: question 6 | assignees: AlanCheen 7 | 8 | --- 9 | 10 | **Flap Version** 11 | Flap Version : $The version of Flap you are using. 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FFC107 5 | #FFA000 6 | #FFECB3 7 | #009688 8 | #212121 9 | #757575 10 | #212121 11 | #BDBDBD 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/ViewTypeGenerator.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | import java.util.concurrent.atomic.AtomicInteger 4 | 5 | /** 6 | * Created by 程序亦非猿 on 2021/12/27. 7 | * @since 3.0.0 8 | */ 9 | object ViewTypeGenerator { 10 | private const val VIEW_TYPE_OFFSET = 20200522 11 | private val sNextGeneratedViewType = AtomicInteger(VIEW_TYPE_OFFSET) 12 | fun generateViewType(): Int { 13 | while (true) { 14 | val result = sNextGeneratedViewType.get() 15 | val newValue = result + 1 16 | if (sNextGeneratedViewType.compareAndSet(result, newValue)) { 17 | return result 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/flap_item_generic_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/event/Event.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.event 2 | 3 | /** 4 | * 在 FlapAdapter 与 Component 之间发送的事件 5 | * 6 | * @see me.yifeiyuan.flap.Flap.fireEvent 7 | * @see me.yifeiyuan.flap.Flap.observeEvent 8 | * @see EventObserver 9 | * 10 | * Created by 程序亦非猿 on 2021/9/30. 11 | * @since 3.0.0 12 | */ 13 | data class Event(val eventName: String, var arg: T? = null, val onError: ((result: Any?) -> Unit)? = null, val onSuccess: ((result: Any?) -> Unit)? = null) { 14 | 15 | fun setEventResult(isSuccess: Boolean, result: Any? = null) { 16 | if (isSuccess) { 17 | onSuccess?.invoke(result) 18 | } else { 19 | onError?.invoke(result) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FeatureRequest]" 5 | labels: FeatureRequest 6 | assignees: AlanCheen 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /app/src/main/res/values/drawables.xml: -------------------------------------------------------------------------------- 1 | 2 | @android:drawable/ic_menu_camera 3 | @android:drawable/ic_menu_gallery 4 | @android:drawable/ic_menu_slideshow 5 | @android:drawable/ic_menu_manage 6 | @android:drawable/ic_menu_share 7 | @android:drawable/ic_menu_send 8 | @android:drawable/ic_menu_info_details 9 | -------------------------------------------------------------------------------- /flap/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/flap_item_vb.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/androidTest/java/me/yifeiyuan/flapdev/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4; 4 | 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | /** 9 | * Instrumented test, which will execute on an Android device. 10 | * 11 | * @see Testing documentation 12 | */ 13 | @RunWith(AndroidJUnit4.class) 14 | public class ExampleInstrumentedTest { 15 | @Test 16 | public void useAppContext() { 17 | // Context of the app under test. 18 | // Context appContext = InstrumentationRegistry.getTargetContext(); 19 | 20 | // assertEquals("me.yifeiyuan.flapdev", appContext.getPackageName()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/DataBindingComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import me.yifeiyuan.flap.dsl.databinding.adapterDelegateDataBinding 4 | import me.yifeiyuan.flapdev.R 5 | import me.yifeiyuan.flapdev.databinding.FlapItemSimpleDatabindingBinding 6 | 7 | /** 8 | * 测试 DataBinding 功能 9 | * 10 | * Created by 程序亦非猿 on 2022/8/9. 11 | */ 12 | 13 | class SimpleDataBindingModel { 14 | var text = "使用 DataBinding 的 Component" 15 | } 16 | 17 | //DSL 的方式 18 | fun createDataBindingDelegate() = adapterDelegateDataBinding(R.layout.flap_item_simple_databinding) { 19 | onBind { model -> 20 | binding.model = model 21 | binding.executePendingBindings() 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/flap_component_simple_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/flap_item_custom_type.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/CustomViewTypeComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import me.yifeiyuan.flap.dsl.adapterDelegate 4 | import me.yifeiyuan.flap.ext.bindTextView 5 | import me.yifeiyuan.flapdev.R 6 | 7 | /** 8 | * 完全自定义 AdapterDelegate 用例,使用了自定义 ItemViewType 9 | * 10 | * Created by 程序亦非猿 on 2019/1/18. 11 | */ 12 | 13 | class CustomViewTypeModel(var content: String = "自定义 itemViewType 的 Component") 14 | 15 | const val CUSTOM_ITEM_VIEW_TYPE = 466 16 | 17 | fun createCustomViewTypeComponentDelegate() = adapterDelegate(R.layout.flap_item_custom_type, itemViewType = CUSTOM_ITEM_VIEW_TYPE) { 18 | onBind { model, position, payloads -> 19 | bindTextView(R.id.tv_content) { 20 | text = model.content 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/decoration/SizedColorDrawable.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.decoration 2 | 3 | import android.graphics.drawable.ColorDrawable 4 | 5 | /** 6 | * 可以指定宽高的 ColorDrawable 7 | * 8 | * Created by 程序亦非猿 on 2022/8/18. 9 | * @since 3.0.4 10 | */ 11 | class SizedColorDrawable : ColorDrawable { 12 | 13 | private val height: Int 14 | 15 | private val width: Int 16 | 17 | constructor(color: Int, height: Int, width: Int) : super(color) { 18 | this.height = height 19 | this.width = width 20 | } 21 | 22 | constructor(color: Int, size: Int) : this(color, size, size) 23 | 24 | override fun getIntrinsicHeight(): Int { 25 | return height 26 | } 27 | 28 | override fun getIntrinsicWidth(): Int { 29 | return width 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 灵动 3 | MainActivity 4 | Open navigation drawer 5 | Close navigation drawer 6 | Android Studio 7 | android.studio@android.com 8 | Navigation header 9 | Settings 10 | 11 | Home 12 | Gallery 13 | Slideshow 14 | Hello blank fragment 15 | 16 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /flap-paging/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /flap-paging/src/androidTest/java/me/yifeiyuan/flap/paging/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.paging 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("me.yifeiyuan.flap.paging.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /flap/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /flap-animation/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /flap-animation/src/androidTest/java/me/yifeiyuan/flap/animation/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("me.yifeiyuan.flap.animation.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /flap-dsl-databinding/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /flap-dsl-viewbinding/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /flap-gradle-plugin/src/main/java/me/yifeiyuan/flap/plugin/Log.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.plugin; 2 | 3 | import org.gradle.api.Project; 4 | import org.gradle.api.logging.Logger; 5 | 6 | /** 7 | * Created by 程序亦非猿 on 2020/9/8. 8 | */ 9 | class Log { 10 | 11 | private static Logger logger; 12 | 13 | private static boolean debug = false; 14 | 15 | static void setup(Project project) { 16 | logger = project.getLogger(); 17 | } 18 | 19 | static void i(String info) { 20 | if (debug) { 21 | logger.info("FlapPlugin:" + info); 22 | } 23 | } 24 | 25 | static void println(Object obj) { 26 | System.out.println("FlapPlugin: " + obj); 27 | } 28 | 29 | static void println(String msg) { 30 | if (debug) { 31 | System.out.println("FlapPlugin: " + msg); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /flap-dsl-databinding/src/androidTest/java/me/yifeiyuan/flap/dsl/databinding/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.dsl.databinding 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("me.yifeiyuan.flap.dsl.databinding.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /flap-dsl-viewbinding/src/androidTest/java/me/yifeiyuan/flap/dsl/viewbinding/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.dsl.viewbinding 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("me.yifeiyuan.flap.dsl.viewbinding.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/FlapDebug.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | import android.util.Log 4 | import androidx.annotation.RestrictTo 5 | 6 | /** 7 | * Debug helper for Flap. 8 | * 9 | * Created by 程序亦非猿 on 2021/9/22. 10 | * 11 | * Flap Github: https://github.com/AlanCheen/Flap 12 | * @author 程序亦非猿 [Follow me]( https://github.com/AlanCheen) 13 | * @since 2020/9/22 14 | * @since 3.0.0 15 | */ 16 | @RestrictTo(RestrictTo.Scope.LIBRARY) 17 | object FlapDebug { 18 | 19 | private const val TAG = "Flap" 20 | 21 | var isDebug = false 22 | 23 | fun e(tag: String, msg: String?, tr: Throwable? = null) { 24 | if (isDebug) { 25 | Log.e("$TAG-$tag", msg, tr) 26 | } 27 | } 28 | 29 | fun d(tag: String, msg: String?) { 30 | if (isDebug) { 31 | Log.d("$TAG-$tag", msg) 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /flap/src/androidTest/java/me/yifeiyuan/flap/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap; 2 | 3 | import android.content.Context; 4 | import androidx.test.platform.app.InstrumentationRegistry; 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getInstrumentation().getContext(); 23 | 24 | assertEquals("me.yifeiyuan.flap.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/SimpleImageComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import com.bumptech.glide.Glide 4 | import me.yifeiyuan.flap.dsl.adapterDelegate 5 | import me.yifeiyuan.flap.ext.bindImageView 6 | import me.yifeiyuan.flapdev.R 7 | 8 | /** 9 | * 简单的图片组件测试 10 | * Created by 程序亦非猿 on 2018/12/4. 11 | */ 12 | 13 | class SimpleImageModel { 14 | var url: String? = null 15 | var resId: Int = 0 16 | } 17 | 18 | fun createSimpleImageDelegate() = adapterDelegate(R.layout.component_simple_image) { 19 | onBind { model, position, payloads -> 20 | bindImageView(R.id.logo) { 21 | if (model.url?.isNotEmpty() == true) { 22 | Glide.with(context).load(model.url).into(this) 23 | } else if (model.resId > 0) { 24 | Glide.with(context).load(model.resId).into(this) 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /flap-animation/src/main/java/me/yifeiyuan/flap/animation/ScaleInAdapterAnimation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorSet 5 | import android.animation.ObjectAnimator 6 | import android.view.View 7 | import me.yifeiyuan.flap.Component 8 | 9 | /** 10 | * 放大进入 11 | * 12 | * Created by 程序亦非猿 on 2022/8/29. 13 | * 14 | * @since 3.0.7 15 | */ 16 | class ScaleInAdapterAnimation(var startScaleX: Float = 0.5f, var startScaleY: Float = 0.5f) : BaseAdapterAnimation() { 17 | 18 | override fun createAnimator(view: View, component: Component<*>, data: Any, position: Int): Animator { 19 | val set = AnimatorSet() 20 | val scaleX = ObjectAnimator.ofFloat(view, "scaleX", startScaleX, 1f) 21 | val scaleY = ObjectAnimator.ofFloat(view, "scaleY", startScaleY, 1f) 22 | set.playTogether(scaleX, scaleY) 23 | return set 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/widget/ParallaxHeaderEffect.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.widget 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | /** 6 | * Created by 程序亦非猿 on 2022/10/18. 7 | */ 8 | 9 | /** 10 | * 开启视差 Header 效果 11 | * 12 | * @param factor 视差因子,默认 0.5f ,当 1.0f 效果类似固定的 Header 13 | * @since 3.1.9 14 | */ 15 | fun RecyclerView.enableParallaxHeader(factor: Float = 0.5f) { 16 | addOnScrollListener(ParallaxHeaderHandler(factor)) 17 | } 18 | 19 | internal class ParallaxHeaderHandler(private val factor: Float = 0.5f) : RecyclerView.OnScrollListener() { 20 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 21 | super.onScrolled(recyclerView, dx, dy) 22 | val child = recyclerView.getChildAt(0) 23 | if (child != null && recyclerView.getChildAdapterPosition(child) == 0) { 24 | child.translationY = -child.top * factor 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/StickyHeaders.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev; 2 | 3 | import android.view.View; 4 | 5 | /** 6 | * Created by 程序亦非猿 on 2022/10/17. 7 | */ 8 | interface StickyHeaders { 9 | 10 | boolean isStickyHeader(int position); 11 | 12 | interface ViewSetup { 13 | /** 14 | * Adjusts any necessary properties of the {@code holder} that is being used as a sticky header. 15 | * 16 | * {@link #teardownStickyHeaderView(View)} will be called sometime after this method 17 | * and before any other calls to this method go through. 18 | */ 19 | void setupStickyHeaderView(View stickyHeader); 20 | 21 | /** 22 | * Reverts any properties changed in {@link #setupStickyHeaderView(View)}. 23 | * 24 | * Called after {@link #setupStickyHeaderView(View)}. 25 | */ 26 | void teardownStickyHeaderView(View stickyHeader); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/menu/flap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 14 | 17 | 18 | 19 | 22 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/ComponentConfig.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | import me.yifeiyuan.flap.ext.SwipeDragHelper 4 | 5 | /** 6 | * 组件的配置项抽象,可以控制部分功能的开关 7 | * 8 | * @see SwipeDragHelper 9 | * @see me.yifeiyuan.flap.ext.ItemClicksHelper 10 | * 11 | * Created by 程序亦非猿 on 2022/8/31. 12 | * 13 | * @since 3.0.8 14 | */ 15 | interface ComponentConfig { 16 | 17 | /** 18 | * 支持滑动的方向 19 | */ 20 | fun getSwipeFlags(): Int = SwipeDragHelper.FLAG_UN_SET 21 | 22 | /** 23 | * 支持拖动的方向 24 | */ 25 | fun getDragFlags(): Int = SwipeDragHelper.FLAG_UN_SET 26 | 27 | /** 28 | * 是否可以滑动删除 29 | */ 30 | fun isSwipeEnabled(): Boolean = true 31 | 32 | /** 33 | * 是否可以拖动 34 | */ 35 | fun isDragEnabled(): Boolean = true 36 | 37 | /** 38 | * 是否可以点击 39 | */ 40 | fun isClickable(): Boolean = true 41 | 42 | /** 43 | * 是否可以长按点击 44 | */ 45 | fun isLongClickable(): Boolean = true 46 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## 贡献 2 | 3 | 发 PR 提 issue 都欢迎~ 4 | 5 | ## Styleguides 6 | 7 | ### Git Commit Messages 8 | - Use the present tense ("Add feature" not "Added feature") 9 | - Use the imperative mood ("Move cursor to..." not "Moves cursor to...") 10 | - Limit the first line to 72 characters or less 11 | - Reference issues and pull requests liberally after the first line 12 | - When only changing documentation, include [ci skip] in the commit title 13 | - Consider starting the commit message with an applicable emoji: 14 | - 🎨 `:art:` when improving the format/structure of the code 15 | - 🐎 `:racehorse:` when improving performance 16 | - 🚱 `:non-potable_water:` when plugging memory leaks 17 | - 📝 `:memo:` when writing docs 18 | - 🐛 `:bug:` when fixing a bug 19 | - 🔥 `:fire:` when removing code or files 20 | - ✅ `:white_check_mark:` when adding tests 21 | - ⬆️ `:arrow_up:` when upgrading dependencies 22 | - ⬇️ `:arrow_down:` when downgrading dependencies 23 | - 👕 `:shirt:` when removing linter warnings 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/flap_item_simple_databinding.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 17 | 18 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/skeleton_layout2.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main2.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/menu/animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 10 | 13 | 16 | 19 | 20 | 23 | 26 | 27 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/ViewBindingComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import android.util.Log 4 | import me.yifeiyuan.flap.dsl.viewbinding.adapterDelegateViewBinding 5 | import me.yifeiyuan.flapdev.FlapApplication.Companion.toast 6 | import me.yifeiyuan.flapdev.databinding.FlapItemVbBinding 7 | 8 | /** 9 | * 10 | * 测试使用了 ViewBinding 的 Component 11 | * 使用 ViewBinding 的例子 12 | * Created by 程序亦非猿 on 2020/9/30. 13 | */ 14 | 15 | class ViewBindingModel 16 | 17 | private const val TAG = "ViewBindingComponent" 18 | 19 | fun createViewBindingDelegate() = adapterDelegateViewBinding({ layoutInflater, parent -> FlapItemVbBinding.inflate(layoutInflater, parent, false) }) { 20 | 21 | onBind { model -> 22 | binding.tvContent.text = "adapterDelegateViewBinding DSL 支持 ViewBinding" 23 | } 24 | 25 | onClick { model, position -> 26 | toast("viewBindingDelegate onClick() called with: component = $this, model = $model, position = $position") 27 | } 28 | 29 | onResume { 30 | Log.d(TAG, "viewBindingDelegate onResume() called") 31 | } 32 | 33 | onPause { 34 | Log.d(TAG, "viewBindingDelegate onPause() called") 35 | } 36 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/hook/IAdapterHookManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.hook 2 | 3 | import androidx.annotation.RestrictTo 4 | 5 | /** 6 | * Manage all the AdapterHooks 7 | * 8 | * AdapterHook 管理者的抽象 9 | * @see me.yifeiyuan.flap.FlapRegistry 10 | * @see AdapterHookManager 11 | * 12 | * Created by 程序亦非猿 on 2022/9/5. 13 | * @since 3.1.0 14 | */ 15 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 16 | interface IAdapterHookManager { 17 | 18 | val adapterHooks: MutableList 19 | 20 | fun registerAdapterHook(adapterHook: AdapterHook) { 21 | if (!adapterHooks.contains(adapterHook)) { 22 | adapterHooks.add(adapterHook) 23 | } 24 | } 25 | 26 | fun registerAdapterHook(index: Int, adapterHook: AdapterHook) { 27 | if (!adapterHooks.contains(adapterHook)) { 28 | adapterHooks.add(index, adapterHook) 29 | } 30 | } 31 | 32 | fun registerAdapterHooks(vararg adapterHooks: AdapterHook) { 33 | this.adapterHooks.addAll(adapterHooks) 34 | } 35 | 36 | fun unregisterAdapterHook(adapterHook: AdapterHook) { 37 | adapterHooks.remove(adapterHook) 38 | } 39 | 40 | fun clearAdapterHooks() { 41 | adapterHooks.clear() 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_case_flap_rv.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 19 | 20 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/ext/OnAdapterDataChangedObserver.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.ext 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | /** 6 | * 只关心数据有变化,不关心变化的细节。 7 | * 8 | * Created by 程序亦非猿 on 2022/8/12. 9 | * 10 | * @since 3.0.1 11 | */ 12 | abstract class OnAdapterDataChangedObserver : RecyclerView.AdapterDataObserver() { 13 | 14 | /** 15 | * 当 Adapter 的数据发生任何变化都会回调 16 | */ 17 | abstract fun onAdapterDataChanged() 18 | 19 | override fun onChanged() { 20 | super.onChanged() 21 | onAdapterDataChanged() 22 | } 23 | 24 | override fun onItemRangeChanged(positionStart: Int, itemCount: Int) { 25 | onAdapterDataChanged() 26 | } 27 | 28 | override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) { 29 | onAdapterDataChanged() 30 | } 31 | 32 | override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { 33 | onAdapterDataChanged() 34 | } 35 | 36 | override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) { 37 | onAdapterDataChanged() 38 | } 39 | 40 | override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) { 41 | onAdapterDataChanged() 42 | } 43 | 44 | override fun onStateRestorationPolicyChanged() {} 45 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | 14 | android.enableJetifier=true 15 | android.useAndroidX=true 16 | #org.gradle.daemon=true 17 | #org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 18 | #org.gradle.jvmargs=-Xmx1536m 19 | 20 | 21 | # Disable build features that are enabled by default, 22 | # https://developer.android.com/studio/releases/gradle-plugin#buildFeatures 23 | android.defaults.buildfeatures.buildconfig=false 24 | android.defaults.buildfeatures.aidl=false 25 | android.defaults.buildfeatures.renderscript=false 26 | android.defaults.buildfeatures.resvalues=false 27 | android.defaults.buildfeatures.shaders=false -------------------------------------------------------------------------------- /app/src/main/res/layout/component_full_feature.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 22 | 23 | 33 | 34 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/differ/IDiffer.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.differ 2 | 3 | /** 4 | * 5 | * 实现 IDiffer 接口,可以高效刷新 6 | * 7 | * @see FlapDifferAdapter 8 | * @see androidx.recyclerview.widget.AsyncListDiffer 9 | * 10 | * Created by 程序亦非猿 on 2021/9/22. 11 | * 12 | * Flap Github: https://github.com/AlanCheen/Flap 13 | * @author 程序亦非猿 [Follow me]( https://github.com/AlanCheen) 14 | * @since 2020/9/22 15 | * @since 3.0.0 16 | */ 17 | interface IDiffer { 18 | 19 | /** 20 | * 是否是同一个 item,如果有 id 之类的属性,就比较它们是否相等 21 | * this.id == newItem.id 22 | * 23 | * 当返回 true,则会继续调用 areContentsTheSame 24 | * 25 | * @see androidx.recyclerview.widget.DiffUtil.ItemCallback.areItemsTheSame 26 | */ 27 | fun areItemsTheSame(newItem: Any): Boolean 28 | 29 | /** 30 | * 用于判断两个数据的内容是否相同 31 | * 32 | * 当 areItemsTheSame 返回 true 的时候会调用这个方法 33 | * 34 | * @see androidx.recyclerview.widget.DiffUtil.ItemCallback.areContentsTheSame 35 | */ 36 | fun areContentsTheSame(newItem: Any): Boolean 37 | 38 | /** 39 | * 当 areItemsTheSame 返回 true , 并且 areContentsTheSame 返回 false, 就会调用该方法 40 | * 41 | * @see androidx.recyclerview.widget.DiffUtil.ItemCallback.getChangePayload 42 | * 43 | * @return 一般返回 areContentsTheSame 中不相同的数据,例如 newItem.xxx 44 | */ 45 | fun getChangePayload(newItem: Any): Any? = null 46 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/MyLayoutManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.ViewGroup 6 | import androidx.recyclerview.widget.RecyclerView 7 | 8 | /** 9 | * Created by 程序亦非猿 on 2022/10/9. 10 | */ 11 | class MyLayoutManager : RecyclerView.LayoutManager() { 12 | 13 | //必须重写 14 | override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams = LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) 15 | 16 | class LayoutParams : RecyclerView.LayoutParams { 17 | constructor(c: Context?, attrs: AttributeSet?) : super(c, attrs) 18 | constructor(width: Int, height: Int) : super(width, height) 19 | constructor(source: ViewGroup.MarginLayoutParams?) : super(source) 20 | constructor(source: ViewGroup.LayoutParams?) : super(source) 21 | constructor(source: RecyclerView.LayoutParams?) : super(source) 22 | } 23 | 24 | override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { 25 | super.onLayoutChildren(recycler, state) 26 | } 27 | 28 | override fun canScrollHorizontally(): Boolean { 29 | return super.canScrollHorizontally() 30 | } 31 | 32 | override fun canScrollVertically(): Boolean { 33 | return super.canScrollVertically() 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /flap-animation/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | group = 'me.yifeiyuan' 8 | 9 | apply from: '../config.gradle' 10 | 11 | android { 12 | 13 | compileSdkVersion flapCompileSdkVersion 14 | 15 | defaultConfig { 16 | minSdkVersion flapMinSdkVersion 17 | targetSdkVersion flapTargetSdkVersion 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 43 | 44 | testImplementation 'junit:junit:4.13.2' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 47 | 48 | compileOnly 'androidx.recyclerview:recyclerview:1.2.1' 49 | compileOnly project(':flap') 50 | } -------------------------------------------------------------------------------- /flap-dsl-databinding/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | group = 'me.yifeiyuan' 8 | 9 | apply from: '../config.gradle' 10 | 11 | android { 12 | 13 | compileSdkVersion flapCompileSdkVersion 14 | 15 | defaultConfig { 16 | minSdkVersion flapMinSdkVersion 17 | targetSdkVersion flapTargetSdkVersion 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | buildFeatures { 41 | dataBinding true 42 | } 43 | } 44 | 45 | dependencies { 46 | testImplementation 'junit:junit:4.13.2' 47 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 49 | 50 | compileOnly 'androidx.recyclerview:recyclerview:1.2.1' 51 | api project(':flap') 52 | } -------------------------------------------------------------------------------- /flap-dsl-viewbinding/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | group = 'me.yifeiyuan' 8 | 9 | apply from: '../config.gradle' 10 | 11 | android { 12 | 13 | compileSdkVersion flapCompileSdkVersion 14 | 15 | defaultConfig { 16 | minSdkVersion flapMinSdkVersion 17 | targetSdkVersion flapTargetSdkVersion 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | 41 | buildFeatures { 42 | viewBinding true 43 | } 44 | } 45 | 46 | dependencies { 47 | testImplementation 'junit:junit:4.13.2' 48 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 49 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 50 | 51 | compileOnly 'androidx.recyclerview:recyclerview:1.2.1' 52 | api project(':flap') 53 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/FlapAdapterDelegation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | import android.view.ViewGroup 4 | import androidx.annotation.RestrictTo 5 | import androidx.recyclerview.widget.RecyclerView 6 | 7 | /** 8 | * All the methods that Flap have must be called by the Adapter. 9 | * 10 | * Created by 程序亦非猿 on 2022/12/4. 11 | */ 12 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 13 | interface FlapAdapterDelegation { 14 | 15 | fun onCreateViewHolder(adapter: RecyclerView.Adapter<*>, parent: ViewGroup, viewType: Int): Component<*> 16 | 17 | fun onBindViewHolder( 18 | adapter: RecyclerView.Adapter<*>, 19 | itemData: Any, 20 | component: Component<*>, 21 | position: Int, 22 | payloads: MutableList) 23 | 24 | fun getItemViewType(position: Int, itemData: Any): Int 25 | 26 | fun getItemId(position: Int, itemData: Any): Long 27 | 28 | fun onViewRecycled(adapter: RecyclerView.Adapter<*>, component: Component<*>) 29 | 30 | fun onFailedToRecycleView(adapter: RecyclerView.Adapter<*>, component: Component<*>): Boolean 31 | 32 | fun onViewAttachedToWindow(adapter: RecyclerView.Adapter<*>, component: Component<*>) 33 | fun onViewDetachedFromWindow(adapter: RecyclerView.Adapter<*>, component: Component<*>) 34 | 35 | fun onAttachedToRecyclerView(adapter: RecyclerView.Adapter<*>, recyclerView: RecyclerView) 36 | fun onDetachedFromRecyclerView(adapter: RecyclerView.Adapter<*>, recyclerView: RecyclerView) 37 | } -------------------------------------------------------------------------------- /flap-paging/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | } 5 | 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | group = 'me.yifeiyuan' 8 | 9 | apply from: '../config.gradle' 10 | 11 | android { 12 | 13 | compileSdkVersion flapCompileSdkVersion 14 | 15 | defaultConfig { 16 | minSdkVersion flapMinSdkVersion 17 | targetSdkVersion flapTargetSdkVersion 18 | versionCode 1 19 | versionName "1.0" 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 42 | 43 | testImplementation 'junit:junit:4.13.2' 44 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 45 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 46 | 47 | api project(':flap') 48 | 49 | def paging_version = "3.1.1" 50 | 51 | implementation "androidx.paging:paging-runtime:$paging_version" 52 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_base_case_flap_rv.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 20 | 21 | 27 | 28 | 29 | 37 | -------------------------------------------------------------------------------- /flap/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-parcelize' 4 | 5 | apply plugin: 'com.github.dcendents.android-maven' 6 | group = 'me.yifeiyuan' 7 | 8 | apply from: '../config.gradle' 9 | 10 | android { 11 | 12 | compileSdkVersion flapCompileSdkVersion 13 | 14 | defaultConfig { 15 | minSdkVersion flapMinSdkVersion 16 | targetSdkVersion flapTargetSdkVersion 17 | versionCode 1 18 | versionName "1.0" 19 | 20 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | } 39 | 40 | dependencies { 41 | implementation fileTree(dir: 'libs', include: ['*.jar']) 42 | 43 | testImplementation 'junit:junit:4.13.2' 44 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 45 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 46 | compileOnly 'androidx.recyclerview:recyclerview:1.2.1' 47 | compileOnly "androidx.core:core-ktx:1.6.0" 48 | compileOnly 'androidx.appcompat:appcompat:1.2.0' 49 | compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 50 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/app_bar_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_base_case.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 20 | 21 | 28 | 29 | 30 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/layout/nav_header_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 22 | 23 | 29 | 30 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/GitHubDemoFragment.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev 2 | 3 | import android.view.View 4 | import me.yifeiyuan.flap.decoration.LinearSpaceItemDecoration 5 | import me.yifeiyuan.flap.dsl.adapterDelegate 6 | import me.yifeiyuan.flap.ext.bindTextView 7 | import me.yifeiyuan.flapdev.components.SimpleTextModel 8 | import me.yifeiyuan.flapdev.testcases.BaseTestcaseFragment 9 | 10 | /** 11 | * GitHub 代码示例 12 | * 13 | * Created by 程序亦非猿 on 2022/8/9. 14 | */ 15 | class GitHubDemoFragment : BaseTestcaseFragment() { 16 | 17 | override fun onInit(view: View) { 18 | super.onInit(view) 19 | 20 | recyclerView.addItemDecoration(LinearSpaceItemDecoration(requireActivity().toPixel(6))) 21 | 22 | val simpleTextDelegate = adapterDelegate(R.layout.flap_component_simple_text) { 23 | onBind { model -> 24 | bindTextView(R.id.tv_content) { 25 | text = model.content 26 | } 27 | } 28 | } 29 | 30 | adapter.registerAdapterDelegate(simpleTextDelegate) 31 | 32 | val dataList = ArrayList() 33 | 34 | dataList.add(SimpleTextModel("Android")) 35 | dataList.add(SimpleTextModel("Java")) 36 | dataList.add(SimpleTextModel("Kotlin")) 37 | 38 | adapter.setDataAndNotify(dataList) 39 | } 40 | 41 | override fun createRefreshData(size: Int): MutableList { 42 | return mutableListOf( 43 | SimpleTextModel("Android"), 44 | SimpleTextModel("Java"), 45 | SimpleTextModel("Kotlin") 46 | ) 47 | } 48 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/FlapInitializer.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap 2 | 3 | import android.content.Context 4 | import me.yifeiyuan.flap.delegate.* 5 | import me.yifeiyuan.flap.delegate.DefaultFallbackAdapterDelegate 6 | import me.yifeiyuan.flap.hook.AdapterHook 7 | import me.yifeiyuan.flap.service.AdapterService 8 | 9 | /** 10 | * FlapInitializer ,初始化器,存放全局的配置,会应用于所有的 Flap 实例。 11 | * 12 | * - AdapterDelegate 13 | * - AdapterHook 14 | * - AdapterService 15 | * 16 | * Created by 程序亦非猿 on 2021/9/22. 17 | * 18 | * Flap Github: https://github.com/AlanCheen/Flap 19 | * @author 程序亦非猿 [Follow me]( https://github.com/AlanCheen) 20 | * @since 2020/9/22 21 | * @since 3.0.0 22 | */ 23 | object FlapInitializer : FlapRegistry { 24 | 25 | override val adapterDelegates: MutableList> = mutableListOf() 26 | override val adapterHooks: MutableList = mutableListOf() 27 | override val adapterServices: MutableMap, AdapterService> = mutableMapOf() 28 | 29 | /** 30 | * 是否使用 application context 来创建 Component 31 | */ 32 | var inflateWithApplicationContext = false 33 | 34 | internal var globalFallbackAdapterDelegate: FallbackAdapterDelegate = DefaultFallbackAdapterDelegate() 35 | 36 | fun withContext(context: Context) = apply { 37 | context.applicationContext 38 | } 39 | 40 | /** 41 | * 设置全局的 FallbackAdapterDelegate 42 | */ 43 | fun withFallbackAdapterDelegate(fallbackAdapterDelegate: FallbackAdapterDelegate) = apply { 44 | globalFallbackAdapterDelegate = fallbackAdapterDelegate 45 | } 46 | 47 | fun setDebug(debug: Boolean) = apply { 48 | FlapDebug.isDebug = debug 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/Banner.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import android.widget.ImageView 4 | import androidx.viewpager2.widget.ViewPager2 5 | import com.bumptech.glide.Glide 6 | import me.yifeiyuan.flap.FlapAdapter 7 | import me.yifeiyuan.flap.dsl.adapterDelegate 8 | import me.yifeiyuan.flapdev.FlapApplication.Companion.toast 9 | import me.yifeiyuan.flapdev.R 10 | 11 | /** 12 | * Created by 程序亦非猿 on 2022/9/11. 13 | */ 14 | 15 | class BannerModel { 16 | var images: MutableList? = null 17 | } 18 | 19 | class BannerImageModel { 20 | var url: String? = null 21 | var resId: Int = 0 22 | } 23 | 24 | fun createBannerAdapterDelegate() = adapterDelegate(R.layout.component_banner) { 25 | 26 | swipeEnable = false 27 | dragEnable = false 28 | 29 | val viewPager2 = findViewById(R.id.banner) 30 | 31 | val bannerAdapter = FlapAdapter { 32 | registerAdapterDelegate(bannerImageDelegate()) 33 | } 34 | 35 | viewPager2.adapter = bannerAdapter 36 | 37 | onBind { model, position, payloads -> 38 | model.images?.let { 39 | bannerAdapter.setDataAndNotify(it) 40 | } 41 | } 42 | 43 | bannerAdapter.doOnItemClick { recyclerView, childView, position -> 44 | toast("点击 banner position=$position") 45 | } 46 | } 47 | 48 | fun bannerImageDelegate() = adapterDelegate(R.layout.component_banner_image) { 49 | 50 | val imageView = findViewById(R.id.logo) 51 | 52 | onBind { model -> 53 | if (model.url?.isNotEmpty() == true) { 54 | Glide.with(context).load(model.url).into(imageView) 55 | } else if (model.resId > 0) { 56 | Glide.with(context).load(model.resId).into(imageView) 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/delegate/IAdapterDelegateManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.delegate 2 | 3 | import androidx.annotation.RestrictTo 4 | 5 | /** 6 | * Manage all the AdapterDelegates. 7 | * 8 | * AdapterDelegate 管理者的抽象 9 | * 10 | * @see me.yifeiyuan.flap.FlapRegistry 11 | * @see AdapterDelegateManager 12 | * 13 | * Created by 程序亦非猿 on 2022/9/5. 14 | * @since 3.1.0 15 | */ 16 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 17 | interface IAdapterDelegateManager { 18 | 19 | val adapterDelegates: MutableList> 20 | 21 | /** 22 | * 注册单个 AdapterDelegate 23 | */ 24 | fun registerAdapterDelegate(adapterDelegate: AdapterDelegate<*, *>) { 25 | this.adapterDelegates.add(adapterDelegate) 26 | } 27 | 28 | /** 29 | * 注册单个 AdapterDelegate,并指定 index 30 | */ 31 | fun registerAdapterDelegate(index: Int, adapterDelegate: AdapterDelegate<*, *>) { 32 | this.adapterDelegates.add(index, adapterDelegate) 33 | } 34 | 35 | /** 36 | * 注册多个 AdapterDelegate 37 | */ 38 | fun registerAdapterDelegates(vararg adapterDelegates: AdapterDelegate<*, *>) { 39 | this.adapterDelegates.addAll(adapterDelegates) 40 | } 41 | 42 | /** 43 | * 注册多个 AdapterDelegate 44 | */ 45 | fun registerAdapterDelegates(index: Int,vararg adapterDelegates: AdapterDelegate<*, *>) { 46 | this.adapterDelegates.addAll(index, adapterDelegates.toList()) 47 | } 48 | 49 | /** 50 | * 注销单个 AdapterDelegate 51 | */ 52 | fun unregisterAdapterDelegate(adapterDelegate: AdapterDelegate<*, *>) { 53 | this.adapterDelegates.remove(adapterDelegate) 54 | } 55 | 56 | /** 57 | * 注销所有 AdapterDelegate 58 | */ 59 | fun clearAdapterDelegates() { 60 | adapterDelegates.clear() 61 | } 62 | } -------------------------------------------------------------------------------- /flap-annotations/src/main/java/me/yifeiyuan/flap/annotations/Delegate.java: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * 被 @Delegate 注解的组件,会生成一个 AdapterDelegate 类,生成的类的命名规则:组件类名+AdapterDelegate。 11 | * 12 | * 13 | * Created by 程序亦非猿 on 2021/9/22. 14 | * 15 | * Flap Github: https://github.com/AlanCheen/Flap 16 | * 17 | * @author 程序亦非猿 [Follow me]( https://github.com/AlanCheen) 18 | * @since 2020/9/22 19 | * @since 3.0.0 20 | */ 21 | @Retention(RetentionPolicy.SOURCE) 22 | @Target(ElementType.TYPE) 23 | public @interface Delegate { 24 | 25 | /** 26 | * 返回组件的布局 ID,也会被当做组件的 itemViewType 27 | *

28 | * 注意:layoutId 只支持在 App 模块中使用,如果要在子模块中使用,请用 layoutName 29 | * 30 | * @see #layoutName() 31 | * 32 | * @return the layout res id 33 | * @since 1.1.0 34 | */ 35 | int layoutId() default -1; 36 | 37 | /** 38 | * 该组件的布局文件的名字,例如一个布局文件叫 a_b_c.xml ,那么该值为 a_b_c,不需要后缀 .xml 39 | * 40 | * @return 组件布局的名字 41 | * @since 2.1.0 42 | */ 43 | String layoutName() default ""; 44 | 45 | /** 46 | * 是否使用 DataBinding,假如使用 DataBinding,那么组件的构造方法需要做一定的修改; 47 | * DataBinding 的优先级大于 ViewBinding 48 | * 49 | * @see #useViewBinding() 50 | * 51 | * @return 如果要使用 DataBinding 则设置 true 52 | * @since 1.5.1 53 | */ 54 | boolean useDataBinding() default false; 55 | 56 | /** 57 | * 是否使用 ViewBinding,假如使用 ViewBinding,那么组件的构造方法需要做一定的修改 58 | * DataBinding 的优先级大于 ViewBinding 59 | * 60 | * @see #useDataBinding() 61 | * 62 | * @return 如果要使用 ViewBinding 则设置 true 63 | * @since 2.2.0 64 | */ 65 | boolean useViewBinding() default false; 66 | 67 | } 68 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/skeleton/SkeletonAdapter.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.skeleton 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.annotation.LayoutRes 7 | import androidx.recyclerview.widget.RecyclerView 8 | 9 | /** 10 | * Created by 程序亦非猿 on 2022/8/11. 11 | * 12 | * @since 3.0.1 13 | */ 14 | class SkeletonAdapter : RecyclerView.Adapter() { 15 | 16 | var shimmer: Boolean = false 17 | var skeletonItemCount: Int = 0 18 | 19 | @LayoutRes 20 | var skeletonLayoutRes: Int = -1 21 | 22 | var multiSkeletonLayoutRes: ((position: Int) -> Int)? = null 23 | 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SkeletonViewHolder { 25 | val inflater = LayoutInflater.from(parent.context) 26 | val view: View 27 | if (shimmer) { 28 | val shimmerView = ShimmerFrameLayout(parent.context) 29 | shimmerView.layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) 30 | shimmerView.shimmer?.autoStart = true 31 | inflater.inflate(viewType, shimmerView, true) 32 | view = shimmerView 33 | } else { 34 | view = inflater.inflate(viewType, parent, false) 35 | } 36 | return SkeletonViewHolder(view) 37 | } 38 | 39 | override fun onBindViewHolder(holder: SkeletonViewHolder, position: Int) {} 40 | 41 | override fun getItemCount(): Int { 42 | return skeletonItemCount 43 | } 44 | 45 | override fun getItemViewType(position: Int): Int { 46 | if (multiSkeletonLayoutRes != null) { 47 | return multiSkeletonLayoutRes!!.invoke(position) 48 | } 49 | return skeletonLayoutRes 50 | } 51 | } 52 | 53 | class SkeletonViewHolder(view: View) : RecyclerView.ViewHolder(view) -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/widget/FlapGridLayoutManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.widget 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.recyclerview.widget.GridLayoutManager 6 | import androidx.recyclerview.widget.RecyclerView 7 | import me.yifeiyuan.flap.FlapDebug 8 | 9 | /** 10 | * Created by 程序亦非猿 on 2021/9/30. 11 | * @since 3.0.0 12 | */ 13 | open class FlapGridLayoutManager 14 | : GridLayoutManager { 15 | 16 | constructor(context: Context?, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : super(context, attrs, defStyleAttr, defStyleRes) 17 | constructor(context: Context?, spanCount: Int) : super(context, spanCount) 18 | constructor(context: Context?, spanCount: Int, orientation: Int = RecyclerView.VERTICAL, reverseLayout: Boolean = false) : super(context, spanCount, orientation, reverseLayout) 19 | 20 | companion object { 21 | private const val TAG = "FlapGridLayoutManager" 22 | } 23 | 24 | var supportsPredictiveItemAnimations = false 25 | 26 | init { 27 | isSmoothScrollbarEnabled = false 28 | } 29 | 30 | /** 31 | * Disable predictive animations. There is a bug in RecyclerView which causes views that 32 | * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the 33 | * adapter size has decreased since the ViewHolder was recycled. 34 | * 35 | * https://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position 36 | */ 37 | override fun supportsPredictiveItemAnimations(): Boolean { 38 | return supportsPredictiveItemAnimations 39 | } 40 | 41 | override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { 42 | try { 43 | super.onLayoutChildren(recycler, state) 44 | } catch (e: Exception) { 45 | e.printStackTrace() 46 | FlapDebug.e(TAG, "onLayoutChildren: ", e) 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/activity_main_drawer.xml: -------------------------------------------------------------------------------- 1 | 2 |

5 | 6 | 7 | 12 | 16 | 20 | 24 | 25 | 29 | 33 | 37 | 41 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/SimpleTextComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import me.yifeiyuan.flap.Component 8 | import me.yifeiyuan.flap.ViewTypeGenerator 9 | import me.yifeiyuan.flap.delegate.AdapterDelegate 10 | import me.yifeiyuan.flap.dsl.adapterDelegate 11 | import me.yifeiyuan.flap.ext.bindTextView 12 | import me.yifeiyuan.flapdev.R 13 | 14 | /** 15 | * Created by 程序亦非猿 on 2018/12/4. 16 | * 17 | * 作为文档 Demo 18 | */ 19 | 20 | data class SimpleTextModel(val content: String) { 21 | override fun toString(): String { 22 | return "content=$content,SimpleTextModel" 23 | } 24 | } 25 | 26 | fun createSimpleTextDelegate() = adapterDelegate(R.layout.flap_component_simple_text) { 27 | onBind { model -> 28 | bindTextView(R.id.tv_content) { 29 | text = model.content 30 | } 31 | } 32 | } 33 | 34 | class SimpleTextComponent(itemView: View) : Component(itemView) { 35 | 36 | private val tvContent: TextView = findViewById(R.id.tv_content) 37 | 38 | override fun onBind(model: SimpleTextModel, position: Int, payloads: List) { 39 | tvContent.text = model.content 40 | } 41 | } 42 | 43 | //自定义 AdapterDelegate 实现 44 | class SimpleTextComponentDelegate : AdapterDelegate { 45 | 46 | override fun isDelegateFor(model: Any): Boolean { 47 | return SimpleTextModel::class.java == model::class.java 48 | } 49 | 50 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): SimpleTextComponent { 51 | return SimpleTextComponent(inflater.inflate(R.layout.flap_component_simple_text, parent, false)) 52 | } 53 | 54 | override fun getItemViewType(model: Any): Int { 55 | return ViewTypeGenerator.generateViewType() 56 | // return R.layout.flap_item_simple_text 57 | } 58 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/widget/FlapLinearLayoutManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.widget 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.recyclerview.widget.LinearLayoutManager 6 | import androidx.recyclerview.widget.RecyclerView 7 | import me.yifeiyuan.flap.FlapDebug 8 | 9 | /** 10 | * Created by 程序亦非猿 on 2021/9/30. 11 | * @since 3.0.0 12 | */ 13 | open class FlapLinearLayoutManager 14 | : LinearLayoutManager { 15 | 16 | constructor(context: Context) : super(context) 17 | 18 | constructor(context: Context, orientation: Int = RecyclerView.VERTICAL, reverseLayout: Boolean = false) : super(context, orientation, reverseLayout) 19 | 20 | constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : super(context, attrs, defStyleAttr, defStyleRes) 21 | 22 | companion object { 23 | private const val TAG = "FlapLinearLayoutManager" 24 | } 25 | 26 | var supportsPredictiveItemAnimations = false 27 | 28 | init { 29 | //当第一个 Item 高度为 0,会影响 RV 计算是否能滑动,所以设置为 false 30 | isSmoothScrollbarEnabled = false 31 | } 32 | 33 | /** 34 | * Disable predictive animations. There is a bug in RecyclerView which causes views that 35 | * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the 36 | * adapter size has decreased since the ViewHolder was recycled. 37 | * 38 | * https://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position 39 | */ 40 | override fun supportsPredictiveItemAnimations(): Boolean { 41 | return supportsPredictiveItemAnimations 42 | } 43 | 44 | override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { 45 | try { 46 | super.onLayoutChildren(recycler, state) 47 | } catch (e: Exception) { 48 | e.printStackTrace() 49 | FlapDebug.e(TAG, "onLayoutChildren: ", e) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /flap/src/main/res/values/shimmer_attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/delegate/FallbackAdapterDelegate.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.delegate 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import me.yifeiyuan.flap.Component 8 | import me.yifeiyuan.flap.FlapDebug 9 | 10 | /** 11 | * FallbackAdapterDelegate is a build-in AdapterDelegate that would be used when something went wrong. 12 | * So that Flap won't crash your App. 13 | * 14 | * FallbackAdapterDelegate 是一个默认代理所有数据模型的 AdapterDelegate,可以用来处理未知模型兜底逻辑。 15 | * 16 | * 如果一个 model 没有任何 AdapterDelegate 代理,那么 FallbackAdapterDelegate 会负责接手处理。 17 | * 18 | * 开发者也可以自己定义自己的 FallbackAdapterDelegate。 19 | * 20 | * @see me.yifeiyuan.flap.FlapInitializer.globalFallbackAdapterDelegate 21 | * @see me.yifeiyuan.flap.Flap.fallbackDelegate 22 | * @see DefaultFallbackComponent 23 | * 24 | * Created by 程序亦非猿 on 2021/9/22. 25 | * 26 | * Flap Github: https://github.com/AlanCheen/Flap 27 | * @author 程序亦非猿 [Follow me]( https://github.com/AlanCheen) 28 | * @since 2020/9/22 29 | * @since 3.0.0 30 | */ 31 | abstract class FallbackAdapterDelegate : AdapterDelegate> { 32 | override fun isDelegateFor(model: Any): Boolean { 33 | return true 34 | } 35 | } 36 | 37 | abstract class FallbackComponent(v: View) : Component(v) { 38 | 39 | override fun isClickable(): Boolean { 40 | return false 41 | } 42 | 43 | override fun isLongClickable(): Boolean { 44 | return false 45 | } 46 | 47 | override fun isDragEnabled(): Boolean { 48 | return false 49 | } 50 | 51 | override fun isSwipeEnabled(): Boolean { 52 | return false 53 | } 54 | } 55 | 56 | internal class DefaultFallbackAdapterDelegate : FallbackAdapterDelegate() { 57 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): DefaultFallbackComponent { 58 | return DefaultFallbackComponent(TextView(parent.context)) 59 | } 60 | } 61 | 62 | internal class DefaultFallbackComponent(v: View) : FallbackComponent(v) { 63 | override fun onBind(model: Any) {} 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/res/layout/skeleton_layout3.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 23 | 24 | 35 | 36 | 40 | 41 | 46 | 47 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/res/layout/skeleton_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 24 | 25 | 35 | 36 | 40 | 41 | 46 | 47 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/widget/FlapStaggeredGridLayoutManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.widget 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 7 | import me.yifeiyuan.flap.FlapDebug 8 | 9 | /** 10 | * Created by 程序亦非猿 on 2021/9/30. 11 | * @since 3.0.0 12 | */ 13 | open class FlapStaggeredGridLayoutManager 14 | : StaggeredGridLayoutManager { 15 | 16 | companion object { 17 | private const val TAG = "FlapStaggeredGridLayoutManager" 18 | } 19 | 20 | constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : super(context, attrs, defStyleAttr, defStyleRes) 21 | 22 | constructor(spanCount: Int, orientation: Int = RecyclerView.VERTICAL) : super(spanCount, orientation) 23 | 24 | var supportsPredictiveItemAnimations = false 25 | 26 | init { 27 | gapStrategy = GAP_HANDLING_NONE 28 | } 29 | 30 | /** 31 | * Disable predictive animations. There is a bug in RecyclerView which causes views that 32 | * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the 33 | * adapter size has decreased since the ViewHolder was recycled. 34 | * 35 | * https://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position 36 | */ 37 | override fun supportsPredictiveItemAnimations(): Boolean { 38 | return supportsPredictiveItemAnimations 39 | } 40 | 41 | override fun onLayoutChildren(recycler: RecyclerView.Recycler?, state: RecyclerView.State?) { 42 | try { 43 | super.onLayoutChildren(recycler, state) 44 | } catch (e: Exception) { 45 | e.printStackTrace() 46 | FlapDebug.e(TAG, "onLayoutChildren: ", e) 47 | } 48 | } 49 | 50 | override fun onScrollStateChanged(state: Int) { 51 | try { 52 | super.onScrollStateChanged(state) 53 | } catch (e: Exception) { 54 | e.printStackTrace() 55 | FlapDebug.e(TAG, "onScrollStateChanged: ", e) 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/TestConfig.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import me.yifeiyuan.flap.differ.IDiffer 4 | import me.yifeiyuan.flap.dsl.adapterDelegate 5 | import me.yifeiyuan.flap.ext.bindTextView 6 | import me.yifeiyuan.flapdev.FlapApplication.Companion.application 7 | import me.yifeiyuan.flapdev.R 8 | import me.yifeiyuan.flapdev.toPixel 9 | import kotlin.random.Random 10 | 11 | /** 12 | * 13 | * 由 Model 属性决定 Component 的功能开关 14 | * 15 | * Created by 程序亦非猿 on 2022/9/13. 16 | */ 17 | 18 | class TestConfigModel : IDiffer { 19 | 20 | var id: Int = -1 21 | var title: String? = null 22 | var content: String? = null 23 | 24 | var dragEnable: Boolean = false 25 | var swipeEnable: Boolean = false 26 | 27 | var swipeFlags: Int = 0 28 | var dragFlags: Int = 0 29 | 30 | var clickEnable: Boolean = false 31 | var longClickEnable: Boolean = false 32 | 33 | var height: Int = -1 34 | 35 | init { 36 | height = Random.nextInt(application!!.toPixel(60), application!!.toPixel(120)) 37 | } 38 | 39 | override fun areItemsTheSame(newItem: Any): Boolean { 40 | return false 41 | } 42 | 43 | override fun areContentsTheSame(newItem: Any): Boolean { 44 | return false 45 | } 46 | 47 | override fun getChangePayload(newItem: Any): Any? { 48 | return super.getChangePayload(newItem) 49 | } 50 | } 51 | 52 | fun createFullConfigAdapterDelegate() = adapterDelegate(R.layout.component_full_feature) { 53 | 54 | onBind { model, position, payloads -> 55 | 56 | bindTextView(R.id.title) { 57 | text = model.title 58 | } 59 | 60 | bindTextView(R.id.content) { 61 | text = model.content 62 | } 63 | 64 | swipeEnable = model.swipeEnable 65 | dragEnable = model.dragEnable 66 | swipeFlags = model.swipeFlags 67 | dragFlags = model.dragFlags 68 | clickable = model.clickEnable 69 | longClickable = model.longClickEnable 70 | 71 | if (model.height > 0) { 72 | val lp = itemView.layoutParams.apply { 73 | height = model.height 74 | } 75 | itemView.layoutParams = lp 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/service/IAdapterServiceManager.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.service 2 | 3 | import androidx.annotation.RestrictTo 4 | 5 | /** 6 | * IAdapterServiceManager is for managing all the AdapterServices. 7 | * 8 | * You can register an AdapterService in Application or Activity or Fragment. 9 | * 10 | * And get the AdapterService when binding Component by calling {getAdapterService} or {Component#callService} 11 | * 12 | * @see me.yifeiyuan.flap.FlapRegistry 13 | * @see AdapterServiceManager 14 | * 15 | * AdapterService 管理,可以注册或获取 AdapterService 16 | * 17 | * 可以在 Activity 中注册 AdapterService,在 Component 中使用。 18 | * 19 | * Created by 程序亦非猿 on 2022/8/18. 20 | * @since 3.0.3 21 | */ 22 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 23 | interface IAdapterServiceManager { 24 | 25 | val adapterServices: MutableMap, AdapterService> 26 | 27 | /** 28 | * 注册 AdapterService,并反射调用无参构造器进行实例化 29 | */ 30 | fun registerAdapterService(serviceClass: Class) { 31 | try { 32 | val service = serviceClass.getConstructor().newInstance() 33 | adapterServices[serviceClass] = service 34 | } catch (e: Exception) { 35 | e.printStackTrace() 36 | } 37 | } 38 | 39 | /** 40 | * 注册多个 AdapterService,并反射调用无参构造器进行实例化 41 | */ 42 | fun registerAdapterServices(vararg serviceClasses: Class) { 43 | try { 44 | serviceClasses.forEach { 45 | val service = it.getConstructor().newInstance() 46 | adapterServices[it] = service 47 | } 48 | } catch (e: Exception) { 49 | e.printStackTrace() 50 | } 51 | } 52 | 53 | /** 54 | * 55 | * 注册 AdapterService 实例 56 | */ 57 | fun registerAdapterService(serviceClass: Class, service: T) { 58 | adapterServices[serviceClass] = service 59 | } 60 | 61 | /** 62 | * Return an AdapterService instance of the Given service class. 63 | * If no AdapterService is found it returns null. 64 | * 65 | * 获取 AdapterService 66 | */ 67 | @Suppress("UNCHECKED_CAST") 68 | fun getAdapterService(serviceClass: Class): T? { 69 | return adapterServices[serviceClass] as? T 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /flap-animation/src/main/java/me/yifeiyuan/flap/animation/BaseAdapterAnimation.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.animation 2 | 3 | import android.animation.Animator 4 | import android.view.View 5 | import android.view.animation.Interpolator 6 | import android.view.animation.LinearInterpolator 7 | import androidx.recyclerview.widget.RecyclerView 8 | import me.yifeiyuan.flap.Component 9 | import me.yifeiyuan.flap.FlapAdapter 10 | import me.yifeiyuan.flap.hook.AdapterHook 11 | 12 | /** 13 | * Adapter Animation ,在完成 bind 后执行动画 14 | * 15 | * Created by 程序亦非猿 on 2022/8/29. 16 | * 17 | * @since 3.0.7 18 | */ 19 | abstract class BaseAdapterAnimation : AdapterHook { 20 | 21 | var duration: Long = 300L 22 | var isFirstOnly: Boolean = true 23 | var interpolator: Interpolator = LinearInterpolator() 24 | private var lastPosition = -1 25 | 26 | override fun onPostBindViewHolder(adapter: RecyclerView.Adapter<*>, component: Component<*>, data: Any, position: Int, payloads: MutableList) { 27 | super.onPostBindViewHolder(adapter, component, data, position, payloads) 28 | val adapterPosition = component.adapterPosition 29 | if (!isFirstOnly || adapterPosition > lastPosition) { 30 | val animator = createAnimator(component.itemView, component, data, position) 31 | animator?.let { 32 | it.duration = duration 33 | it.interpolator = interpolator 34 | it.start() 35 | } 36 | lastPosition = adapterPosition 37 | } else { 38 | clear(component.itemView) 39 | } 40 | } 41 | 42 | abstract fun createAnimator(view: View, component: Component<*>, data: Any, position: Int): Animator? 43 | 44 | private fun clear(v: View) { 45 | v.apply { 46 | alpha = 1f 47 | scaleY = 1f 48 | scaleX = 1f 49 | translationY = 0f 50 | translationX = 0f 51 | rotation = 0f 52 | rotationY = 0f 53 | rotationX = 0f 54 | pivotY = v.measuredHeight / 2f 55 | pivotX = v.measuredWidth / 2f 56 | animate().setInterpolator(null).startDelay = 0 57 | } 58 | } 59 | 60 | fun reset() { 61 | lastPosition = -1 62 | } 63 | 64 | fun attachToAdapter(flapAdapter: FlapAdapter) { 65 | flapAdapter.registerAdapterHook(this) 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/TestAdapterApiComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import android.util.Log 4 | import android.widget.TextView 5 | import me.yifeiyuan.flap.dsl.adapterDelegate 6 | import me.yifeiyuan.flap.event.Event 7 | import me.yifeiyuan.flap.ext.bindButton 8 | import me.yifeiyuan.flapdev.R 9 | import me.yifeiyuan.flapdev.LogService 10 | 11 | /** 12 | * Created by 程序亦非猿 on 2022/7/29. 13 | */ 14 | 15 | private const val TAG = "TestAllComponent" 16 | 17 | class TestAdapterApiModel 18 | 19 | fun createTestAdapterApiComponentDelegate() = adapterDelegate(R.layout.component_test_all_feature){ 20 | 21 | //展示信息 22 | val messageTextView = findViewById(R.id.message) 23 | 24 | onBind { model, position, payloads -> 25 | 26 | bindButton(R.id.testFireEvent) { 27 | // fireEvent 发送事件 28 | setOnClickListener{ 29 | val showToastEvent = Event("showToast", "Fire event:showToast,position=$position") { 30 | Log.d(TAG, "onBind: showToastEvent success") 31 | 32 | messageTextView.text = "showToast event 收到回调:success" 33 | } 34 | fireEvent(showToastEvent) 35 | 36 | val intE = Event("intEvent", 233333) 37 | fireEvent(intE) 38 | } 39 | } 40 | 41 | bindButton(R.id.testGetParam){ 42 | 43 | setOnClickListener { 44 | val stringValue = getParam("stringValue") 45 | val intValue = getParam("intValue") 46 | val booleanValue = getParam("booleanValue") 47 | 48 | val results :String = StringBuilder(). 49 | append("stringValue=$stringValue ;;") 50 | .append("intValue=$intValue ;;") 51 | .append("booleanValue=$booleanValue ;;") 52 | .toString() 53 | 54 | messageTextView.text = "Adapter.getParam results=$results" 55 | } 56 | } 57 | 58 | bindButton(R.id.testGetAdapterService) { 59 | setOnClickListener { 60 | 61 | callService { 62 | log("LogService Message") 63 | messageTextView.text = testResult() 64 | } 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/pool/ComponentPool.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.pool 2 | 3 | import android.content.ComponentCallbacks2 4 | import android.content.res.Configuration 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.recyclerview.widget.RecyclerView.RecycledViewPool 7 | import me.yifeiyuan.flap.FlapDebug 8 | 9 | /** 10 | * 自定义的 RecycledViewPool,实现了 ComponentCallbacks2 ,可以在内存不足的时候清理缓存 11 | * 12 | * @see me.yifeiyuan.flap.Flap.setComponentPoolEnable 设置开关 13 | * 14 | * Created by 程序亦非猿 on 2021/9/22. 15 | * 16 | * Flap Github: https://github.com/AlanCheen/Flap 17 | * @author 程序亦非猿 [Follow me]( https://github.com/AlanCheen) 18 | * @since 2020/9/22 19 | * @since 3.0.2 20 | */ 21 | open class ComponentPool : RecycledViewPool(), ComponentCallbacks2 { 22 | 23 | companion object { 24 | private const val TAG = "ComponentPool" 25 | } 26 | 27 | override fun setMaxRecycledViews(viewType: Int, max: Int) { 28 | super.setMaxRecycledViews(viewType, max) 29 | FlapDebug.d(TAG, "setMaxRecycledViews() called with: viewType = $viewType, max = $max") 30 | } 31 | 32 | override fun getRecycledViewCount(viewType: Int): Int { 33 | val result = super.getRecycledViewCount(viewType) 34 | FlapDebug.d(TAG, "getRecycledViewCount() called with: viewType = $viewType,result=$result") 35 | return result 36 | } 37 | 38 | override fun getRecycledView(viewType: Int): RecyclerView.ViewHolder? { 39 | val result = super.getRecycledView(viewType) 40 | FlapDebug.d(TAG, "getRecycledView() called with: viewType = $viewType,result=$result") 41 | return result 42 | } 43 | 44 | override fun putRecycledView(scrap: RecyclerView.ViewHolder) { 45 | super.putRecycledView(scrap) 46 | FlapDebug.d(TAG, "putRecycledView() called with: scrap = $scrap") 47 | } 48 | 49 | //参考 Glide 的 MemoryCache 实现 50 | override fun onTrimMemory(level: Int) { 51 | when (level) { 52 | in ComponentCallbacks2.TRIM_MEMORY_BACKGROUND..ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> { 53 | clear() 54 | } 55 | else -> { 56 | } 57 | } 58 | FlapDebug.d(TAG, "onTrimMemory() called with: level = $level") 59 | } 60 | 61 | override fun onConfigurationChanged(newConfig: Configuration) {} 62 | 63 | override fun onLowMemory() { 64 | FlapDebug.d(TAG, "onLowMemory: ") 65 | clear() 66 | } 67 | 68 | override fun clear() { 69 | super.clear() 70 | FlapDebug.d("ComponentPool", "ComponentPool 执行清理缓存") 71 | } 72 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/components/DiffComponent.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.components 2 | 3 | import android.os.SystemClock 4 | import me.yifeiyuan.flap.differ.IDiffer 5 | import me.yifeiyuan.flap.dsl.adapterDelegate 6 | import me.yifeiyuan.flap.ext.bindButton 7 | import me.yifeiyuan.flap.ext.bindTextView 8 | import me.yifeiyuan.flapdev.R 9 | 10 | /** 11 | * Created by 程序亦非猿 on 2022/8/1. 12 | * 13 | * 如果都返回 true ,内容修改后再下拉刷新,不会有 onbind 行为 14 | */ 15 | 16 | class TestDiffModel(var content: String, var id: Int, var desc: String) : IDiffer { 17 | 18 | override fun areItemsTheSame(newItem: Any): Boolean { 19 | if (newItem.javaClass == TestDiffModel::class.java) { 20 | return id == (newItem as TestDiffModel).id 21 | } else { 22 | return false 23 | } 24 | // return true 25 | } 26 | 27 | override fun areContentsTheSame(newItem: Any): Boolean { 28 | return content == (newItem as TestDiffModel).content 29 | // return true 30 | } 31 | 32 | override fun getChangePayload(newItem: Any): Any? { 33 | return (newItem as TestDiffModel).content 34 | } 35 | 36 | override fun toString(): String { 37 | return "(id=$id,content='$content',desc='$desc')TestDiffModel" 38 | } 39 | } 40 | 41 | fun createDiffDelegate() = adapterDelegate(R.layout.component_diff) { 42 | onBind { model, position, payloads -> 43 | 44 | //当 payloads 更新时,事件点击需要重新设置 45 | bindButton(R.id.modifyContent) { 46 | setOnClickListener { 47 | model.content = "修改后 Content:" + (SystemClock.uptimeMillis() % 10000).toInt().toString() 48 | 49 | adapter.notifyItemChanged(position, model.content)//不会闪 50 | // adapter.notifyItemChanged(position)//会闪 51 | } 52 | } 53 | 54 | bindButton(R.id.modifyId) { 55 | setOnClickListener { 56 | model.id = (SystemClock.uptimeMillis() % 10000).toInt() 57 | adapter.notifyItemChanged(position) 58 | } 59 | } 60 | 61 | if (payloads.isNotEmpty()) { 62 | bindTextView(R.id.content) { 63 | text = "展示 content :${payloads.get(0)}" 64 | } 65 | } else { 66 | bindTextView(R.id.content) { 67 | text = "展示 content :${model.content}" 68 | } 69 | 70 | bindTextView(R.id.id) { 71 | text = "展示 ID :${model.id}" 72 | } 73 | 74 | bindTextView(R.id.desc) { 75 | text = "展示 desc :${model.desc}" 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /flap-dsl-viewbinding/src/main/java/me/yifeiyuan/flap/dsl/viewbinding/AdapterDelegateViewBindingDsl.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.dsl.viewbinding 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.viewbinding.ViewBinding 7 | import me.yifeiyuan.flap.delegate.AdapterDelegate 8 | import me.yifeiyuan.flap.dsl.DslComponent 9 | 10 | /** 11 | * 支持 AdapterDelegate ViewBinding DSL 功能 12 | * Created by 程序亦非猿 on 2022/9/20. 13 | * @since 3.1.3 14 | */ 15 | inline fun adapterDelegateViewBinding( 16 | noinline viewBinding: (layoutInflater: LayoutInflater, parent: ViewGroup) -> V, 17 | itemViewType: Int = 0, 18 | noinline isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == T::class.java }, 19 | itemId: Long = RecyclerView.NO_ID, 20 | noinline componentInitializer: ViewBindingDslComponent.() -> Unit): ViewBindingDslAdapterDelegate { 21 | return ViewBindingDslAdapterDelegate(T::class.java, viewBinding, itemViewType, itemId, isDelegateFor = isDelegateFor, block = componentInitializer) 22 | } 23 | 24 | /** 25 | * 支持 AdapterDelegate ViewBinding DSL 功能 26 | * Created by 程序亦非猿 on 2022/9/20. 27 | * @since 3.1.3 28 | */ 29 | class ViewBindingDslAdapterDelegate( 30 | private var modelClass: Class, 31 | private var viewBinding: (layoutInflater: LayoutInflater, parent: ViewGroup) -> V, 32 | private var itemViewType: Int, 33 | private var itemId: Long = RecyclerView.NO_ID, 34 | private var isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == modelClass }, 35 | private var block: ViewBindingDslComponent.() -> Unit, 36 | ) : AdapterDelegate> { 37 | 38 | override fun isDelegateFor(model: Any): Boolean { 39 | return isDelegateFor.invoke(model) 40 | } 41 | 42 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): ViewBindingDslComponent { 43 | val binding = viewBinding(inflater, parent) 44 | val component = ViewBindingDslComponent(binding) 45 | block.invoke(component) 46 | return component 47 | } 48 | 49 | override fun getItemId(model: Any, position: Int): Long { 50 | return itemId 51 | } 52 | 53 | override fun getItemViewType(model: Any): Int { 54 | return itemViewType 55 | } 56 | } 57 | 58 | /** 59 | * 支持 AdapterDelegate ViewBinding DSL 功能 60 | * Created by 程序亦非猿 on 2022/9/20. 61 | * @since 3.1.3 62 | */ 63 | class ViewBindingDslComponent(val binding: V) : DslComponent(binding.root) -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # Flap 2 | 3 | [![Build Status](https://travis-ci.org/AlanCheen/Flap.svg?branch=master)](https://travis-ci.org/AlanCheen/Flap) ![AndroidX](https://img.shields.io/badge/AndroidX-Migrated-brightgreen) ![RecyclerView](https://img.shields.io/badge/RecyclerView-1.1.0-brightgreen.svg) ![API](https://img.shields.io/badge/API-14%2B-brightgreen.svg?style=flat) [![license](https://img.shields.io/github/license/AlanCheen/Flap.svg)](./LICENSE) [![Author](https://img.shields.io/badge/%E4%BD%9C%E8%80%85-%E7%A8%8B%E5%BA%8F%E4%BA%A6%E9%9D%9E%E7%8C%BF-blue.svg)](https://github.com/AlanCheen) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/AlanCheen/Flap/pulls) 4 | 5 | ------ 6 | 7 | ## Usage 8 | 9 | 1. Step1 : create a Model 10 | 2. Step2 : create a layout file for component(A ViewHolder) 11 | 3. Step3 : create a AdapterDelegate and register it. 12 | 13 | 14 | 1)Create a Model, lets say `SimpleTextModel` : 15 | ```kotlin 16 | data class SimpleTextModel(val content: String) 17 | ``` 18 | 2)A simple layout that containers a TextView : 19 | ```xml 20 | 21 | 26 | 27 | 37 | 38 | 39 | ``` 40 | 41 | 3)Create a `AdapterDelegate` by `adapterDelegate` DSL ,and
override `onBind` method: 42 | 43 | ```kotlin 44 | val simpleTextDelegate = adapterDelegate(R.layout.flap_item_simple_text) { 45 | onBind { model -> 46 | bindTextView(R.id.tv_content) { 47 | text = model.content 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | Use a `FlapAdapter` instead of `Adapter` and register AdapterDelegate to a FlapAdapter: : 54 | 55 | ```kotlin 56 | //create FlapAdapter 57 | var adapter: FlapAdapter = FlapAdapter() 58 | 59 | //register AdapterDelegate 60 | adapter.registerAdapterDelegate(simpleTextDelegate) 61 | 62 | val dataList = ArrayList() 63 | dataList.add(SimpleTextModel("Android")) 64 | dataList.add(SimpleTextModel("Java")) 65 | dataList.add(SimpleTextModel("Kotlin")) 66 | 67 | //setData 68 | adapter.setDataAndNotify(dataList); 69 | 70 | recyclerView.adapter = adapter 71 | ``` 72 | 73 | Here we go!! 74 | 75 |
76 | 77 | ## License 78 | Apache 2.0 -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/testcases/FlapDifferAdapterTestcase.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.testcases 2 | 3 | import android.os.Handler 4 | import me.yifeiyuan.flap.FlapAdapter 5 | import me.yifeiyuan.flap.differ.IDiffer 6 | import me.yifeiyuan.flap.differ.FlapDifferAdapter 7 | import me.yifeiyuan.flapdev.components.TestDiffModel 8 | import java.util.ArrayList 9 | 10 | 11 | /** 12 | * 测试 FlapDifferAdapter 功能 13 | * 14 | * 测试说明,点击按钮后,再下拉刷新,只有被修改了的数据才会有刷新动画 15 | * 16 | * @see FlapDifferAdapter 17 | * 18 | * Created by 程序亦非猿 on 2022/8/1. 19 | */ 20 | class FlapDifferAdapterTestcase : BaseTestcaseFragment() { 21 | 22 | //如果不测试 header footer,就注释掉 onViewCreated 23 | // override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | // super.onViewCreated(view, savedInstanceState) 25 | // 26 | // val headerFooterAdapter = HeaderFooterAdapter(adapter) 27 | // 28 | // val headerView = LayoutInflater.from(activity).inflate(R.layout.header_layout, null, false) 29 | // headerFooterAdapter.setupHeaderView(headerView) 30 | // 31 | // val footerView = LayoutInflater.from(activity).inflate(R.layout.footer_layout, null, false) 32 | // headerFooterAdapter.setupFooterView(footerView) 33 | // 34 | // adapter.doOnItemClick { recyclerView, childView, position -> 35 | // val component = recyclerView.getChildViewHolder(childView) 36 | // if (headerFooterAdapter.isHeader(component)) { 37 | // toast("点击了 position = $position,是 Header!") 38 | // return@doOnItemClick 39 | // } 40 | // if (headerFooterAdapter.isFooter(component)) { 41 | // toast("点击了 position = $position,是 Footer!") 42 | // return@doOnItemClick 43 | // } 44 | // val realPosition = if (position == 0) position else position - headerFooterAdapter.getHeaderCount() 45 | // toast("点击了 position = $position,model=${adapter.getItemData(realPosition)}") 46 | // } 47 | // 48 | // recyclerView.adapter = headerFooterAdapter 49 | // } 50 | 51 | override fun createAdapter(): FlapAdapter { 52 | return FlapDifferAdapter().apply { 53 | setData(createRefreshData()) 54 | } 55 | } 56 | 57 | override fun createRefreshData(size: Int): MutableList { 58 | val list = mutableListOf() 59 | repeat(20) { 60 | list.add(TestDiffModel("content,$it of 20", it, "this is desc :${it % 3}")) 61 | } 62 | return list 63 | } 64 | 65 | override fun loadMoreData(size: Int) { 66 | Handler().postDelayed({ 67 | val list = ArrayList() 68 | repeat(size) { 69 | list.add(TestDiffModel("content,$it of 20,more data", it, "this is desc :${it % 3}")) 70 | } 71 | adapter.appendDataAndNotify(list) 72 | }, 500) 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /app/src/main/java/me/yifeiyuan/flapdev/testcases/PreloadTestcase.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flapdev.testcases 2 | 3 | import android.os.Bundle 4 | import android.os.Handler 5 | import android.util.Log 6 | import android.view.Menu 7 | import android.view.MenuInflater 8 | import android.view.MenuItem 9 | import android.view.View 10 | import me.yifeiyuan.flap.hook.PreloadHook 11 | import me.yifeiyuan.flapdev.R 12 | import me.yifeiyuan.flapdev.components.SimpleTextModel 13 | import java.util.* 14 | 15 | /** 16 | * Created by 程序亦非猿 on 2021/10/19. 17 | */ 18 | class PreloadTestcase : BaseTestcaseFragment() { 19 | 20 | companion object { 21 | private const val TAG = "PreloadTestcase" 22 | } 23 | 24 | private var testPreloadErrorCase = false 25 | 26 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 27 | super.onViewCreated(view, savedInstanceState) 28 | setHasOptionsMenu(true) 29 | setupPreload() 30 | 31 | recyclerView.layoutManager = linearLayoutManager 32 | recyclerView.addItemDecoration(linearItemDecoration) 33 | } 34 | 35 | private fun setupPreload() { 36 | //滑动到底部 37 | adapter.doOnPreload(offset = 0, minItemCount = 2, direction = PreloadHook.SCROLL_DOWN) { 38 | requestMoreData() 39 | } 40 | 41 | //滑动到顶部 42 | adapter.doOnPreload(offset = 2, minItemCount = 2, direction = PreloadHook.SCROLL_UP) { 43 | 44 | toast("顶部,开始预加载") 45 | 46 | Handler().postDelayed({ 47 | val list = ArrayList() 48 | val size = 5 49 | repeat(size) { 50 | list.add(SimpleTextModel("头部加载更多数据 $it of $size")) 51 | } 52 | adapter.addDataAndNotify(list) 53 | }, 300) 54 | } 55 | } 56 | 57 | private fun requestMoreData() { 58 | Log.d(TAG, "requestMoreData") 59 | if (testPreloadErrorCase) { 60 | Log.d(TAG, "预加载失败场景,必须调用 setPreloadComplete ") 61 | adapter.setPreloadComplete() // 当出错时,需要手动调用,不然不会再进行检查 62 | } else { 63 | Log.d(TAG, "onViewCreated: 开始预加载") 64 | toast("底部,开始预加载") 65 | loadMoreData() 66 | } 67 | } 68 | 69 | override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { 70 | inflater.inflate(R.menu.preload, menu) 71 | } 72 | 73 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 74 | when (item.itemId) { 75 | R.id.setLoadMoreError -> { 76 | item.isChecked = item.isChecked.not() 77 | testPreloadErrorCase = item.isChecked 78 | } 79 | R.id.setLoadMoreEnable -> { 80 | item.isChecked = item.isChecked.not() 81 | adapter.setPreloadEnable(item.isChecked) 82 | } 83 | } 84 | return super.onOptionsItemSelected(item) 85 | } 86 | } -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/ext/ItemClicksHelper.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.ext 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | import me.yifeiyuan.flap.ComponentConfig 6 | import me.yifeiyuan.flap.hook.AdapterHook 7 | 8 | 9 | typealias OnItemClickListener = ((recyclerView: RecyclerView, childView: View, position: Int) -> Unit) 10 | typealias OnItemLongClickListener = ((recyclerView: RecyclerView, childView: View, position: Int) -> Boolean) 11 | 12 | /** 13 | * 给 RecyclerView 添加 Component 级别的单击、长按事件的帮助类 14 | * 15 | * @see OnItemClickListener 16 | * @see me.yifeiyuan.flap.FlapAdapter.doOnItemClick 17 | * @see OnItemLongClickListener 18 | * @see me.yifeiyuan.flap.FlapAdapter.doOnItemLongClick 19 | * 20 | * 实现参考: 21 | * https://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/ 22 | * https://stackoverflow.com/questions/24885223/why-doesnt-recyclerview-have-onitemclicklistener 23 | * 24 | * Created by 程序亦非猿 on 2022/7/28. 25 | * 26 | * @since 3.0.0 27 | */ 28 | internal class ItemClicksHelper : RecyclerView.OnChildAttachStateChangeListener, AdapterHook { 29 | 30 | lateinit var recyclerView: RecyclerView 31 | 32 | var onItemClickListener: OnItemClickListener? = null 33 | 34 | var onItemLongClickListener: OnItemLongClickListener? = null 35 | 36 | private val internalOnClickListener = View.OnClickListener { v -> 37 | val holder: RecyclerView.ViewHolder = recyclerView.getChildViewHolder(v) 38 | if (holder is ComponentConfig && holder.isClickable()) { 39 | onItemClickListener?.invoke(recyclerView, v, holder.position) 40 | } 41 | } 42 | 43 | private val internalOnLongClickListener = View.OnLongClickListener { v -> 44 | val holder: RecyclerView.ViewHolder = recyclerView.getChildViewHolder(v) 45 | if (holder is ComponentConfig && holder.isLongClickable()) { 46 | onItemLongClickListener?.invoke(recyclerView, v, holder.position) ?: false 47 | } else { 48 | false 49 | } 50 | } 51 | 52 | override fun onChildViewAttachedToWindow(view: View) { 53 | onItemClickListener?.let { 54 | view.setOnClickListener(internalOnClickListener) 55 | } 56 | 57 | onItemLongClickListener?.let { 58 | view.setOnLongClickListener(internalOnLongClickListener) 59 | } 60 | } 61 | 62 | override fun onChildViewDetachedFromWindow(view: View) { 63 | //do nothing 64 | } 65 | 66 | override fun onAdapterAttachedToRecyclerView(adapter: RecyclerView.Adapter<*>, recyclerView: RecyclerView) { 67 | this.recyclerView = recyclerView 68 | this.recyclerView.addOnChildAttachStateChangeListener(this) 69 | } 70 | 71 | override fun onAdapterDetachedFromRecyclerView(adapter: RecyclerView.Adapter<*>, recyclerView: RecyclerView) { 72 | recyclerView.removeOnChildAttachStateChangeListener(this) 73 | } 74 | } -------------------------------------------------------------------------------- /flap-dsl-databinding/src/main/java/me/yifeiyuan/flap/dsl/databinding/AdapterDelegateDataBindingDsl.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.dsl.databinding 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.annotation.LayoutRes 6 | import androidx.databinding.DataBindingUtil 7 | import androidx.databinding.ViewDataBinding 8 | import androidx.recyclerview.widget.RecyclerView 9 | import me.yifeiyuan.flap.delegate.AdapterDelegate 10 | import me.yifeiyuan.flap.dsl.DslComponent 11 | 12 | /** 13 | * 支持 AdapterDelegate DataBinding DSL 功能 14 | * Created by 程序亦非猿 on 2022/9/20. 15 | * @since 3.1.4 16 | */ 17 | inline fun adapterDelegateDataBinding( 18 | @LayoutRes layoutId: Int, 19 | noinline dataBinding: ((layoutInflater: LayoutInflater, parent: ViewGroup) -> D) = { l, p -> DataBindingUtil.inflate(l, layoutId, p, false) }, 20 | noinline isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == T::class.java }, 21 | itemId: Long = RecyclerView.NO_ID, 22 | noinline componentInitializer: DataBindingDslComponent.() -> Unit): DataBindingDslAdapterDelegate { 23 | return DataBindingDslAdapterDelegate(T::class.java, dataBinding, layoutId, itemId, isDelegateFor, componentInitializer) 24 | } 25 | 26 | /** 27 | * 支持 AdapterDelegate DataBinding DSL 功能 28 | * Created by 程序亦非猿 on 2022/9/20. 29 | * @since 3.1.4 30 | */ 31 | class DataBindingDslAdapterDelegate( 32 | private var modelClass: Class, 33 | private var dataBinding: ((layoutInflater: LayoutInflater, parent: ViewGroup) -> D) = { l, p -> DataBindingUtil.inflate(l, layoutId, p, false) }, 34 | @LayoutRes private var layoutId: Int, 35 | private var itemId: Long = RecyclerView.NO_ID, 36 | private var isDelegateFor: ((model: Any) -> Boolean) = { m -> m.javaClass == modelClass }, 37 | private var block: DataBindingDslComponent.() -> Unit, 38 | ) : AdapterDelegate> { 39 | 40 | override fun isDelegateFor(model: Any): Boolean { 41 | return isDelegateFor.invoke(model) 42 | } 43 | 44 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup, viewType: Int): DataBindingDslComponent { 45 | val viewDataBinding = dataBinding.invoke(inflater, parent) 46 | val component = DataBindingDslComponent(viewDataBinding) 47 | block.invoke(component) 48 | return component 49 | } 50 | 51 | override fun getItemId(model: Any, position: Int): Long { 52 | return itemId 53 | } 54 | 55 | override fun getItemViewType(model: Any): Int { 56 | return layoutId 57 | } 58 | } 59 | 60 | /** 61 | * 支持 AdapterDelegate DataBinding DSL 功能 62 | * Created by 程序亦非猿 on 2022/9/20. 63 | * @since 3.1.4 64 | */ 65 | class DataBindingDslComponent(val binding: V) : DslComponent(binding.root) -------------------------------------------------------------------------------- /flap/src/main/java/me/yifeiyuan/flap/ext/ComponentBinder.kt: -------------------------------------------------------------------------------- 1 | package me.yifeiyuan.flap.ext 2 | 3 | import android.view.View 4 | import android.widget.* 5 | import androidx.recyclerview.widget.RecyclerView 6 | import me.yifeiyuan.flap.Component 7 | 8 | /** 9 | * 如果不想写 Component.findViewById 那么也可以用 Component 的 bindXXXView 来做绑定。 10 | * 11 | * 只是挑选了一些常用的基础组件提供 binder 方法,目前包含: 12 | * 13 | * @see bindView 14 | * @see bindTextView 15 | * @see bindImageView 16 | * @see bindEditText 17 | * @see bindButton 18 | * @see bindCheckBox 19 | * @see bindRecyclerView 20 | * @see bindImageButton 21 | * @see bindProgressBar 22 | * @see bindRatingBar 23 | * @see bindRadioButton 24 | * @see bindRadioGroup 25 | * 26 | * Created by 程序亦非猿 on 2021/9/29. 27 | * @since 3.0.0 28 | */ 29 | 30 | fun Component<*>.bindView(viewId: Int, binder: V.() -> Unit) { 31 | val view = itemView.findViewById(viewId) 32 | view.binder() 33 | } 34 | 35 | fun Component<*>.bindTextView(viewId: Int, binder: TextView.() -> Unit) { 36 | val view = itemView.findViewById(viewId) 37 | view.binder() 38 | } 39 | 40 | fun Component<*>.setText(viewId: Int, text: CharSequence?) { 41 | itemView.findViewById(viewId).text = text 42 | } 43 | 44 | fun Component<*>.bindButton(viewId: Int, binder: Button.() -> Unit) { 45 | val textView = itemView.findViewById