Repository: CymChad/BaseRecyclerViewAdapterHelper Branch: master Commit: c4d7e43c6e5a Files: 177 Total size: 379.3 KB Directory structure: gitextract_07v8rp8z/ ├── .circleci/ │ └── config.yml ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── bug_report.md │ └── pull_request_template.md ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── chad/ │ │ └── baserecyclerviewadapterhelper/ │ │ ├── MyApplication.kt │ │ ├── activity/ │ │ │ ├── WelcomeActivity.java │ │ │ ├── animation/ │ │ │ │ ├── AnimationUseActivity.kt │ │ │ │ └── adapter/ │ │ │ │ └── AnimationAdapter.kt │ │ │ ├── databinding/ │ │ │ │ ├── DataBindingUseActivity.java │ │ │ │ └── adapter/ │ │ │ │ └── DataBindingAdapter.java │ │ │ ├── differ/ │ │ │ │ ├── DifferActivity.java │ │ │ │ └── adapter/ │ │ │ │ ├── DiffEntityCallback.java │ │ │ │ └── DiffUtilAdapter.java │ │ │ ├── dragswipe/ │ │ │ │ ├── DefaultDragAndSwipeActivity.kt │ │ │ │ ├── DragAndSwipeDifferActivity.kt │ │ │ │ ├── DragAndSwipeUseActivity.java │ │ │ │ ├── HeaderDragAndSwipe.kt │ │ │ │ ├── HeaderDragAndSwipeActivity.kt │ │ │ │ ├── ManualDragAndSwipeUseActivity.java │ │ │ │ └── adapter/ │ │ │ │ ├── DiffDragAndSwipeAdapter.kt │ │ │ │ ├── DragAndSwipeAdapter.java │ │ │ │ └── HeaderDragAndSwipeAdapter.kt │ │ │ ├── emptyview/ │ │ │ │ ├── EmptyViewUseActivity.kt │ │ │ │ └── adapter/ │ │ │ │ └── EmptyViewAdapter.kt │ │ │ ├── headerfooter/ │ │ │ │ ├── HeaderAndFooterUseActivity.kt │ │ │ │ └── adapter/ │ │ │ │ ├── FooterAdapter.kt │ │ │ │ ├── HeaderAdapter.kt │ │ │ │ └── HeaderAndFooterAdapter.kt │ │ │ ├── home/ │ │ │ │ ├── HomeActivity.kt │ │ │ │ └── adapter/ │ │ │ │ ├── HomeAdapter.kt │ │ │ │ └── HomeTopHeaderAdapter.kt │ │ │ ├── itemclick/ │ │ │ │ ├── ItemClickActivity.kt │ │ │ │ └── adapter/ │ │ │ │ └── ItemClickAdapter.java │ │ │ ├── loadmore/ │ │ │ │ ├── AutoLoadMoreRefreshUseActivity.kt │ │ │ │ ├── NoAutoAutoLoadMoreRefreshUseActivity.kt │ │ │ │ └── adapter/ │ │ │ │ ├── CustomLoadMoreAdapter.kt │ │ │ │ └── RecyclerViewAdapter.kt │ │ │ ├── node/ │ │ │ │ ├── NodeActivity.kt │ │ │ │ └── adapter/ │ │ │ │ └── NodeAdapter.kt │ │ │ ├── scene/ │ │ │ │ ├── GroupDemoActivity.kt │ │ │ │ └── adapter/ │ │ │ │ └── GroupAdapter.kt │ │ │ └── upfetch/ │ │ │ ├── UpFetchUseActivity.kt │ │ │ └── adapter/ │ │ │ └── UpFetchAdapter.kt │ │ ├── animator/ │ │ │ ├── CustomAnimation1.java │ │ │ ├── CustomAnimation2.java │ │ │ └── CustomAnimation3.java │ │ ├── base/ │ │ │ ├── BaseActivity.kt │ │ │ └── BaseViewBindingActivity.kt │ │ ├── data/ │ │ │ └── DataServer.kt │ │ ├── decoration/ │ │ │ ├── GridItemDecoration.java │ │ │ └── GridSectionAverageGapItemDecoration.java │ │ ├── entity/ │ │ │ ├── ClickEntity.java │ │ │ ├── DiffEntity.java │ │ │ ├── GroupDemoEntity.kt │ │ │ ├── HomeEntity.kt │ │ │ ├── Movie.java │ │ │ ├── MoviePresenter.java │ │ │ ├── NodeEntity.kt │ │ │ └── Status.java │ │ ├── utils/ │ │ │ ├── AppUtils.kt │ │ │ ├── ClickableMovementMethod.java │ │ │ ├── Ext.kt │ │ │ ├── Tips.java │ │ │ └── VibratorUtils.kt │ │ └── widget/ │ │ └── BRVAHToolbar.kt │ └── res/ │ ├── anim/ │ │ ├── item_animation_from_bottom.xml │ │ └── layout_animation_from_bottom.xml │ ├── drawable/ │ │ ├── actionbar_bottom_bg.xml │ │ ├── brvah_sample_footer_loading_progress.xml │ │ ├── custom_text_state_color.xml │ │ ├── gv_up_fetch.xml │ │ ├── ic_node_down.xml │ │ ├── ic_node_right.xml │ │ ├── selector_item_child.xml │ │ ├── shape_right_top_float_bg.xml │ │ ├── thumb_drawable.xml │ │ └── touch_bg.xml │ ├── drawable-v21/ │ │ └── touch_bg.xml │ ├── layout/ │ │ ├── activity_animation_use.xml │ │ ├── activity_choose_multiple_item_use_type.xml │ │ ├── activity_choose_node_use_type.xml │ │ ├── activity_diffutil.xml │ │ ├── activity_empty_view_use.xml │ │ ├── activity_home.xml │ │ ├── activity_load_more.xml │ │ ├── activity_node.xml │ │ ├── activity_universal_recycler.xml │ │ ├── activity_welcome.xml │ │ ├── def_section_head.xml │ │ ├── empty_view.xml │ │ ├── error_view.xml │ │ ├── footer_view.xml │ │ ├── head_view.xml │ │ ├── home_item_view.xml │ │ ├── item_click_childview.xml │ │ ├── item_click_view.xml │ │ ├── item_draggable_view.xml │ │ ├── item_group_type.xml │ │ ├── item_header_and_footer.xml │ │ ├── item_image_view.xml │ │ ├── item_img_text_view.xml │ │ ├── item_long_click_childview.xml │ │ ├── item_long_click_view.xml │ │ ├── item_movie.xml │ │ ├── item_node_level_1.xml │ │ ├── item_node_level_2.xml │ │ ├── item_node_level_3.xml │ │ ├── item_section_content.xml │ │ ├── layout_animation.xml │ │ ├── layout_title_bar.xml │ │ ├── layout_tool_bar.xml │ │ ├── loading_view.xml │ │ ├── node_footer.xml │ │ ├── toolbar_layout.xml │ │ ├── top_view.xml │ │ └── view_load_more.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-v21/ │ │ └── styles.xml │ └── values-zh/ │ └── strings.xml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── library/ │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── chad/ │ │ └── library/ │ │ └── adapter4/ │ │ ├── BaseDifferAdapter.kt │ │ ├── BaseMultiItemAdapter.kt │ │ ├── BaseNodeAdapter.kt │ │ ├── BaseQuickAdapter.kt │ │ ├── BaseSingleItemAdapter.kt │ │ ├── QuickAdapterHelper.kt │ │ ├── animation/ │ │ │ ├── AlphaInAnimation.kt │ │ │ ├── ItemAnimator.kt │ │ │ ├── ScaleInAnimation.kt │ │ │ ├── SlideInBottomAnimation.kt │ │ │ ├── SlideInLeftAnimation.kt │ │ │ └── SlideInRightAnimation.kt │ │ ├── dragswipe/ │ │ │ ├── DragSwipeExt.kt │ │ │ ├── QuickDragAndSwipe.kt │ │ │ └── listener/ │ │ │ ├── DragAndSwipeDataCallback.kt │ │ │ ├── OnItemDragListener.java │ │ │ └── OnItemSwipeListener.java │ │ ├── fullspan/ │ │ │ └── FullSpanAdapterType.kt │ │ ├── layoutmanager/ │ │ │ └── QuickGridLayoutManager.kt │ │ ├── loadState/ │ │ │ ├── LoadState.kt │ │ │ ├── LoadStateAdapter.kt │ │ │ ├── leading/ │ │ │ │ ├── DefaultLeadingLoadStateAdapter.kt │ │ │ │ └── LeadingLoadStateAdapter.kt │ │ │ └── trailing/ │ │ │ ├── DefaultTrailingLoadStateAdapter.kt │ │ │ └── TrailingLoadStateAdapter.kt │ │ ├── util/ │ │ │ ├── AdapterUtils.kt │ │ │ └── ItemClickUtils.kt │ │ └── viewholder/ │ │ ├── DataBindingHolder.java │ │ ├── QuickViewHolder.kt │ │ └── StateLayoutVH.kt │ └── res/ │ ├── layout/ │ │ ├── brvah_leading_load_more.xml │ │ └── brvah_trailing_load_more.xml │ ├── values/ │ │ ├── ids.xml │ │ └── strings.xml │ ├── values-en/ │ │ └── strings.xml │ ├── values-zh-rHK/ │ │ └── strings.xml │ └── values-zh-rTW/ │ └── strings.xml └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .circleci/config.yml ================================================ version: 2 jobs: build: working_directory: ~/code docker: - image: cimg/android:2023.04 environment: JVM_OPTS: -Xmx3200m steps: - checkout - restore_cache: key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} - run: name: Download Dependencies command: ./gradlew androidDependencies - save_cache: paths: - ~/.gradle key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} - run: name: Run Tests command: ./gradlew lint test - store_artifacts: path: app/build/reports destination: reports - store_test_results: path: app/build/test-results ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. 1. 尝试在[历史问题](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues?q=is%3Aissue+is%3Aclosed)搜索答案。 2. 尝试阅读[文档](http://www.jianshu.com/p/b343fcff51b0)找到答案。 3. 尝试阅读[Demo](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)找到答案。 4. 尝试自己检查或试验以找到答案。 5. 尝试阅读源代码以找到答案。 6. 请勿将产品的一些特殊交互需求 和 该库暂不支持作为bug混为一谈,请您仔细甄别 如果以上都尝试过了请提一个新的[issues](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues/new) 参考[提問的智慧](https://github.com/FredWe/How-To-Ask-Questions-The-Smart-Way) 如果还是没有找到答案,提问请带上这几个必要信息 1. 当前使用的版本号 2. 复现操作描述 3. 使用代码 3. crash日志 4. gif复现效果 5. 抽取demo **将你出现的问题代码抽出来成一个可直接运行的项目。(最好fork[本库](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)修改)** **在本地修改demo,然后把commit push到github上,在issue里贴下demo的地址。** **有详细的描述才能使得我们更快速的定位问题并解决问题,感谢配合!** ================================================ FILE: .github/pull_request_template.md ================================================ Thank you for contributing to BaseRecyclerViewAdapterHelper. Before pressing the "Create Pull Request" button, please consider the following points: - [1] Please give a description about what and why you are contributing, even if it's trivial. - [2] Please include the issue list number(s) or other PR numbers in the description if you are contributing in response to those. - [3] Please include a reasonable set of demo tests if you contribute new code or change an existing one. please make sure you have demo for working correctly. ================================================ FILE: .gitignore ================================================ #///////////////////////////////////////////////////////////////////////////// # OS generated files #///////////////////////////////////////////////////////////////////////////// .DS_Store ehthumbs.db Thumbs.db # Built application files *.apk *.ap_ # Files for the Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Android Studio project files *.iml .gradle .idea build ================================================ FILE: .travis.yml ================================================ language: android dist: trusty jdk: oraclejdk11 sudo: false android: components: - tools - platform-tools - build-tools-33.0.0 - android-32 - extra-android-m2repository before_install: - chmod +x gradlew - mkdir "$ANDROID_HOME/licenses" || true # Hack to accept Android licenses - yes | sdkmanager "platforms;android-32" script: - ./gradlew assembleRelease ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 陈宇明 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ![](https://user-images.githubusercontent.com/7698209/33198075-ef8f2230-d123-11e7-85a3-4cb9b22f877d.png) [![](https://img.shields.io/maven-central/v/io.github.cymchad/BaseRecyclerViewAdapterHelper4)](https://repo.maven.apache.org/maven2/io/github/cymchad/BaseRecyclerViewAdapterHelper4/) [![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=16) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-BaseRecyclerViewAdapterHelper-green.svg?style=true)](https://android-arsenal.com/details/1/3644) [![CircleCI](https://circleci.com/gh/CymChad/BaseRecyclerViewAdapterHelper/tree/master.svg?style=svg)](https://circleci.com/gh/CymChad/BaseRecyclerViewAdapterHelper/tree/master) [![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS4zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/CymChad/BaseRecyclerViewAdapterHelper) [![](https://img.shields.io/badge/%E4%BD%9C%E8%80%85-%E9%99%88%E5%AE%87%E6%98%8E-7AD6FD.svg)](https://mp.weixin.qq.com/s/U4QAPlu5WDm8U5Ljc7TuAQ) [![](https://img.shields.io/badge/%E4%BD%9C%E8%80%85-limuyang2-7AD6FD)](https://github.com/limuyang2) # BRVAH Powerful and flexible RecyclerView Adapter, Please feel free to use this. (Welcome to **Star** and **Fork**) 强大而灵活的RecyclerView Adapter(欢迎 **Star** 和 **Fork**) ​ 新版4.x.x已发布,完美兼容`ConcatAdapter`,解决了许多遗留问题,拆分了功能模块,BaseAdapter更加简洁干净。“多类型布局”更加灵活。向上、向下加载得到极大加强。 v4版本已经上传 maven 中央仓库,不需要再引入三方仓库配置了。欢迎尝试。 Of course, you can continue to use the [2.x](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/tree/2.x) version. 当然,你也可以继续使用[2.x](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/tree/2.x) 版本、[3.x](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/blob/3.x/readme/0-BaseRecyclerViewAdapterHelper.md)版本。 # Document - English Writing ... - [3.0版本 中文](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/blob/3.x/readme/0-BaseRecyclerViewAdapterHelper.md) - [4.0版本 中文](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/wiki) (由于各位项目成员工作较为繁忙,请各位同学谅解) ## v4 版本 [wiki](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/wiki) ``` implementation("io.github.cymchad:BaseRecyclerViewAdapterHelper4:4.3.4") ``` ## [demo](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/tree/master/demo) # proguard-rules.pro > 此资源库自带混淆规则,并且会自动导入,正常情况下无需手动导入。 > The library comes with `proguard-rules.pro` rules and is automatically imported. Normally no manual import is required. > You can also go here to view [proguard-rules](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/blob/master/library/proguard-rules.pro) # Thanks [JoanZapata / base-adapter-helper](https://github.com/JoanZapata/base-adapter-helper) # [License](https://github.com/CymChad/BaseRecyclerViewAdapterHelper/blob/master/LICENSE) ================================================ FILE: app/.gitignore ================================================ .gradle/ .DS_Store local.properties # build files build/ bin/ gen/ output/ # android studio *.iml .idea ================================================ FILE: app/build.gradle ================================================ plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' id 'com.google.devtools.ksp' } android { compileSdk 36 defaultConfig { applicationId "com.chad.baserecyclerviewadapterhelper" minSdk 23 targetSdk 36 versionCode 21 versionName "4.3.2" } buildTypes { release { minifyEnabled true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } debug { minifyEnabled false zipAlignEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } buildFeatures { viewBinding = true dataBinding = true } namespace 'com.chad.baserecyclerviewadapterhelper' } dependencies { implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') implementation project(path: ':library') implementation 'com.google.android.material:material:1.13.0' implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.appcompat:appcompat:1.7.1' implementation "androidx.core:core-ktx:1.17.0" implementation 'com.kyleduo.switchbutton:library:2.1.0' implementation 'androidx.recyclerview:recyclerview:1.4.0' implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation("com.squareup.moshi:moshi:1.15.2") ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2") implementation 'com.jaredrummler:material-spinner:1.3.1' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/huasheng/Desktop/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} #BaseRecyclerViewAdapterHelper ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/MyApplication.kt ================================================ /* ******************************* Copyright (c)*********************************\ ** ** (c) Copyright 2015, Allen, china, shanghai ** All Rights Reserved ** ** ** **-----------------------------------版本信息------------------------------------ ** 版 本: V0.1 ** **------------------------------------------------------------------------------ ********************************End of Head************************************\ */ package com.chad.baserecyclerviewadapterhelper import android.app.Application import com.chad.baserecyclerviewadapterhelper.utils.AppUtils /** * 文 件 名: MyApplication * 创 建 人: Allen * 创建日期: 16/12/24 15:33 * 邮 箱: AllenCoder@126.com * 修改时间: * 修改备注: */ class MyApplication : Application() { override fun onCreate() { super.onCreate() AppUtils.init(this) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/WelcomeActivity.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import androidx.appcompat.app.AppCompatActivity; import com.chad.baserecyclerviewadapterhelper.R; import com.chad.baserecyclerviewadapterhelper.activity.home.HomeActivity; public class WelcomeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_welcome); new Handler().postDelayed(new Runnable() { @Override public void run() { Intent intent = new Intent(WelcomeActivity.this, HomeActivity.class); startActivity(intent); finish(); } }, 1000); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/animation/AnimationUseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.animation import android.os.Bundle import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import com.chad.baserecyclerviewadapterhelper.activity.animation.adapter.AnimationAdapter import com.chad.baserecyclerviewadapterhelper.animator.CustomAnimation1 import com.chad.baserecyclerviewadapterhelper.animator.CustomAnimation2 import com.chad.baserecyclerviewadapterhelper.animator.CustomAnimation3 import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityAnimationUseBinding import com.chad.library.adapter4.BaseQuickAdapter /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper * * * modify by AllenCoder */ class AnimationUseActivity : BaseViewBindingActivity() { private val mAnimationAdapter: AnimationAdapter = AnimationAdapter().apply { // 打开 Adapter 的动画 animationEnable = true // 是否是首次显示时候加载动画 isAnimationFirstOnly = false } override fun initBinding(): ActivityAnimationUseBinding { return ActivityAnimationUseBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Animation Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.adapter = mAnimationAdapter initMenu() } /** * Init menu * 初始化下拉菜单 */ private fun initMenu() { viewBinding.spinner.setItems( "AlphaIn", "ScaleIn", "SlideInBottom", "SlideInLeft", "SlideInRight", "Custom1", "Custom2", "Custom3" ) viewBinding.spinner.setOnItemSelectedListener { _, position, _, _ -> when (position) { 0 -> mAnimationAdapter.setItemAnimation(BaseQuickAdapter.AnimationType.AlphaIn) 1 -> mAnimationAdapter.setItemAnimation(BaseQuickAdapter.AnimationType.ScaleIn) 2 -> mAnimationAdapter.setItemAnimation(BaseQuickAdapter.AnimationType.SlideInBottom) 3 -> mAnimationAdapter.setItemAnimation(BaseQuickAdapter.AnimationType.SlideInLeft) 4 -> mAnimationAdapter.setItemAnimation(BaseQuickAdapter.AnimationType.SlideInRight) 5 -> mAnimationAdapter.itemAnimation = CustomAnimation1() 6 -> mAnimationAdapter.itemAnimation = CustomAnimation2() 7 -> mAnimationAdapter.itemAnimation = CustomAnimation3() else -> {} } mAnimationAdapter.notifyDataSetChanged() } //init firstOnly state viewBinding.switchButton.setOnCheckedChangeListener { _, isChecked -> mAnimationAdapter.isAnimationFirstOnly = isChecked mAnimationAdapter.notifyDataSetChanged() } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/animation/adapter/AnimationAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.animation.adapter import android.content.Context import android.text.TextPaint import android.text.style.ClickableSpan import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.entity.Status import com.chad.baserecyclerviewadapterhelper.utils.ClickableMovementMethod import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder /** * 文 件 名: AnimationAdapter * 创 建 人: Allen * 创建日期: 16/12/24 15:33 * 邮 箱: AllenCoder@126.com * 修改时间: * 修改备注: */ class AnimationAdapter : BaseQuickAdapter(DataServer.getSampleData(100)) { override fun onCreateViewHolder( context: Context, parent: ViewGroup, viewType: Int ): QuickViewHolder { return QuickViewHolder(R.layout.layout_animation, parent) } override fun onBindViewHolder(holder: QuickViewHolder, position: Int, item: Status?) { when (holder.layoutPosition % 3) { 0 -> holder.setImageResource(R.id.img, R.mipmap.animation_img1) 1 -> holder.setImageResource(R.id.img, R.mipmap.animation_img2) 2 -> holder.setImageResource(R.id.img, R.mipmap.animation_img3) else -> {} } holder.setText(R.id.tweetName, "Hoteis in Rio de Janeiro") val msg = "\"He was one of Australia's most of distinguished artistes, renowned for his portraits\"" holder.getView(R.id.tweetText).text = buildSpannedString { append(msg) inSpans(clickableSpan) { append("landscapes and nedes") } } holder.getView(R.id.tweetText).movementMethod = ClickableMovementMethod.getInstance() holder.getView(R.id.tweetText).isFocusable = false holder.getView(R.id.tweetText).isClickable = false holder.getView(R.id.tweetText).isLongClickable = false } private val clickableSpan: ClickableSpan = object : ClickableSpan() { override fun onClick(widget: View) { Tips.show("事件触发了 landscapes and nedes") } override fun updateDrawState(ds: TextPaint) { ds.color = ContextCompat.getColor(context, R.color.clickspan_color) ds.isUnderlineText = true } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/databinding/DataBindingUseActivity.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.databinding; import android.os.Bundle; import android.view.View; import androidx.annotation.NonNull; import androidx.core.graphics.Insets; import androidx.core.view.OnApplyWindowInsetsListener; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.recyclerview.widget.LinearLayoutManager; import com.chad.baserecyclerviewadapterhelper.activity.databinding.adapter.DataBindingAdapter; import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity; import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding; import com.chad.baserecyclerviewadapterhelper.entity.Movie; import com.chad.baserecyclerviewadapterhelper.utils.Tips; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * @author limuyang * @date 2019-12-05 * @description */ public final class DataBindingUseActivity extends BaseViewBindingActivity { private final DataBindingAdapter adapter = new DataBindingAdapter(); @NonNull @Override public ActivityUniversalRecyclerBinding initBinding() { return ActivityUniversalRecyclerBinding.inflate(getLayoutInflater()); } @Override public void onPointerCaptureChanged(boolean hasCapture) { super.onPointerCaptureChanged(hasCapture); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewCompat.setOnApplyWindowInsetsListener(getViewBinding().getRoot(), new OnApplyWindowInsetsListener() { @Override public @NonNull WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { Insets bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()); getViewBinding().titleBar.updateFakeBarHeight(bar.top); return insets; } }); getViewBinding().titleBar.setTitle("DataBinding Use"); getViewBinding().titleBar.setOnBackListener(v -> finish()); getViewBinding().rv.setLayoutManager(new LinearLayoutManager(this)); getViewBinding().rv.setAdapter(adapter); //item 点击事件 adapter.setOnItemClickListener((movieBaseQuickAdapter, view, position) -> { Tips.show("onItemClick: " + position); }); //设置数据 adapter.submitList(genData()); } private List genData() { ArrayList list = new ArrayList<>(); Random random = new Random(); for (int i = 0; i < 10; i++) { String name = "Chad " + i; int price = random.nextInt(10) + 10; int len = random.nextInt(80) + 60; Movie movie = new Movie(name, len, price, "He was one of Australia's most distinguished artistes"); list.add(movie); } return list; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/databinding/adapter/DataBindingAdapter.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.databinding.adapter; import android.content.Context; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.chad.baserecyclerviewadapterhelper.R; import com.chad.baserecyclerviewadapterhelper.databinding.ItemMovieBinding; import com.chad.baserecyclerviewadapterhelper.entity.Movie; import com.chad.baserecyclerviewadapterhelper.entity.MoviePresenter; import com.chad.library.adapter4.BaseQuickAdapter; import com.chad.library.adapter4.viewholder.DataBindingHolder; /** * @author: limuyang * @date: 2019-12-05 * @Description: DataBinding Adapter * */ public class DataBindingAdapter extends BaseQuickAdapter> { private final MoviePresenter mPresenter = new MoviePresenter(); @NonNull @Override protected DataBindingHolder onCreateViewHolder(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new DataBindingHolder<>(R.layout.item_movie, parent); } @Override protected void onBindViewHolder(@NonNull DataBindingHolder holder, int position, @Nullable Movie item) { if (item == null) return; // 获取 Binding ItemMovieBinding binding = holder.getBinding(); binding.setMovie(item); binding.setPresenter(mPresenter); binding.executePendingBindings(); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/differ/DifferActivity.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.differ; import android.os.Bundle; import android.view.View; import androidx.annotation.NonNull; import androidx.core.graphics.Insets; import androidx.core.view.OnApplyWindowInsetsListener; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import com.chad.baserecyclerviewadapterhelper.R; import com.chad.baserecyclerviewadapterhelper.activity.differ.adapter.DiffUtilAdapter; import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity; import com.chad.baserecyclerviewadapterhelper.data.DataServer; import com.chad.baserecyclerviewadapterhelper.databinding.ActivityDiffutilBinding; import com.chad.baserecyclerviewadapterhelper.entity.DiffEntity; import java.util.ArrayList; import java.util.List; /** * Created by limuyang * Date: 2019/7/14O */ public final class DifferActivity extends BaseViewBindingActivity { private final DiffUtilAdapter mAdapter = new DiffUtilAdapter(); @NonNull @Override public ActivityDiffutilBinding initBinding() { return ActivityDiffutilBinding.inflate(getLayoutInflater()); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewCompat.setOnApplyWindowInsetsListener(getViewBinding().getRoot(), new OnApplyWindowInsetsListener() { @Override public @NonNull WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { Insets bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()); getViewBinding().titleBar.updateFakeBarHeight(bar.top); return insets; } }); getViewBinding().titleBar.setTitle("DiffUtil Use"); getViewBinding().titleBar.setOnBackListener(v -> finish()); initRv(); initClick(); } @Override protected void onStart() { super.onStart(); // 增加延迟,模拟网络加载 getViewBinding().diffRv.postDelayed(() -> mAdapter.submitList(DataServer.getDiffUtilDemoEntities()), 1500); } private void initRv() { // 打开空布局功能 mAdapter.setStateViewEnable(true); // 传入 空布局 layout id mAdapter.setStateViewLayout(this, R.layout.loading_view); getViewBinding().diffRv.setAdapter(mAdapter); } private int idAdd = 0; private void initClick() { getViewBinding().btnChange.setOnClickListener(v -> { List newData = getNewList(); mAdapter.submitList(newData); }); getViewBinding().btnAdd.setOnClickListener(v -> { mAdapter.add(2, new DiffEntity( 1000 + idAdd, "add - 😊😊Item " + 1000 + idAdd, "Item " + 0 + " content have change (notifyItemChanged)", "06-12")); idAdd++; }); getViewBinding().btnRemove.setOnClickListener(v -> { if (2 >= mAdapter.getItems().size()) { return; } mAdapter.removeAt(2); }); } /** * get new data */ private List getNewList() { List list = new ArrayList<>(); for (int i = 0; i < 10; i++) { /* Simulate deletion of data No. 1 and No. 3 模拟删除1号和3号数据 */ if (i == 1 || i == 3) { continue; } /* Simulate modification title of data No. 0 模拟修改0号数据的title */ if (i == 0) { list.add(new DiffEntity( i, "😊Item " + i, "This item " + i + " content", "06-12") ); continue; } /* Simulate modification content of data No. 4 模拟修改4号数据的content发生变化 */ if (i == 4) { list.add(new DiffEntity( i, "Item " + i, "Oh~~~~~~, Item " + i + " content have change", "06-12") ); continue; } list.add(new DiffEntity( i, "Item " + i, "This item " + i + " content", "06-12") ); } return list; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/differ/adapter/DiffEntityCallback.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.differ.adapter; import androidx.annotation.NonNull; import androidx.recyclerview.widget.DiffUtil; import com.chad.baserecyclerviewadapterhelper.entity.DiffEntity; /** * Create DiffCallback */ public class DiffEntityCallback extends DiffUtil.ItemCallback { /** * Determine if it is the same item *

* 判断是否是同一个item * * @param oldItem New data * @param newItem old Data * @return */ @Override public boolean areItemsTheSame(@NonNull DiffEntity oldItem, @NonNull DiffEntity newItem) { return oldItem.getId() == newItem.getId(); } /** * When it is the same item, judge whether the content has changed. *

* 当是同一个item时,再判断内容是否发生改变 * * @param oldItem New data * @param newItem old Data * @return */ @Override public boolean areContentsTheSame(@NonNull DiffEntity oldItem, @NonNull DiffEntity newItem) { return oldItem.getTitle().equals(newItem.getTitle()) && oldItem.getContent().equals(newItem.getContent()) && oldItem.getDate().equals(newItem.getDate()); } /** * Optional implementation * Implement this method if you need to precisely modify the content of a view. * If this method is not implemented, or if null is returned, the entire item will be refreshed. * * 可选实现 * 如果需要精确修改某一个view中的内容,请实现此方法。 * 如果不实现此方法,或者返回null,将会直接刷新整个item。 * * @param oldItem Old data * @param newItem New data * @return Payload info. if return null, the entire item will be refreshed. */ @Override public Object getChangePayload(@NonNull DiffEntity oldItem, @NonNull DiffEntity newItem) { return null; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/differ/adapter/DiffUtilAdapter.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.differ.adapter; import android.content.Context; import android.view.ViewGroup; import androidx.annotation.NonNull; import com.chad.baserecyclerviewadapterhelper.R; import com.chad.baserecyclerviewadapterhelper.entity.DiffEntity; import com.chad.library.adapter4.BaseQuickAdapter; import com.chad.library.adapter4.viewholder.QuickViewHolder; /** * Create adapter */ public class DiffUtilAdapter extends BaseQuickAdapter { public DiffUtilAdapter() { super(new DiffEntityCallback()); } @NonNull @Override protected QuickViewHolder onCreateViewHolder(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new QuickViewHolder(R.layout.layout_animation, parent); } @Override protected void onBindViewHolder(@NonNull QuickViewHolder holder, int position, DiffEntity item) { holder.setText(R.id.tweetName, item.getTitle()) .setText(R.id.tweetText, item.getContent()) .setText(R.id.tweetDate, item.getDate()); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/DefaultDragAndSwipeActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe import android.animation.ValueAnimator import android.graphics.Canvas import android.graphics.Color import android.os.Build import android.os.Bundle import android.util.Log import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter.DragAndSwipeAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.baserecyclerviewadapterhelper.utils.vibrate import com.chad.library.adapter4.dragswipe.QuickDragAndSwipe import com.chad.library.adapter4.dragswipe.listener.OnItemDragListener import com.chad.library.adapter4.dragswipe.listener.OnItemSwipeListener import com.chad.library.adapter4.viewholder.QuickViewHolder /** * 默认实现拖动与侧滑效果 * Drag and Drag effects are implemented by default */ class DefaultDragAndSwipeActivity : BaseViewBindingActivity() { private val mAdapter: DragAndSwipeAdapter = DragAndSwipeAdapter() private val quickDragAndSwipe = QuickDragAndSwipe() .setDragMoveFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) .setSwipeMoveFlags(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Default Drag And Swipe" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.layoutManager = GridLayoutManager(this,3) viewBinding.rv.adapter = mAdapter val mData = generateData(50) mAdapter.submitList(mData) // 拖拽监听 val listener: OnItemDragListener = object : OnItemDragListener { override fun onItemDragStart(viewHolder: RecyclerView.ViewHolder?, pos: Int) { vibrate() Log.d(TAG, "drag start") val holder = viewHolder as QuickViewHolder? ?: return // 开始时,item背景色变化,demo这里使用了一个动画渐变,使得自然 val startColor = Color.WHITE val endColor = Color.rgb(245, 245, 245) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val v = ValueAnimator.ofArgb(startColor, endColor) v.addUpdateListener { animation: ValueAnimator -> holder.itemView.setBackgroundColor( animation.animatedValue as Int ) } v.duration = 300 v.start() } } override fun onItemDragMoving( source: RecyclerView.ViewHolder, from: Int, target: RecyclerView.ViewHolder, to: Int ) { Log.d( TAG, "move from: " + source.bindingAdapterPosition + " to: " + target.bindingAdapterPosition ) } override fun onItemDragEnd(viewHolder: RecyclerView.ViewHolder, pos: Int) { Log.d(TAG, "drag end") val holder = viewHolder as QuickViewHolder // 结束时,item背景色变化,demo这里使用了一个动画渐变,使得自然 val startColor = Color.rgb(245, 245, 245) val endColor = Color.WHITE // 动画 val v = ValueAnimator.ofArgb(startColor, endColor) v.addUpdateListener { animation: ValueAnimator -> holder.itemView.setBackgroundColor( animation.animatedValue as Int ) } v.duration = 300 v.start() mAdapter.items.forEach { Log.d( TAG, "-------->> w 顺序 ${it} " ) } } } val swipeListener: OnItemSwipeListener = object : OnItemSwipeListener { override fun onItemSwipeStart(viewHolder: RecyclerView.ViewHolder?, bindingAdapterPosition: Int) { Log.d(TAG, "onItemSwipeStart") } override fun onItemSwipeEnd(viewHolder: RecyclerView.ViewHolder, bindingAdapterPosition: Int) { Log.d(TAG, "onItemSwipeEnd: " + bindingAdapterPosition) } override fun onItemSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int, bindingAdapterPosition: Int) { Log.d(TAG, "onItemSwiped") } override fun onItemSwipeMoving( canvas: Canvas, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, isCurrentlyActive: Boolean ) { Log.d(TAG, "onItemSwipeMoving") } } // 滑动事件 quickDragAndSwipe.attachToRecyclerView(viewBinding.rv) .setDataCallback(mAdapter) .setItemDragListener(listener) .setItemSwipeListener(swipeListener) // 点击事件 mAdapter.setOnItemClickListener { adapter, view, position -> Tips.show("点击了:$position") } } private fun generateData(size: Int): List { val data = ArrayList(size) for (i in 0 until size) { data.add("item $i") } return data } companion object { private const val TAG = "Default Drag And Swipe" } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/DragAndSwipeDifferActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe import android.animation.ValueAnimator import android.graphics.Canvas import android.graphics.Color import android.os.Build import android.os.Bundle import android.util.Log import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter.DiffDragAndSwipeAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.utils.vibrate import com.chad.library.adapter4.dragswipe.QuickDragAndSwipe import com.chad.library.adapter4.dragswipe.listener.DragAndSwipeDataCallback import com.chad.library.adapter4.dragswipe.listener.OnItemDragListener import com.chad.library.adapter4.dragswipe.listener.OnItemSwipeListener import com.chad.library.adapter4.viewholder.QuickViewHolder /** * Created by limuyang * Date: 2019/7/14O * * DiffAdapter DragAndSwipe */ class DragAndSwipeDifferActivity : BaseViewBindingActivity() { private var mAdapter: DiffDragAndSwipeAdapter = DiffDragAndSwipeAdapter() private var quickDragAndSwipe = QuickDragAndSwipe() .setDragMoveFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN) .setSwipeMoveFlags(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Diff Drag Swipe Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.layoutManager = LinearLayoutManager(this) initRv() initDrag() } private fun initRv() { viewBinding.rv.adapter = mAdapter mAdapter.submitList(DataServer.diffUtilDemoEntities) } private fun initDrag() { // 拖拽监听 val listener: OnItemDragListener = object : OnItemDragListener { override fun onItemDragStart(viewHolder: RecyclerView.ViewHolder?, pos: Int) { vibrate() Log.d(TAG, "drag start") val holder = viewHolder as QuickViewHolder // 开始时,item背景色变化,demo这里使用了一个动画渐变,使得自然 val startColor = Color.WHITE val endColor = Color.rgb(245, 245, 245) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val v = ValueAnimator.ofArgb(startColor, endColor) v.addUpdateListener { animation: ValueAnimator -> holder.itemView.setBackgroundColor( animation.animatedValue as Int ) } v.duration = 300 v.start() } } override fun onItemDragMoving( source: RecyclerView.ViewHolder, from: Int, target: RecyclerView.ViewHolder, to: Int ) { Log.d( TAG, "move from: " + source.bindingAdapterPosition + " to: " + target.bindingAdapterPosition ) } override fun onItemDragEnd(viewHolder: RecyclerView.ViewHolder, pos: Int) { Log.d(TAG, "drag end") val holder = viewHolder as QuickViewHolder // 结束时,item背景色变化,demo这里使用了一个动画渐变,使得自然 val startColor = Color.rgb(245, 245, 245) val endColor = Color.WHITE if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val v = ValueAnimator.ofArgb(startColor, endColor) v.addUpdateListener { animation: ValueAnimator -> holder.itemView.setBackgroundColor( animation.animatedValue as Int ) } v.duration = 300 v.start() } } } val swipeListener: OnItemSwipeListener = object : OnItemSwipeListener { override fun onItemSwipeStart(viewHolder: RecyclerView.ViewHolder?, bindingAdapterPosition: Int) { Log.d(TAG, "onItemSwipeStart") } override fun onItemSwipeEnd(viewHolder: RecyclerView.ViewHolder, bindingAdapterPosition: Int) { Log.d(TAG, "onItemSwipeEnd") } override fun onItemSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int, bindingAdapterPosition: Int) { Log.d(TAG, "onItemSwiped") } override fun onItemSwipeMoving( canvas: Canvas, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, isCurrentlyActive: Boolean ) { Log.d(TAG, "onItemSwipeMoving") } } quickDragAndSwipe.attachToRecyclerView(viewBinding.rv) .setDataCallback(object : DragAndSwipeDataCallback { override fun dataMove(fromPosition: Int, toPosition: Int) { mAdapter.swap(fromPosition, toPosition) } override fun dataRemoveAt(position: Int) { mAdapter.removeAt(position) } }) .setItemDragListener(listener) .setItemSwipeListener(swipeListener) } companion object { private const val TAG = "Diff Drag Swipe Use" } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/DragAndSwipeUseActivity.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe; import android.content.Intent; import android.os.Bundle; import android.view.View; import androidx.annotation.NonNull; import androidx.core.graphics.Insets; import androidx.core.view.OnApplyWindowInsetsListener; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.recyclerview.widget.LinearLayoutManager; import com.chad.baserecyclerviewadapterhelper.R; import com.chad.baserecyclerviewadapterhelper.activity.home.adapter.HomeAdapter; import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity; import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding; import com.chad.baserecyclerviewadapterhelper.entity.HomeEntity; import java.util.ArrayList; public class DragAndSwipeUseActivity extends BaseViewBindingActivity { private final ArrayList homeItemData = new ArrayList<>(); @NonNull @Override public ActivityUniversalRecyclerBinding initBinding() { return ActivityUniversalRecyclerBinding.inflate(getLayoutInflater()); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewCompat.setOnApplyWindowInsetsListener(getViewBinding().getRoot(), new OnApplyWindowInsetsListener() { @Override public @NonNull WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { Insets bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()); getViewBinding().titleBar.updateFakeBarHeight(bar.top); return insets; } }); getViewBinding().titleBar.setTitle("Drag And Swipe"); getViewBinding().titleBar.setOnBackListener(v -> finish()); // 设置数据 homeItemData.add(new HomeEntity("Default Drag And Swipe", DefaultDragAndSwipeActivity.class, R.mipmap.gv_drag_and_swipe, "")); homeItemData.add(new HomeEntity("Manual Drag And Swipe", ManualDragAndSwipeUseActivity.class, R.mipmap.gv_drag_and_swipe, "")); homeItemData.add(new HomeEntity("Head Drag And Swipe", HeaderDragAndSwipeActivity.class, R.mipmap.gv_drag_and_swipe, "")); homeItemData.add(new HomeEntity("Diff Drag And Swipe", DragAndSwipeDifferActivity.class, R.mipmap.gv_drag_and_swipe, "")); /* * RV适配器 */ HomeAdapter mAdapter = new HomeAdapter(homeItemData); getViewBinding().rv.setLayoutManager(new LinearLayoutManager(this)); getViewBinding().rv.setAdapter(mAdapter); mAdapter.setOnItemClickListener((adapter, view, position) -> { HomeEntity item = adapter.getItems().get(position); if (!item.isSection()) { startActivity(new Intent(DragAndSwipeUseActivity.this, item.getActivity())); } }); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/HeaderDragAndSwipe.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter.HeaderDragAndSwipeAdapter import com.chad.library.adapter4.dragswipe.QuickDragAndSwipe /** * 重写拖拽类,根据itemType 设置某个类型的是否允许拖拽 */ class HeaderDragAndSwipe : QuickDragAndSwipe() { override fun getMovementFlags( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { if (recyclerView.adapter is ConcatAdapter) { val adapter = recyclerView.adapter as ConcatAdapter val absoluteAdapter = adapter.getWrappedAdapterAndPosition(viewHolder.absoluteAdapterPosition).first if (absoluteAdapter is HeaderDragAndSwipeAdapter) { return super.getMovementFlags(recyclerView, viewHolder) } return makeMovementFlags(0, 0) } return makeMovementFlags(0, 0) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/HeaderDragAndSwipeActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe import android.animation.ValueAnimator import android.graphics.Color import android.os.Build import android.os.Bundle import android.util.Log import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter.HeaderDragAndSwipeAdapter import com.chad.baserecyclerviewadapterhelper.activity.home.adapter.HomeTopHeaderAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.utils.vibrate import com.chad.library.adapter4.QuickAdapterHelper import com.chad.library.adapter4.dragswipe.setItemDragListener import com.chad.library.adapter4.dragswipe.setItemSwipeListener import com.chad.library.adapter4.loadState.LoadState.NotLoading import com.chad.library.adapter4.loadState.trailing.TrailingLoadStateAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext /** * 带头部局以及带加载下一页的,拖拽demo */ class HeaderDragAndSwipeActivity : BaseViewBindingActivity() { private val pageInfo = PageInfo() internal class PageInfo { var page = 0 fun nextPage() { page++ } fun reset() { page = 0 } val isFirstPage: Boolean get() = page == 0 } var headerDragAndSwipe = HeaderDragAndSwipe() .setDragMoveFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN) .setSwipeMoveFlags(ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) private val mAdapter: HeaderDragAndSwipeAdapter = HeaderDragAndSwipeAdapter() private lateinit var helper: QuickAdapterHelper override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Head Drag And Swipe" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.layoutManager = LinearLayoutManager(this) helper = QuickAdapterHelper.Builder(mAdapter) .setTrailingLoadStateAdapter( object : TrailingLoadStateAdapter.OnTrailingListener { override fun onLoad() { loadMore() } override fun onFailRetry() { } override fun isAllowLoading(): Boolean { return true } }) .build() .addBeforeAdapter(HomeTopHeaderAdapter()) headerDragAndSwipe.attachToRecyclerView(viewBinding.rv) .setDataCallback(mAdapter) .setItemDragListener( onItemDragStart = { viewHolder, pos -> Log.d(TAG, "drag start") vibrate() val holder = viewHolder as QuickViewHolder // 开始时,item背景色变化,demo这里使用了一个动画渐变,使得自然 val startColor = Color.WHITE val endColor = Color.rgb(245, 245, 245) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ValueAnimator.ofArgb(startColor, endColor).apply { addUpdateListener { animation: ValueAnimator -> holder.itemView.setBackgroundColor( animation.animatedValue as Int ) } duration = 300 start() } } }, onItemDragMoving = { source, from, target, to -> Log.d(TAG, "move from: $from to: $to") }, onItemDragEnd = { viewHolder, pos -> Log.d(TAG, "drag end") val holder = viewHolder as QuickViewHolder // 结束时,item背景色变化,demo这里使用了一个动画渐变,使得自然 val startColor = Color.rgb(245, 245, 245) val endColor = Color.WHITE if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ValueAnimator.ofArgb(startColor, endColor).apply { addUpdateListener { animation: ValueAnimator -> holder.itemView.setBackgroundColor( animation.animatedValue as Int ) } duration = 300 start() } } } ) .setItemSwipeListener( onItemSwipeStart = { viewHolder, pos -> Log.d(TAG, "onItemSwipeStart") }, onItemSwipeMoving = { canvas, viewHolder, dX, dY, isCurrentlyActive -> Log.d(TAG, "onItemSwipeMoving") }, onItemSwiped = { viewHolder, _, pos -> Log.d(TAG, "onItemSwiped") }, onItemSwipeEnd = { viewHolder, pos -> Log.d(TAG, "onItemSwipeEnd") } ) viewBinding.rv.adapter = helper.adapter loadMore() } private fun loadMore() { Request(pageInfo.page, object : RequestCallBack { override fun success(data: List) { if (pageInfo.page == 0) { mAdapter.submitList(data) } else { mAdapter.addAll(data) } helper.trailingLoadState = NotLoading(false) // page加一 pageInfo.nextPage() } override fun fail(e: Exception?) { } override fun end() { helper.trailingLoadState = NotLoading(true) } }).loadMore() } /** * 模拟加载数据的类,不用特别关注 */ internal class Request( private val mPage: Int, private val mCallBack: RequestCallBack ) { fun loadMore() { GlobalScope.launch(Dispatchers.IO) { if (mPage != 0) { delay(1500) } withContext(Dispatchers.Main) { val size = PAGE_SIZE if (mPage == 3) { mCallBack.end() } else { val starIndex = mPage.times(size) mCallBack.success(generateData(starIndex, size)) } } } } private fun generateData(starIndex: Int, size: Int): List { val data = java.util.ArrayList(size) val endIndex = starIndex.plus(size) for (i in starIndex until endIndex) { data.add("item $i") } return data } } internal interface RequestCallBack { /** * 模拟加载成功 * * @param data 数据 */ fun success(data: List) /** * 模拟加载失败 * * @param e 错误信息 */ fun fail(e: Exception?) /** * 模拟加载结束 */ fun end() } companion object { private const val PAGE_SIZE = 20 private const val TAG = "Default Drag And Swipe" } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/ManualDragAndSwipeUseActivity.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe; import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.Color; import android.os.Bundle; import android.util.Log; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.Insets; import androidx.core.view.OnApplyWindowInsetsListener; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter.DragAndSwipeAdapter; import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity; import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding; import com.chad.baserecyclerviewadapterhelper.utils.Tips; import com.chad.baserecyclerviewadapterhelper.utils.VibratorUtilsKt; import com.chad.library.adapter4.QuickAdapterHelper; import com.chad.library.adapter4.dragswipe.QuickDragAndSwipe; import com.chad.library.adapter4.dragswipe.listener.OnItemDragListener; import com.chad.library.adapter4.dragswipe.listener.OnItemSwipeListener; import com.chad.library.adapter4.viewholder.QuickViewHolder; import java.util.ArrayList; import java.util.List; /** * 手动实现拖动与侧滑效果 * Manual drag and Drag effects */ public class ManualDragAndSwipeUseActivity extends BaseViewBindingActivity { private final String TAG = "Manual Drag And Swipe"; private DragAndSwipeAdapter mAdapter; private QuickAdapterHelper helper; QuickDragAndSwipe quickDragAndSwipe = new QuickDragAndSwipe() .setDragMoveFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN) .setSwipeMoveFlags(ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) .setItemViewSwipeEnabled(true) .setLongPressDragEnabled(false);//关闭默认的长按拖拽功能,通过自定义长按事件进行拖拽 @NonNull @Override public ActivityUniversalRecyclerBinding initBinding() { return ActivityUniversalRecyclerBinding.inflate(getLayoutInflater()); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewCompat.setOnApplyWindowInsetsListener(getViewBinding().getRoot(), new OnApplyWindowInsetsListener() { @Override public @NonNull WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { Insets bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()); getViewBinding().titleBar.updateFakeBarHeight(bar.top); return insets; } }); getViewBinding().titleBar.setTitle("Manual Drag And Swipe"); getViewBinding().titleBar.setOnBackListener(v -> finish()); getViewBinding().rv.setLayoutManager(new LinearLayoutManager(this)); // 拖拽监听 OnItemDragListener listener = new OnItemDragListener() { @Override public void onItemDragStart(@Nullable RecyclerView.ViewHolder viewHolder, int pos) { VibratorUtilsKt.vibrate(getApplicationContext()); Log.d(TAG, "drag start"); final QuickViewHolder holder = ((QuickViewHolder) viewHolder); if (holder == null) return; // 开始时,item背景色变化,demo这里使用了一个动画渐变,使得自然 int startColor = Color.WHITE; int endColor = Color.rgb(245, 245, 245); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { ValueAnimator v = ValueAnimator.ofArgb(startColor, endColor); v.addUpdateListener(animation -> holder.itemView.setBackgroundColor((int) animation.getAnimatedValue())); v.setDuration(300); v.start(); } } @Override public void onItemDragMoving(@NonNull RecyclerView.ViewHolder source, int from, @NonNull RecyclerView.ViewHolder target, int to) { Log.d(TAG, "move from: " + source.getBindingAdapterPosition() + " to: " + target.getBindingAdapterPosition()); } @Override public void onItemDragEnd(@NonNull RecyclerView.ViewHolder viewHolder, int pos) { Log.d(TAG, "drag end"); final QuickViewHolder holder = ((QuickViewHolder) viewHolder); // 结束时,item背景色变化,demo这里使用了一个动画渐变,使得自然 int startColor = Color.rgb(245, 245, 245); int endColor = Color.WHITE; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { ValueAnimator v = ValueAnimator.ofArgb(startColor, endColor); v.addUpdateListener(animation -> holder.itemView.setBackgroundColor((int) animation.getAnimatedValue())); v.setDuration(300); v.start(); } } }; OnItemSwipeListener swipeListener = new OnItemSwipeListener() { @Override public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int bindingAdapterPosition) { Log.d(TAG, "onItemSwipeStart"); } @Override public void onItemSwipeEnd(@NonNull RecyclerView.ViewHolder viewHolder, int bindingAdapterPosition) { Log.d(TAG, "onItemSwipeEnd " + bindingAdapterPosition); } @Override public void onItemSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction, int bindingAdapterPosition) { Log.d(TAG, "onItemSwiped"); } @Override public void onItemSwipeMoving(@NonNull Canvas canvas, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, boolean isCurrentlyActive) { Log.d(TAG, "onItemSwipeMoving"); } }; List mData = generateData(50); mAdapter = new DragAndSwipeAdapter(); helper = new QuickAdapterHelper.Builder(mAdapter) .build(); getViewBinding().rv.setAdapter(helper.getAdapter()); mAdapter.submitList(mData); quickDragAndSwipe.attachToRecyclerView(getViewBinding().rv) .setDataCallback(mAdapter) .setItemDragListener(listener) .setItemSwipeListener(swipeListener); mAdapter.setOnItemClickListener((adapter, view, position) -> { Tips.show("点击了:" + position + ",侧滑可进行删除" + position); quickDragAndSwipe.startSwipe(position); }); mAdapter.setOnItemLongClickListener((adapter, view, position) -> { /* * 长按默认可拖动,可不进行设置此方法 * 此方法可以做特殊使用进行调用 * 如:长按此条position对应的item,触发 position+1 对应的item * 此处使用,关闭了默认长按拖拽功能 */ Tips.show("长按了:" + position + ",现在拖动可进行变换位置"); quickDragAndSwipe.startDrag(position); return false; }); } private List generateData(int size) { ArrayList data = new ArrayList<>(size); for (int i = 0; i < size; i++) { data.add("item " + i); } return data; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/adapter/DiffDragAndSwipeAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter import android.content.Context import android.view.ViewGroup import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.differ.adapter.DiffEntityCallback import com.chad.baserecyclerviewadapterhelper.entity.DiffEntity import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder /** * Create adapter */ class DiffDragAndSwipeAdapter : BaseQuickAdapter(DiffEntityCallback()) { override fun onCreateViewHolder( context: Context, parent: ViewGroup, viewType: Int ): QuickViewHolder { return QuickViewHolder(R.layout.layout_animation, parent) } override fun onBindViewHolder( holder: QuickViewHolder, position: Int, item: DiffEntity? ) { if (item == null) return holder.setText(R.id.tweetName, item.title) .setText(R.id.tweetText, item.content) .setText(R.id.tweetDate, item.date) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/adapter/DragAndSwipeAdapter.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter; import android.content.Context; import android.view.ViewGroup; import androidx.annotation.NonNull; import com.chad.baserecyclerviewadapterhelper.R; import com.chad.library.adapter4.BaseQuickAdapter; import com.chad.library.adapter4.dragswipe.listener.DragAndSwipeDataCallback; import com.chad.library.adapter4.viewholder.QuickViewHolder; public class DragAndSwipeAdapter extends BaseQuickAdapter implements DragAndSwipeDataCallback { @NonNull @Override protected QuickViewHolder onCreateViewHolder(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new QuickViewHolder(R.layout.item_draggable_view, parent); } @Override protected void onBindViewHolder(@NonNull QuickViewHolder holder, int position, String item) { switch (holder.getLayoutPosition() % 3) { case 0 -> holder.setImageResource(R.id.iv_head, R.mipmap.head_img0); case 1 -> holder.setImageResource(R.id.iv_head, R.mipmap.head_img1); case 2 -> holder.setImageResource(R.id.iv_head, R.mipmap.head_img2); default -> { } } holder.setText(R.id.tv, item); } @Override public void dataMove(int fromPosition, int toPosition) { move(fromPosition, toPosition); } @Override public void dataRemoveAt(int position) { removeAt(position); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/dragswipe/adapter/HeaderDragAndSwipeAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.dragswipe.adapter import android.content.Context import android.view.ViewGroup import com.chad.baserecyclerviewadapterhelper.R import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.dragswipe.listener.DragAndSwipeDataCallback /** * kotlin方式集成案例 */ open class HeaderDragAndSwipeAdapter : BaseQuickAdapter(), DragAndSwipeDataCallback { override fun onCreateViewHolder( context: Context, parent: ViewGroup, viewType: Int ): QuickViewHolder { return QuickViewHolder(R.layout.item_draggable_view, parent) } override fun onBindViewHolder(holder: QuickViewHolder, position: Int, item: String?) { when (holder.layoutPosition % 3) { 0 -> holder.setImageResource(R.id.iv_head, R.mipmap.head_img0) 1 -> holder.setImageResource(R.id.iv_head, R.mipmap.head_img1) 2 -> holder.setImageResource(R.id.iv_head, R.mipmap.head_img2) else -> {} } holder.setText(R.id.tv, item) } override fun dataMove(fromPosition: Int, toPosition: Int) { move(fromPosition, toPosition) } override fun dataRemoveAt(position: Int) { removeAt(position) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/emptyview/EmptyViewUseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.emptyview import android.os.Bundle import android.view.View import android.widget.FrameLayout import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.StaggeredGridLayoutManager import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.emptyview.adapter.EmptyViewAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.databinding.ActivityEmptyViewUseBinding class EmptyViewUseActivity : BaseViewBindingActivity() { private val mAdapter = EmptyViewAdapter() private var mError = true private var mNoData = true override fun initBinding(): ActivityEmptyViewUseBinding = ActivityEmptyViewUseBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "EmptyView Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.btnReset.setOnClickListener { reset() } viewBinding.rvList.adapter = mAdapter viewBinding.rvList.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) // 打开空布局功能 mAdapter.isStateViewEnable = true mAdapter.isUseStateViewSize = true onRefresh() } private fun reset() { mError = true mNoData = true mAdapter.submitList(null) onRefresh() } private val emptyDataView: View get() { val notDataView = layoutInflater.inflate(R.layout.empty_view, FrameLayout(this), false) notDataView.setOnClickListener { onRefresh() } return notDataView } private val errorView: View get() { val errorView = layoutInflater.inflate(R.layout.error_view, FrameLayout(this), false) errorView.setOnClickListener { onRefresh() } return errorView } private fun onRefresh() { // 方式一:直接传入 layout id mAdapter.setStateViewLayout(this, R.layout.loading_view) viewBinding.rvList.postDelayed({ if (mError) { // 模拟网络错误 // 方式二:传入View mAdapter.stateView = errorView mError = false } else { if (mNoData) { // 模拟接口没有数据 mAdapter.stateView = emptyDataView mNoData = false } else { // 模拟正常数据返回 mAdapter.submitList(DataServer.getSampleData(10)) } } }, 1000) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/emptyview/adapter/EmptyViewAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.emptyview.adapter import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.databinding.LayoutAnimationBinding import com.chad.baserecyclerviewadapterhelper.entity.Status import com.chad.library.adapter4.BaseQuickAdapter class EmptyViewAdapter : BaseQuickAdapter() { class VH( parent: ViewGroup, val binding: LayoutAnimationBinding = LayoutAnimationBinding.inflate( LayoutInflater.from(parent.context), parent, false ), ) : RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { return VH(parent) } override fun onBindViewHolder(holder: VH, position: Int, item: Status?) { if (item == null) return holder.binding.img.setImageResource(item.userAvatar) holder.binding.tweetName.text = item.userName holder.binding.tweetText.text = "O ever youthful,O ever weeping" } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/headerfooter/HeaderAndFooterUseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.headerfooter import android.os.Bundle import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter.FooterAdapter import com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter.HeaderAdapter import com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter.HeaderAndFooterAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.library.adapter4.QuickAdapterHelper /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ class HeaderAndFooterUseActivity : BaseViewBindingActivity() { private lateinit var helper: QuickAdapterHelper override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Header And Footer Use" viewBinding.titleBar.setOnBackListener { finish() } val adapter = HeaderAndFooterAdapter(DataServer.getSampleData(PAGE_SIZE)) adapter.setOnItemClickListener { _, _, position -> Tips.show("position: $position") } helper = QuickAdapterHelper.Builder(adapter) .build() viewBinding.rv.layoutManager = LinearLayoutManager(this) viewBinding.rv.adapter = helper.adapter addHeader() helper.addAfterAdapter( FooterAdapter(false).setOnItemClickListener { _, _, _ -> addFooter() } ) } private fun addHeader() { helper.addBeforeAdapter(0, HeaderAdapter().apply { setOnItemClickListener { _, _, _ -> addHeader() } }) } private fun addFooter() { helper.addAfterAdapter(FooterAdapter(true).setOnItemClickListener{ adapter, _, _ -> helper.removeAdapter(adapter) }) } companion object { private const val PAGE_SIZE = 3 } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/headerfooter/adapter/FooterAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter import android.content.Context import android.view.ViewGroup import com.chad.baserecyclerviewadapterhelper.R import com.chad.library.adapter4.BaseSingleItemAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder class FooterAdapter( private val isDelete: Boolean ) : BaseSingleItemAdapter() { override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): QuickViewHolder { return QuickViewHolder(R.layout.footer_view, parent) } override fun onBindViewHolder(holder: QuickViewHolder, item: Any?) { if (isDelete) { holder.setImageResource(R.id.iv, R.mipmap.rm_icon) } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/headerfooter/adapter/HeaderAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.library.adapter4.BaseSingleItemAdapter class HeaderAdapter: BaseSingleItemAdapter() { class VH(view: View): RecyclerView.ViewHolder(view) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { val view = LayoutInflater.from(parent.context).inflate(R.layout.head_view, parent, false) return VH(view) } override fun onBindViewHolder(holder: VH, item: Any?) { } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/headerfooter/adapter/HeaderAndFooterAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.databinding.ItemHeaderAndFooterBinding import com.chad.baserecyclerviewadapterhelper.entity.Status import com.chad.library.adapter4.BaseQuickAdapter /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ class HeaderAndFooterAdapter(list: List) : BaseQuickAdapter(list) { class VH(var binding: ItemHeaderAndFooterBinding) : RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { val binding = ItemHeaderAndFooterBinding.inflate(LayoutInflater.from(context), parent, false) return VH(binding) } override fun onBindViewHolder(holder: VH, position: Int, item: Status?) { when (holder.layoutPosition % 3) { 0 -> holder.binding.iv.setImageResource(R.mipmap.animation_img1) 1 -> holder.binding.iv.setImageResource(R.mipmap.animation_img2) 2 -> holder.binding.iv.setImageResource(R.mipmap.animation_img3) else -> {} } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/home/HomeActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.home import android.content.Intent import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.animation.AnimationUseActivity import com.chad.baserecyclerviewadapterhelper.activity.databinding.DataBindingUseActivity import com.chad.baserecyclerviewadapterhelper.activity.differ.DifferActivity import com.chad.baserecyclerviewadapterhelper.activity.dragswipe.DragAndSwipeUseActivity import com.chad.baserecyclerviewadapterhelper.activity.emptyview.EmptyViewUseActivity import com.chad.baserecyclerviewadapterhelper.activity.headerfooter.HeaderAndFooterUseActivity import com.chad.baserecyclerviewadapterhelper.activity.home.adapter.HomeAdapter import com.chad.baserecyclerviewadapterhelper.activity.home.adapter.HomeTopHeaderAdapter import com.chad.baserecyclerviewadapterhelper.activity.itemclick.ItemClickActivity import com.chad.baserecyclerviewadapterhelper.activity.loadmore.AutoLoadMoreRefreshUseActivity import com.chad.baserecyclerviewadapterhelper.activity.loadmore.NoAutoAutoLoadMoreRefreshUseActivity import com.chad.baserecyclerviewadapterhelper.activity.node.NodeActivity import com.chad.baserecyclerviewadapterhelper.activity.scene.GroupDemoActivity import com.chad.baserecyclerviewadapterhelper.activity.upfetch.UpFetchUseActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityHomeBinding import com.chad.baserecyclerviewadapterhelper.entity.HomeEntity import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.QuickAdapterHelper class HomeActivity : AppCompatActivity(), BaseQuickAdapter.OnItemClickListener { private lateinit var binding: ActivityHomeBinding /** * RV适配器 */ private val homeAdapter by lazy(LazyThreadSafetyMode.NONE) { HomeAdapter(homeItemData) } private val helper by lazy(LazyThreadSafetyMode.NONE) { QuickAdapterHelper.Builder(homeAdapter) .build() .addBeforeAdapter(HomeTopHeaderAdapter()) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityHomeBinding.inflate(layoutInflater) setContentView(binding.root) // 从 QuickAdapterHelper 获取 adapter,设置给 RecycleView binding.recyclerView.adapter = helper.adapter // 设置点击事件 homeAdapter.setOnItemClickListener(this) } /** * item 点击事件 * * @param adapter * @param view * @param position */ override fun onClick( adapter: BaseQuickAdapter, view: View, position: Int, ) { val item = adapter.getItem(position) if (!item.isSection) { startActivity(Intent(this@HomeActivity, item.activity)) } } private val homeItemData: ArrayList get() = arrayListOf( HomeEntity(sectionTitle = "BaseQuickAdapter 基础功能"), HomeEntity("Animation", AnimationUseActivity::class.java, R.mipmap.gv_animation), HomeEntity( "Header/Footer", HeaderAndFooterUseActivity::class.java, R.mipmap.gv_header_and_footer ), HomeEntity("EmptyView", EmptyViewUseActivity::class.java, R.mipmap.gv_empty), HomeEntity("ItemClick", ItemClickActivity::class.java, R.mipmap.gv_item_click), HomeEntity("DataBinding", DataBindingUseActivity::class.java, R.mipmap.gv_databinding), HomeEntity("DiffUtil", DifferActivity::class.java, R.mipmap.gv_databinding), HomeEntity("Multi-node", NodeActivity::class.java, R.mipmap.gv_databinding), // HomeEntity(sectionTitle = "功能模块"), HomeEntity("LoadMore(Auto)", AutoLoadMoreRefreshUseActivity::class.java, R.mipmap.gv_pulltorefresh), HomeEntity("LoadMore", NoAutoAutoLoadMoreRefreshUseActivity::class.java, R.mipmap.gv_pulltorefresh), HomeEntity("DragAndSwipe", DragAndSwipeUseActivity::class.java, R.mipmap.gv_drag_and_swipe), HomeEntity("UpFetch", UpFetchUseActivity::class.java, R.drawable.gv_up_fetch), HomeEntity(sectionTitle = "场景演示"), HomeEntity("Group(ConcatAdapter)", GroupDemoActivity::class.java, R.mipmap.gv_animation), ) } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/home/adapter/HomeAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.home.adapter import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.databinding.DefSectionHeadBinding import com.chad.baserecyclerviewadapterhelper.databinding.HomeItemViewBinding import com.chad.baserecyclerviewadapterhelper.entity.HomeEntity import com.chad.library.adapter4.BaseMultiItemAdapter /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ class HomeAdapter(data: List) : BaseMultiItemAdapter(data) { class ItemVH(val viewBinding: HomeItemViewBinding) : RecyclerView.ViewHolder(viewBinding.root) class HeaderVH(val viewBinding: DefSectionHeadBinding) : RecyclerView.ViewHolder(viewBinding.root) init { addItemType(ITEM_TYPE, object : OnMultiItemAdapterListener { override fun onCreate(context: Context, parent: ViewGroup, viewType: Int): ItemVH { val viewBinding = HomeItemViewBinding.inflate(LayoutInflater.from(context), parent, false) return ItemVH(viewBinding) } override fun onBind(holder: ItemVH, position: Int, item: HomeEntity?) { if (item == null) return holder.viewBinding.textView.text = item.name holder.viewBinding.icon.setImageResource(item.imageResource) } }).addItemType(SECTION_TYPE, object : OnMultiItemAdapterListener { override fun onCreate(context: Context, parent: ViewGroup, viewType: Int): HeaderVH { val viewBinding = DefSectionHeadBinding.inflate(LayoutInflater.from(context), parent, false) return HeaderVH(viewBinding) } override fun onBind(holder: HeaderVH, position: Int, item: HomeEntity?) { if (item == null) return holder.viewBinding.more.visibility = View.GONE holder.viewBinding.header.text = item.sectionTitle } override fun isFullSpanItem(itemType: Int): Boolean { return true } }).onItemViewType { position, list -> if (list[position].isSection) { SECTION_TYPE } else { ITEM_TYPE } } } companion object { private const val ITEM_TYPE = 10 private const val SECTION_TYPE = 11 } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/home/adapter/HomeTopHeaderAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.home.adapter import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.library.adapter4.BaseSingleItemAdapter import com.chad.library.adapter4.fullspan.FullSpanAdapterType class HomeTopHeaderAdapter : BaseSingleItemAdapter(), FullSpanAdapterType { companion object { val HEAD_VIEWTYPE = 0x10000556 } class VH(view: View) : RecyclerView.ViewHolder(view) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { return VH(LayoutInflater.from(parent.context).inflate(R.layout.top_view, parent, false)) } override fun onBindViewHolder(holder: VH, item: Any?) { } override fun getItemViewType(position: Int, list: List): Int { return HEAD_VIEWTYPE } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/itemclick/ItemClickActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.itemclick import android.os.Bundle import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.itemclick.adapter.ItemClickAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.entity.ClickEntity import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.library.adapter4.util.addOnDebouncedChildClick import com.chad.library.adapter4.util.setOnDebouncedItemClick /** * @author Allen */ class ItemClickActivity : BaseViewBindingActivity() { private val adapter: ItemClickAdapter by lazy(LazyThreadSafetyMode.NONE) { // 创建数据 val data = ArrayList().apply { add(ClickEntity(ClickEntity.CLICK_ITEM_VIEW)) add(ClickEntity(ClickEntity.CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) add(ClickEntity(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW)) } // 创建Adapter ItemClickAdapter(data) } override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Item Click Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.layoutManager = LinearLayoutManager(this) viewBinding.rv.adapter = adapter // 设置点击事件 adapter.setOnItemClickListener { _, _, position -> Tips.show("onItemClick $position") } // 去除点击抖动的扩展方法 adapter.setOnDebouncedItemClick {adapter, view, position -> Tips.show("onItemClick $position") } // 设置item 长按事件 adapter.setOnItemLongClickListener { _, _, position -> Tips.show("onItemLongClick $position") true } // 添加子 view 的点击事件 adapter.addOnItemChildClickListener(R.id.btn) { adapter, view, position -> Tips.show("onItemChildClick: $position") } adapter.addOnItemChildClickListener(R.id.iv_num_reduce) { adapter, view, position -> Tips.show("onItemChildClick: reduce $position") } adapter.addOnItemChildClickListener(R.id.iv_num_add) { adapter, view, position -> Tips.show("onItemChildClick: add $position") adapter.removeAt(position) } // 添加子 view 的点击事件(去除点击抖动的扩展方法) adapter.addOnDebouncedChildClick(R.id.btn) { adapter, view, position -> Tips.show("onItemChildClick: $position") } // 设置子 view 长按事件 adapter.addOnItemChildLongClickListener(R.id.btn_long) { adapter, view, position -> Tips.show("onItemChildLongClick $position") true } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/itemclick/adapter/ItemClickAdapter.java ================================================ package com.chad.baserecyclerviewadapterhelper.activity.itemclick.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.chad.baserecyclerviewadapterhelper.databinding.ItemClickChildviewBinding; import com.chad.baserecyclerviewadapterhelper.databinding.ItemClickViewBinding; import com.chad.baserecyclerviewadapterhelper.databinding.ItemLongClickChildviewBinding; import com.chad.baserecyclerviewadapterhelper.databinding.ItemLongClickViewBinding; import com.chad.baserecyclerviewadapterhelper.entity.ClickEntity; import com.chad.library.adapter4.BaseMultiItemAdapter; import java.util.List; /** * */ public class ItemClickAdapter extends BaseMultiItemAdapter { static class ItemViewVH extends RecyclerView.ViewHolder { ItemClickViewBinding viewBinding; public ItemViewVH(@NonNull ItemClickViewBinding viewBinding) { super(viewBinding.getRoot()); this.viewBinding = viewBinding; } public ItemViewVH(@NonNull ViewGroup parent) { this(ItemClickViewBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); } } static class ItemChildVH extends RecyclerView.ViewHolder { ItemClickChildviewBinding viewBinding; public ItemChildVH(@NonNull ItemClickChildviewBinding viewBinding) { super(viewBinding.getRoot()); this.viewBinding = viewBinding; } public ItemChildVH(@NonNull ViewGroup parent) { this(ItemClickChildviewBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); } } static class ItemLongClickVH extends RecyclerView.ViewHolder { ItemLongClickViewBinding viewBinding; public ItemLongClickVH(@NonNull ItemLongClickViewBinding viewBinding) { super(viewBinding.getRoot()); this.viewBinding = viewBinding; } public ItemLongClickVH(@NonNull ViewGroup parent) { this(ItemLongClickViewBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); } } static class ItemChildLongClickVH extends RecyclerView.ViewHolder { ItemLongClickChildviewBinding viewBinding; public ItemChildLongClickVH(@NonNull ItemLongClickChildviewBinding viewBinding) { super(viewBinding.getRoot()); this.viewBinding = viewBinding; } public ItemChildLongClickVH(@NonNull ViewGroup parent) { this(ItemLongClickChildviewBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); } } /** * 构造方法 */ public ItemClickAdapter(List data) { super(data); addItemType(ClickEntity.CLICK_ITEM_VIEW, new OnMultiItemAdapterListener() { @NonNull @Override public ItemViewVH onCreate(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new ItemViewVH(parent); } @Override public void onBind(@NonNull ItemViewVH holder, int position, ClickEntity item) { } @Override public void onViewDetachedFromWindow(@NonNull RecyclerView.ViewHolder holder) { if (holder instanceof ItemViewVH) { System.out.println("---------------------- >> onViewDetachedFromWindow ItemViewVH"); } } }).addItemType(ClickEntity.CLICK_ITEM_CHILD_VIEW, new OnMultiItemAdapterListener() { @NonNull @Override public ItemChildVH onCreate(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new ItemChildVH(parent); } @Override public void onBind(@NonNull ItemChildVH holder, int position, ClickEntity item) { } }).addItemType(ClickEntity.LONG_CLICK_ITEM_VIEW, new OnMultiItemAdapterListener() { @NonNull @Override public ItemLongClickVH onCreate(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new ItemLongClickVH(parent); } @Override public void onBind(@NonNull ItemLongClickVH holder, int position, ClickEntity item) { } }).addItemType(ClickEntity.LONG_CLICK_ITEM_CHILD_VIEW, new OnMultiItemAdapterListener() { @NonNull @Override public ItemChildLongClickVH onCreate(@NonNull Context context, @NonNull ViewGroup parent, int viewType) { return new ItemChildLongClickVH(parent); } @Override public void onBind(@NonNull ItemChildLongClickVH holder, int position, ClickEntity item) { } }).onItemViewType(new OnItemViewTypeListener() { @Override public int onItemViewType(int position, @NonNull List list) { return list.get(position).getItemType(); } }); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/loadmore/AutoLoadMoreRefreshUseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.loadmore import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter.HeaderAdapter import com.chad.baserecyclerviewadapterhelper.activity.loadmore.adapter.RecyclerViewAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.databinding.ActivityLoadMoreBinding import com.chad.baserecyclerviewadapterhelper.entity.Status import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.library.adapter4.QuickAdapterHelper import com.chad.library.adapter4.loadState.LoadState import com.chad.library.adapter4.loadState.trailing.TrailingLoadStateAdapter.OnTrailingListener /** * 自动加载更多 */ class AutoLoadMoreRefreshUseActivity : BaseViewBindingActivity() { internal class PageInfo { var page = 0 fun nextPage() { page++ } fun reset() { page = 0 } val isFirstPage: Boolean get() = page == 0 } private val pageInfo = PageInfo() private val mAdapter: RecyclerViewAdapter = RecyclerViewAdapter() private lateinit var helper: QuickAdapterHelper override fun initBinding(): ActivityLoadMoreBinding = ActivityLoadMoreBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) viewBinding.rvList.updatePadding(bottom = bar.bottom) insets } viewBinding.titleBar.title = "Auto LoadMore Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rvList.layoutManager = LinearLayoutManager(this) initAdapter() addHeadView() initRefreshLayout() } override fun onStart() { super.onStart() // 进入页面,先显示加载状态布局 mAdapter.setEmptyViewLayout(this, R.layout.loading_view) viewBinding.refreshLayout.isRefreshing = true refresh() } private fun initAdapter() { // 使用默认的"加载更多"的样式 helper = QuickAdapterHelper.Builder(mAdapter) .setTrailingLoadStateAdapter(object : OnTrailingListener { override fun onLoad() { request() } override fun onFailRetry() { request() } override fun isAllowLoading(): Boolean { return !viewBinding.refreshLayout.isRefreshing } }) .setTrailPreloadSize(0) // 预加载(默认值为0) .isTrailAutoLoadMore(true) // 是否自动加载更多(默认为true) .build() // 设置预加载,请调用以下方法 // helper.trailingLoadStateAdapter?.preloadSize = 1 viewBinding.rvList.adapter = helper.adapter } private fun addHeadView() { val headerAdapter = HeaderAdapter() headerAdapter.setOnItemClickListener { _, _, _ -> addHeadView() } helper.addBeforeAdapter(0, headerAdapter) } private fun initRefreshLayout() { viewBinding.refreshLayout.setColorSchemeColors(Color.rgb(47, 223, 189)) viewBinding.refreshLayout.setOnRefreshListener { refresh() } } /** * 刷新 */ private fun refresh() { // 下拉刷新,需要重置页数 pageInfo.reset() // 重置“加载更多”时状态 helper.trailingLoadState = LoadState.None request() } /** * 请求数据 */ private fun request() { Request(pageInfo.page, object : RequestCallBack { override fun success(data: List) { viewBinding.refreshLayout.isRefreshing = false if (pageInfo.isFirstPage) { // 如果是加载的第一页数据,用 submitList() // If it is the first page of data loaded, use submitList(). mAdapter.submitList(data) } else { //不是第一页,则用add mAdapter.addAll(data) } // 如果在数据不满足一屏时,暂停加载更多,请调用下面方法 // helper.trailingLoadStateAdapter?.checkDisableLoadMoreIfNotFullPage() if (pageInfo.page >= PAGE_SIZE) { /* Set the status to not loaded, and there is no paging data. 设置状态为未加载,并且没有分页数据了 */ helper.trailingLoadState = LoadState.NotLoading(true) Tips.show("no more data") } else { /* Set the state to not loaded, and there is also paginated data 设置状态为未加载,并且还有分页数据 */ helper.trailingLoadState = LoadState.NotLoading(false) } // page加一 pageInfo.nextPage() } override fun fail(e: Exception) { Tips.show(resources.getString(R.string.network_err)) viewBinding.refreshLayout.isRefreshing = false helper.trailingLoadState = LoadState.Error(e) } }).start() } /** * 模拟加载数据的类,不用特别关注 */ internal class Request(private val mPage: Int, private val mCallBack: RequestCallBack) : Thread() { private val mHandler: Handler = Handler(Looper.getMainLooper()) override fun run() { try { sleep(800) // 模拟网络延迟 } catch (ignored: InterruptedException) { } if (mPage == 2 && mFirstError) { mFirstError = false mHandler.post { mCallBack.fail(RuntimeException("load fail")) } } else { var size = PAGE_SIZE if (mPage == 1) { if (mFirstPageNoMore) { size = 1 } mFirstPageNoMore = !mFirstPageNoMore if (!mFirstError) { mFirstError = true } } else if (mPage == 4) { size = 1 } val dataSize = size mHandler.post { mCallBack.success(DataServer.getSampleData(dataSize)) } } } companion object { private var mFirstPageNoMore = false private var mFirstError = true } } internal interface RequestCallBack { /** * 模拟加载成功 * * @param data 数据 */ fun success(data: List) /** * 模拟加载失败 * * @param e 错误信息 */ fun fail(e: Exception) } companion object { private const val PAGE_SIZE = 5 } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/loadmore/NoAutoAutoLoadMoreRefreshUseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.loadmore import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.headerfooter.adapter.HeaderAdapter import com.chad.baserecyclerviewadapterhelper.activity.loadmore.adapter.CustomLoadMoreAdapter import com.chad.baserecyclerviewadapterhelper.activity.loadmore.adapter.RecyclerViewAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.databinding.ActivityLoadMoreBinding import com.chad.baserecyclerviewadapterhelper.entity.Status import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.library.adapter4.QuickAdapterHelper import com.chad.library.adapter4.loadState.LoadState import com.chad.library.adapter4.loadState.trailing.TrailingLoadStateAdapter.OnTrailingListener /** * 不进行自动加载更多 */ class NoAutoAutoLoadMoreRefreshUseActivity : BaseViewBindingActivity() { internal class PageInfo { var page = 0 fun nextPage() { page++ } fun reset() { page = 0 } val isFirstPage: Boolean get() = page == 0 } private val pageInfo = AutoLoadMoreRefreshUseActivity.PageInfo() private val mAdapter: RecyclerViewAdapter = RecyclerViewAdapter() private lateinit var helper: QuickAdapterHelper override fun initBinding(): ActivityLoadMoreBinding = ActivityLoadMoreBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) viewBinding.rvList.updatePadding(bottom = bar.bottom) insets } viewBinding.titleBar.title = "No Auto LoadMore Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rvList.layoutManager = LinearLayoutManager(this) initAdapter() addHeadView() initRefreshLayout() } override fun onStart() { super.onStart() // 进入页面,刷新数据 mAdapter.setEmptyViewLayout(this, R.layout.loading_view) viewBinding.refreshLayout.isRefreshing = true refresh() } private fun initAdapter() { // 自定义"加载更多"的样式 val loadMoreAdapter = CustomLoadMoreAdapter() loadMoreAdapter.setOnLoadMoreListener(object : OnTrailingListener { override fun onLoad() { request() } override fun onFailRetry() { request() } override fun isAllowLoading(): Boolean { // 下拉刷新的适合,不允许进行"加载更多" return !viewBinding.refreshLayout.isRefreshing } }) //—————————————————————————————————————————————————————————— // 可选,监听加载状态的变化。 //—————————————————————————————————————————————————————————— loadMoreAdapter.addLoadStateListener { previousState, currentState -> // 你的业务逻辑 println("----------- previousState: $previousState - currentState: $currentState ") } //—————————————————————————————————————————————————————————— // 关闭"自动加载更多" //—————————————————————————————————————————————————————————— loadMoreAdapter.isAutoLoadMore = false helper = QuickAdapterHelper.Builder(mAdapter) .setTrailingLoadStateAdapter(loadMoreAdapter) .build() viewBinding.rvList.adapter = helper.adapter } private fun addHeadView() { val headerAdapter = HeaderAdapter() headerAdapter.setOnItemClickListener { _, _, _ -> addHeadView() } helper.addBeforeAdapter(headerAdapter) } private fun initRefreshLayout() { viewBinding.refreshLayout.setColorSchemeColors(Color.rgb(47, 223, 189)) viewBinding.refreshLayout.setOnRefreshListener { refresh() } } /** * 刷新 */ private fun refresh() { // 下拉刷新,需要重置页数 pageInfo.reset() request() } /** * 请求数据 */ private fun request() { Request(pageInfo.page, object : RequestCallBack { override fun success(data: List) { viewBinding.refreshLayout.isRefreshing = false if (pageInfo.isFirstPage) { // 如果是加载的第一页数据,用 submitList() // If it is the first page of data loaded, use submitList(). mAdapter.submitList(data) } else { //不是第一页,则用add mAdapter.addAll(data) } if (pageInfo.page >= PAGE_SIZE) { /* Set the status to not loaded, and there is no paging data. 设置状态为未加载,并且没有分页数据了 */ helper.trailingLoadState = LoadState.NotLoading(true) Tips.show("no more data") } else { /* Set the state to not loaded, and there is also paginated data 设置状态为未加载,并且还有分页数据 */ helper.trailingLoadState = LoadState.NotLoading(false) } // page加一 pageInfo.nextPage() } override fun fail(e: Exception) { Tips.show(resources.getString(R.string.network_err)) viewBinding.refreshLayout.isRefreshing = false helper.trailingLoadState = LoadState.Error(e) } }).start() } /** * 模拟加载数据的类,不用特别关注 */ internal class Request(private val mPage: Int, private val mCallBack: RequestCallBack) : Thread() { private val mHandler: Handler = Handler(Looper.getMainLooper()) override fun run() { try { sleep(1000) // 模拟网络延迟 } catch (ignored: InterruptedException) { } if (mPage == 2 && mFirstError) { mFirstError = false mHandler.post { mCallBack.fail(RuntimeException("load fail")) } } else { var size = PAGE_SIZE if (mPage == 1) { if (mFirstPageNoMore) { size = 1 } mFirstPageNoMore = !mFirstPageNoMore if (!mFirstError) { mFirstError = true } } else if (mPage == 4) { size = 1 } val dataSize = size mHandler.post { mCallBack.success(DataServer.getSampleData(dataSize)) } } } companion object { private var mFirstPageNoMore = false private var mFirstError = true } } internal interface RequestCallBack { /** * 模拟加载成功 * * @param data 数据 */ fun success(data: List) /** * 模拟加载失败 * * @param e 错误信息 */ fun fail(e: Exception) } companion object { private const val PAGE_SIZE = 5 } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/loadmore/adapter/CustomLoadMoreAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.loadmore.adapter import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.databinding.ViewLoadMoreBinding import com.chad.library.adapter4.loadState.LoadState import com.chad.library.adapter4.loadState.trailing.TrailingLoadStateAdapter /** * 自定义的"加载更多"。 * 这里可以做很多事情,这里仅展示了更改自定义布局的使用。 * * There are many things that can be done here, only the use of changing custom layouts is shown here. */ class CustomLoadMoreAdapter : TrailingLoadStateAdapter() { override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): CustomVH { val viewBinding = ViewLoadMoreBinding.inflate(LayoutInflater.from(parent.context), parent, false) return CustomVH(viewBinding).apply { viewBinding.loadMoreLoadFailView.setOnClickListener { // 失败重试点击事件 invokeFailRetry() } viewBinding.loadMoreLoadCompleteView.setOnClickListener { // 加载更多,手动点击事件 invokeLoadMore() } } } override fun onBindViewHolder(holder: CustomVH, loadState: LoadState) { when (loadState) { is LoadState.NotLoading -> { if (loadState.endOfPaginationReached) { holder.viewBinding.loadMoreLoadCompleteView.visibility = View.GONE holder.viewBinding.loadMoreLoadingView.visibility = View.GONE holder.viewBinding.loadMoreLoadFailView.visibility = View.GONE holder.viewBinding.loadMoreLoadEndView.visibility = View.VISIBLE } else { holder.viewBinding.loadMoreLoadCompleteView.visibility = View.VISIBLE holder.viewBinding.loadMoreLoadingView.visibility = View.GONE holder.viewBinding.loadMoreLoadFailView.visibility = View.GONE holder.viewBinding.loadMoreLoadEndView.visibility = View.GONE } } is LoadState.Loading -> { holder.viewBinding.loadMoreLoadCompleteView.visibility = View.GONE holder.viewBinding.loadMoreLoadingView.visibility = View.VISIBLE holder.viewBinding.loadMoreLoadFailView.visibility = View.GONE holder.viewBinding.loadMoreLoadEndView.visibility = View.GONE } is LoadState.Error -> { holder.viewBinding.loadMoreLoadCompleteView.visibility = View.GONE holder.viewBinding.loadMoreLoadingView.visibility = View.GONE holder.viewBinding.loadMoreLoadFailView.visibility = View.VISIBLE holder.viewBinding.loadMoreLoadEndView.visibility = View.GONE } is LoadState.None -> { holder.viewBinding.loadMoreLoadCompleteView.visibility = View.GONE holder.viewBinding.loadMoreLoadingView.visibility = View.GONE holder.viewBinding.loadMoreLoadFailView.visibility = View.GONE holder.viewBinding.loadMoreLoadEndView.visibility = View.GONE } } } class CustomVH(val viewBinding: ViewLoadMoreBinding) : RecyclerView.ViewHolder(viewBinding.root) } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/loadmore/adapter/RecyclerViewAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.loadmore.adapter import android.content.Context import android.text.TextPaint import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.databinding.LayoutAnimationBinding import com.chad.baserecyclerviewadapterhelper.entity.Status import com.chad.baserecyclerviewadapterhelper.utils.Tips import com.chad.library.adapter4.BaseQuickAdapter /** * @author: limuyang * @date: 2019-12-04 * @Description: */ class RecyclerViewAdapter : BaseQuickAdapter() { class VH( parent: ViewGroup, val viewBinding: LayoutAnimationBinding = LayoutAnimationBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) : RecyclerView.ViewHolder(viewBinding.root) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { return VH(parent) } protected override fun onBindViewHolder(holder: VH, position: Int, item: Status?) { when (holder.layoutPosition % 3) { 0 -> holder.viewBinding.img.setImageResource(R.mipmap.animation_img1) 1 -> holder.viewBinding.img.setImageResource(R.mipmap.animation_img2) 2 -> holder.viewBinding.img.setImageResource(R.mipmap.animation_img3) else -> {} } holder.viewBinding.tweetName.text = "Hoteis in Rio de Janeiro " + position + " " + item!!.userName val msg = "\"He was one of Australia's most of distinguished artistes, renowned for his portraits\"" holder.viewBinding.tweetText.text = buildSpannedString { append(msg) inSpans(clickableSpan) { append("landscapes and nedes") } } holder.viewBinding.tweetText.movementMethod = LinkMovementMethod.getInstance() } private val clickableSpan: ClickableSpan = object : ClickableSpan() { override fun onClick(widget: View) { Tips.show("事件触发了 landscapes and nedes") } override fun updateDrawState(ds: TextPaint) { ds.color = ContextCompat.getColor(context, R.color.clickspan_color) ds.isUnderlineText = true } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/node/NodeActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.node import android.os.Bundle import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import com.chad.baserecyclerviewadapterhelper.activity.node.adapter.NodeAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.data.DataServer import com.chad.baserecyclerviewadapterhelper.databinding.ActivityNodeBinding /** * @author LiMuYang * @date 2025/9/5 * @description 多节点demo */ class NodeActivity : BaseViewBindingActivity() { private val adapter = NodeAdapter() override fun initBinding(): ActivityNodeBinding { return ActivityNodeBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "Multi-node Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.adapter = adapter adapter.submitList(DataServer.getNodeData()) viewBinding.btnCloseAll.setOnClickListener { adapter.closeAll() } viewBinding.btnRef.setOnClickListener { adapter.submitList(DataServer.getNodeData()) } viewBinding.btnRefOnSave.setOnClickListener { adapter.submitList(DataServer.getNodeData(), clearOpenStates = true) } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/node/adapter/NodeAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.node.adapter import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import android.widget.Toast import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.databinding.ItemNodeLevel1Binding import com.chad.baserecyclerviewadapterhelper.databinding.ItemNodeLevel2Binding import com.chad.baserecyclerviewadapterhelper.databinding.ItemNodeLevel3Binding import com.chad.baserecyclerviewadapterhelper.entity.NodeEntity import com.chad.library.adapter4.BaseNodeAdapter /** * @author LiMuYang * @date 2025/9/5 * @description */ class NodeAdapter : BaseNodeAdapter() { override fun getChildNodeList(position: Int, parent: Any): List? { when (parent) { is NodeEntity -> { // 一级 return parent.childNode } is NodeEntity.Level2NodeEntity -> { // 二级 return parent.childNode } is NodeEntity.Level2NodeEntity.Level3NodeEntity -> { // 三级 没有子node return null } else -> return null } } override fun isInitialOpen(position: Int, item: Any): Boolean { // 哪些数据需要默认展开 if (item is NodeEntity) { return item.title == "Item 1" } else if (item is NodeEntity.Level2NodeEntity) { // 二级 return item.title.startsWith("Item 1", ignoreCase = true) } return false } override fun isSameNode(item1: Any, item2: Any): Boolean { return when (item1) { is NodeEntity if item2 is NodeEntity -> { item1.title == item2.title } is NodeEntity.Level2NodeEntity if item2 is NodeEntity.Level2NodeEntity -> { // 二级 item1.title == item2.title } is NodeEntity.Level2NodeEntity.Level3NodeEntity if item2 is NodeEntity.Level2NodeEntity.Level3NodeEntity -> { // 三级 item1.title == item2.title } else -> { false } } } override fun getItemViewType(position: Int, list: List): Int { val item = list[position] when (item) { is NodeEntity -> { // 一级 return 1 } is NodeEntity.Level2NodeEntity -> { // 二级 return 2 } is NodeEntity.Level2NodeEntity.Level3NodeEntity -> { // 三级 return 3 } else -> return 0 } } override fun onCreateViewHolder( context: Context, parent: ViewGroup, viewType: Int, ): RecyclerView.ViewHolder { return when (viewType) { 1 -> { Level1Hodler(parent).apply { itemView.setOnClickListener { openOrClose(bindingAdapterPosition) } } } 2 -> { Level2Hodler(parent).apply { itemView.setOnClickListener { openOrClose(bindingAdapterPosition) } } } 3 -> { Level3Hodler(parent).apply { itemView.setOnClickListener { Toast.makeText( it.context, "Level 3 _ index $bindingAdapterPosition", Toast.LENGTH_SHORT ).show() } } } else -> { Level1Hodler(parent) } } } override fun onBindViewHolder( holder: RecyclerView.ViewHolder, position: Int, item: Any?, ) { when (holder) { is Level1Hodler -> { // 一级 val nodeEntity = item as NodeEntity holder.viewBinding.tvTitle.text = nodeEntity.title // 设置箭头图标 if (nodeEntity.childNode.isNullOrEmpty()) { holder.viewBinding.ivArrow.setBackgroundResource(0) } else { if (isOpened(item)) { holder.viewBinding.ivArrow.setBackgroundResource(R.drawable.ic_node_down) } else { holder.viewBinding.ivArrow.setBackgroundResource(R.drawable.ic_node_right) } } } is Level2Hodler -> { // 二级 val nodeEntity = item as NodeEntity.Level2NodeEntity holder.viewBinding.tvTitle.text = nodeEntity.title // 设置箭头图标 if (nodeEntity.childNode.isNullOrEmpty()) { holder.viewBinding.ivArrow.setBackgroundResource(0) } else { if (isOpened(item)) { holder.viewBinding.ivArrow.setBackgroundResource(R.drawable.ic_node_down) } else { holder.viewBinding.ivArrow.setBackgroundResource(R.drawable.ic_node_right) } } } is Level3Hodler -> { // 三级 val nodeEntity = item as NodeEntity.Level2NodeEntity.Level3NodeEntity holder.viewBinding.tvTitle.text = nodeEntity.title } } } } class Level1Hodler( parent: ViewGroup, val viewBinding: ItemNodeLevel1Binding = ItemNodeLevel1Binding.inflate( LayoutInflater.from(parent.context), parent, false ), ) : RecyclerView.ViewHolder(viewBinding.root) class Level2Hodler( parent: ViewGroup, val viewBinding: ItemNodeLevel2Binding = ItemNodeLevel2Binding.inflate( LayoutInflater.from(parent.context), parent, false ), ) : RecyclerView.ViewHolder(viewBinding.root) class Level3Hodler( parent: ViewGroup, val viewBinding: ItemNodeLevel3Binding = ItemNodeLevel3Binding.inflate( LayoutInflater.from(parent.context), parent, false ), ) : RecyclerView.ViewHolder(viewBinding.root) ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/scene/GroupDemoActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.scene import android.graphics.drawable.ColorDrawable import android.os.Bundle import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.ConcatAdapter import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.activity.scene.adapter.GroupAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.entity.GroupDemoEntity import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types import androidx.core.graphics.drawable.toDrawable class GroupDemoActivity : BaseViewBindingActivity() { // 创建一个 ConcatAdapter,用来包裹 GroupAdapter private val concatAdapter = ConcatAdapter() override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } window.setBackgroundDrawable(ContextCompat.getColor(this, R.color.bg).toDrawable()) viewBinding.titleBar.title = "Group Scene(ConcatAdapter)" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.layoutManager = LinearLayoutManager(this) viewBinding.rv.adapter = concatAdapter /* 往常,我们获得这样一个 List 嵌套 List 的结构时,需要展平数据成一个 List, 然后用一个 Adapter 通过不同的ItemViewType 去做,非常繁琐,容易出错,而且不方便数据刷新 现在我们不需要去对数据做处理了,直接使用! */ val list: List = jsonToList() // 循环 list,有多少个数组,就创建多少个 GroupAdapter,就这么简单 list.forEach { val adapter = GroupAdapter() adapter.submitList(it.groupList) // 创建好以后,直接扔进 ConcatAdapter concatAdapter.addAdapter(adapter) } } /** * 解析 Json,不用关注 */ private fun jsonToList(): List { val moshi = Moshi.Builder().build() val type = Types.newParameterizedType(List::class.java, GroupDemoEntity::class.java) val jsonAdapter: JsonAdapter> = moshi.adapter(type) return jsonAdapter.fromJson(JSON) ?: throw IllegalStateException("json 解析出错") } companion object { // 此种格式的json,在业务场景中比较常见 private const val JSON = """ [{ "group_name": "1", "group_list": [{ "title": "patton", "content": "this is content" }, { "title": "nicole", "content": "this is content" }, { "title": "anthony", "content": "this is content" } ] }, { "group_name": "2", "group_list": [{ "title": "zane", "content": "this is content" }, { "title": "venus", "content": "this is content" }, { "title": "yahya", "content": "this is content" }, { "title": "starlight", "content": "this is content" }, { "title": "twinkle", "content": "this is content" } ] }, { "group_name": "3", "group_list": [{ "title": "esther", "content": "this is content" }, { "title": "asta", "content": "this is content" }, { "title": "gary", "content": "this is content" } ] }, { "group_name": "4", "group_list": [{ "title": "peter", "content": "this is content" }, { "title": "aldrich", "content": "this is content" } ] }, { "group_name": "5", "group_list": [{ "title": "edgar", "content": "this is content" }, { "title": "danika", "content": "this is content" }, { "title": "clement", "content": "this is content" } ] } ] """ } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/scene/adapter/GroupAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.scene.adapter import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import android.view.ViewGroup.MarginLayoutParams import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.databinding.ItemGroupTypeBinding import com.chad.baserecyclerviewadapterhelper.entity.GroupDemoEntity import com.chad.baserecyclerviewadapterhelper.utils.dp import com.chad.library.adapter4.BaseQuickAdapter /** * 每一组的Adapter * */ class GroupAdapter : BaseQuickAdapter(){ class VH( parent: ViewGroup, val binding: ItemGroupTypeBinding = ItemGroupTypeBinding.inflate(LayoutInflater.from(parent.context), parent ,false) ):RecyclerView.ViewHolder(binding.root) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { return VH(parent) } override fun onBindViewHolder(holder: VH, position: Int, item: GroupDemoEntity.Group?) { if (item == null) return holder.binding.tvTitle.text = item.title holder.binding.tvContent.text = item.content holder.binding.lineView.isVisible = position > 0 when (position) { 0 -> { // 第一个item,设置上圆角背景 holder.binding.root.setBackgroundResource(R.drawable.ic_group_item_top_bg) // 设置点间距 holder.binding.root.updateLayoutParams { topMargin = 15.dp } } items.size - 1 -> { // 最后一个item,设置下圆角背景 holder.binding.root.setBackgroundResource(R.drawable.ic_group_item_bottom_bg) } else -> { // 其他的,没有圆角的背景 holder.binding.root.setBackgroundResource(R.drawable.ic_group_item_mid_bg) } } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/upfetch/UpFetchUseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.upfetch import android.os.Bundle import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding import androidx.recyclerview.widget.LinearLayoutManager import com.chad.baserecyclerviewadapterhelper.activity.upfetch.adapter.UpFetchAdapter import com.chad.baserecyclerviewadapterhelper.base.BaseViewBindingActivity import com.chad.baserecyclerviewadapterhelper.databinding.ActivityUniversalRecyclerBinding import com.chad.baserecyclerviewadapterhelper.entity.Movie import com.chad.library.adapter4.QuickAdapterHelper import com.chad.library.adapter4.loadState.LoadState import com.chad.library.adapter4.loadState.LoadState.NotLoading import com.chad.library.adapter4.loadState.leading.LeadingLoadStateAdapter.OnLeadingListener import java.util.* /** * @author limuyang * 2019-12-06 */ class UpFetchUseActivity : BaseViewBindingActivity() { private val mAdapter = UpFetchAdapter() private lateinit var helper: QuickAdapterHelper override fun initBinding(): ActivityUniversalRecyclerBinding = ActivityUniversalRecyclerBinding.inflate(layoutInflater) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { view, insets -> val bar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) viewBinding.titleBar.updateFakeBarHeight(bar.top) insets } viewBinding.titleBar.title = "UpFetch Use" viewBinding.titleBar.setOnBackListener { finish() } viewBinding.rv.layoutManager = LinearLayoutManager(this) helper = QuickAdapterHelper.Builder(mAdapter) .setLeadingLoadStateAdapter(object : OnLeadingListener { override fun onLoad() { requestUoFetch() } override fun isAllowLoading(): Boolean { return true } }) .setLeadPreloadSize(0) // 预加载(默认值为0) .build() viewBinding.rv.adapter = helper.adapter } override fun onStart() { super.onStart() requestUoFetch() } private var count = 0 private fun requestUoFetch() { if (count == 0) { count++ // 首次进入页面,设置数据 mAdapter.submitList(genData()) scrollToBottom() helper.leadingLoadState = NotLoading(false) return } count++ /** * When starting to request data from the network, set the status to loading. * 当开始网络请求数据的时候,设置状态为加载中 */ helper.leadingLoadState = LoadState.Loading /* * get data from internet. * 从网络获取数据 */ viewBinding.rv.postDelayed({ mAdapter.addAll(0, genData()) if (count > 5) { /* * Set the status to not loaded, and there is no paging data. * 设置状态为未加载,并且没有分页数据了 */ helper.leadingLoadState = NotLoading(true) } else { /** * Set the state to not loaded, and there is also paginated data * 设置状态为未加载,并且还有分页数据 */ helper.leadingLoadState = NotLoading(false) } }, 600) } /** * 滚动到底部(不带动画) */ private fun scrollToBottom() { val ll = viewBinding.rv.layoutManager as LinearLayoutManager ll.scrollToPositionWithOffset(bottomDataPosition, 0) } private val bottomDataPosition: Int get() = mAdapter.items.size - 1 private fun genData(): List { val list = ArrayList() val random = Random() for (i in 0..9) { val name = "Chad" val price = random.nextInt(10) + 10 val len = random.nextInt(80) + 60 val movie = Movie(name, len, price, "He was one of Australia's most distinguished artistes") list.add(movie) } return list } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/activity/upfetch/adapter/UpFetchAdapter.kt ================================================ package com.chad.baserecyclerviewadapterhelper.activity.upfetch.adapter import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.databinding.ItemHeaderAndFooterBinding import com.chad.baserecyclerviewadapterhelper.entity.Movie import com.chad.library.adapter4.BaseQuickAdapter /** * @author: limuyang * @date: 2019-12-06 * @Description: */ class UpFetchAdapter : BaseQuickAdapter() { class VH( parent: ViewGroup, val viewBinding: ItemHeaderAndFooterBinding = ItemHeaderAndFooterBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) : RecyclerView.ViewHolder(viewBinding.root) override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { return VH(parent) } override fun onBindViewHolder(holder: VH, position: Int, item: Movie?) { when (holder.layoutPosition % 3) { 0 -> holder.viewBinding.iv.setImageResource(R.mipmap.animation_img1) 1 -> holder.viewBinding.iv.setImageResource(R.mipmap.animation_img2) 2 -> holder.viewBinding.iv.setImageResource(R.mipmap.animation_img3) else -> {} } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/animator/CustomAnimation1.java ================================================ package com.chad.baserecyclerviewadapterhelper.animator; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.DecelerateInterpolator; import com.chad.library.adapter4.animation.ItemAnimator; import org.jetbrains.annotations.NotNull; /** * 自定义动画1 */ public class CustomAnimation1 implements ItemAnimator { @NotNull @Override public Animator animator(@NotNull View view) { Animator alpha = ObjectAnimator.ofFloat(view, "alpha", 0, 1f); Animator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.3f, 1); Animator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.3f, 1); scaleY.setInterpolator(new DecelerateInterpolator()); scaleX.setInterpolator(new DecelerateInterpolator()); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(350); animatorSet.play(alpha).with(scaleX).with(scaleY); return animatorSet; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/animator/CustomAnimation2.java ================================================ package com.chad.baserecyclerviewadapterhelper.animator; import android.animation.Animator; import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.Interpolator; import com.chad.library.adapter4.animation.ItemAnimator; import org.jetbrains.annotations.NotNull; import static java.lang.Math.PI; import static java.lang.Math.pow; import static java.lang.Math.sin; /** * 自定义动画2 */ public class CustomAnimation2 implements ItemAnimator { @NotNull @Override public Animator animator(@NotNull View view) { Animator translationX = ObjectAnimator.ofFloat(view, "translationX", -view.getRootView().getWidth(), 0f); translationX.setDuration(800); translationX.setInterpolator(new MyInterpolator2()); return translationX; } private static class MyInterpolator2 implements Interpolator { @Override public float getInterpolation(float input) { float factor = 0.7f; return (float) (pow(2.0, -10.0 * input) * sin((input - factor / 4) * (2 * PI) / factor) + 1); } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/animator/CustomAnimation3.java ================================================ package com.chad.baserecyclerviewadapterhelper.animator; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.view.View; import android.view.animation.DecelerateInterpolator; import com.chad.library.adapter4.animation.ItemAnimator; import org.jetbrains.annotations.NotNull; public class CustomAnimation3 implements ItemAnimator { @NotNull @Override public Animator animator(@NotNull View view) { Animator alpha = ObjectAnimator.ofFloat(view, "alpha", 0, 1f); Animator translationY = ObjectAnimator.ofFloat(view, "translationY", view.getRootView().getHeight(), 0f); translationY.setInterpolator(new DecelerateInterpolator(1.2f)); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(450); animatorSet.play(alpha).with(translationY); return animatorSet; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/base/BaseActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.base import android.os.Build import android.os.Bundle import android.view.View import androidx.activity.enableEdgeToEdge import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.utils.statusBarLightMode abstract class BaseActivity(@LayoutRes layoutRes: Int = 0) : AppCompatActivity(layoutRes) { protected open val contentView: View? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() contentView?.let { setContentView(it) } window.statusBarColor = ContextCompat.getColor(this, R.color.spinner_bg) window.statusBarLightMode = false } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/base/BaseViewBindingActivity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.base import android.view.View import androidx.viewbinding.ViewBinding abstract class BaseViewBindingActivity : BaseActivity() { private var _viewBinding: V? = null protected val viewBinding: V get() { return _viewBinding ?: throw IllegalStateException( "Should be called initBinding()" ) } /** * 初始化 [viewBinding] */ abstract fun initBinding(): V final override val contentView: View get() { return initBinding().apply { _viewBinding = this }.root } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/data/DataServer.kt ================================================ package com.chad.baserecyclerviewadapterhelper.data import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.entity.DiffEntity import com.chad.baserecyclerviewadapterhelper.entity.NodeEntity import com.chad.baserecyclerviewadapterhelper.entity.Status /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ object DataServer { const val HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK = "https://avatars1.githubusercontent.com/u/7698209?v=3&s=460" const val CYM_CHAD = "CymChad" const val CHAY_CHAN = "ChayChan" fun getSampleData(lenth: Int): MutableList { val list: MutableList = ArrayList() for (i in 0 until lenth) { val status = Status() status.userName = "Chad$i" status.createdAt = "04/05/$i" status.isRetweet = i % 2 == 0 status.userAvatar = when (i % 3) { 0 -> R.mipmap.animation_img1 1 -> R.mipmap.animation_img2 else -> R.mipmap.animation_img3 } status.text = "BaseRecyclerViewAdpaterHelper https://www.recyclerview.org" list.add(status) } return list } val strData: List get() { val list: MutableList = ArrayList() for (i in 0..19) { var str = HTTPS_AVATARS1_GITHUBUSERCONTENT_COM_LINK if (i % 2 == 0) { str = CYM_CHAD } list.add(str) } return list } @JvmStatic val diffUtilDemoEntities: List get() { val list: MutableList = ArrayList() for (i in 0..9) { list.add( DiffEntity( i, "Item $i", "This item $i content", "06-12" ) ) } return list } fun getNodeData(): List { val list = ArrayList() for (i in 0..9) { val node = if (i % 2 == 0) { NodeEntity("Item $i", null) } else { val level2list = ArrayList() for (n in 0..4) { val leve2Node = if (n % 2 != 0) { NodeEntity.Level2NodeEntity("Item ${i} - Level 2 - Index $n", null) } else { val level3list = ArrayList() for (m in 0..4) { val level3Node = NodeEntity.Level2NodeEntity.Level3NodeEntity("Item${i} - Level2-index:${n} - Level 3 - Index $m") level3list.add(level3Node) } NodeEntity.Level2NodeEntity("Item ${i} - Level 2 - Index $n with Child", level3list) } level2list.add(leve2Node) } NodeEntity("Item $i", level2list) } list.add(node) } return list } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/decoration/GridItemDecoration.java ================================================ package com.chad.baserecyclerviewadapterhelper.decoration; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.View; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ public class GridItemDecoration extends RecyclerView.ItemDecoration { private Drawable dividerDrawable; private int orientation = LinearLayoutManager.VERTICAL; public GridItemDecoration(Drawable divider) { dividerDrawable = divider; } public GridItemDecoration(Context context, int resId) { dividerDrawable = context.getResources().getDrawable(resId); } public GridItemDecoration(Context context, int resId, int orientation) { dividerDrawable = context.getResources().getDrawable(resId); this.orientation = orientation; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if (dividerDrawable == null) { return; } if (parent.getChildLayoutPosition(view) < 1) { return; } if (orientation == LinearLayoutManager.VERTICAL) { outRect.top = dividerDrawable.getIntrinsicHeight(); } else if (orientation == LinearLayoutManager.HORIZONTAL) { outRect.left = dividerDrawable.getIntrinsicWidth(); } } /** * @param c * @param parent * @param state */ @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { if (dividerDrawable == null) { return; } int childCount = parent.getChildCount(); int rightV = parent.getWidth(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int leftV = parent.getPaddingLeft() + child.getPaddingLeft(); int bottomV = child.getTop() - params.topMargin; int topV = bottomV - dividerDrawable.getIntrinsicHeight(); int topH = child.getTop() + params.topMargin; int bottomH = child.getBottom() + params.bottomMargin; int rightH = child.getLeft() - params.leftMargin; int leftH = rightH - dividerDrawable.getIntrinsicWidth(); dividerDrawable.setBounds(leftH, topH, rightH, bottomH); dividerDrawable.draw(c); dividerDrawable.setBounds(leftV, topV, rightV, bottomV); dividerDrawable.draw(c); } } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/decoration/GridSectionAverageGapItemDecoration.java ================================================ //package com.chad.baserecyclerviewadapterhelper.decoration; // //import android.graphics.Rect; //import android.os.Build; //import android.util.DisplayMetrics; //import android.util.TypedValue; //import android.view.View; // //import androidx.annotation.Nullable; //import androidx.recyclerview.widget.GridLayoutManager; //import androidx.recyclerview.widget.RecyclerView; // //import com.chad.library.adapter.base.BaseSectionQuickAdapter; //import com.chad.library.adapter.base.viewholder.BaseViewHolder; //import com.chad.library.adapter.base.entity.SectionEntity; // //import java.util.ArrayList; //import java.util.List; // ///** // * 应用于RecyclerView的GridLayoutManager,水平方向上固定间距大小,从而使条目宽度自适应。
// * 配合Brvah的Section使用,不对Head生效,仅对每个Head的子Grid列表生效
// * Section Grid中Item的宽度应设为MATCH_PARAENT // * // * @author : renpeng // * @since : 2018/9/29 // */ //public class GridSectionAverageGapItemDecoration extends RecyclerView.ItemDecoration { // // private class Section { // public int startPos = 0; // public int endPos = 0; // // public int getCount() { // return endPos - startPos + 1; // } // // public boolean contains(int pos) { // return pos >= startPos && pos <= endPos; // } // // @Override // public String toString() { // return "Section{" + // "startPos=" + startPos + // ", endPos=" + endPos + // '}'; // } // } // // private float gapHorizontalDp; // private float gapVerticalDp; // private float sectionEdgeHPaddingDp; // private float sectionEdgeVPaddingDp; // private int gapHSizePx = -1; // private int gapVSizePx = -1; // private int sectionEdgeHPaddingPx; // private int eachItemHPaddingPx; //每个条目应该在水平方向上加的padding 总大小,即=paddingLeft+paddingRight // private int sectionEdgeVPaddingPx; // private List

mSectionList = new ArrayList<>(); // private BaseSectionQuickAdapter mAdapter; // private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() { // @Override // public void onChanged() { // markSections(); // } // // @Override // public void onItemRangeChanged(int positionStart, int itemCount) { // markSections(); // } // // @Override // public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { // markSections(); // } // // @Override // public void onItemRangeInserted(int positionStart, int itemCount) { // markSections(); // } // // @Override // public void onItemRangeRemoved(int positionStart, int itemCount) { // markSections(); // } // // @Override // public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { // markSections(); // } // }; // // // /** // * @param gapHorizontalDp item之间的水平间距 // * @param gapVerticalDp item之间的垂直间距 // * @param sectionEdgeHPaddingDp section左右两端的padding大小 // * @param sectionEdgeVPaddingDp section上下两端的padding大小 // */ // public GridSectionAverageGapItemDecoration(float gapHorizontalDp, float gapVerticalDp, float sectionEdgeHPaddingDp, float sectionEdgeVPaddingDp) { // this.gapHorizontalDp = gapHorizontalDp; // this.gapVerticalDp = gapVerticalDp; // this.sectionEdgeHPaddingDp = sectionEdgeHPaddingDp; // this.sectionEdgeVPaddingDp = sectionEdgeVPaddingDp; // } // // @Override // public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { // if (parent.getLayoutManager() instanceof GridLayoutManager && parent.getAdapter() instanceof BaseSectionQuickAdapter) { // GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager(); // BaseSectionQuickAdapter adapter = (BaseSectionQuickAdapter) parent.getAdapter(); // if (mAdapter != adapter) { // setUpWithAdapter(adapter); // } // int spanCount = layoutManager.getSpanCount(); // int position = parent.getChildAdapterPosition(view); // SectionEntity entity = adapter.getItem(position); // // if (entity == null || entity.isHeader()) { // //不处理header // outRect.set(0, 0, 0, 0); //// Log.w("GridAverageGapItem", "pos=" + position + "," + outRect.toShortString()); // return; // } // // Section section = findSectionLastItemPos(position); // // if (gapHSizePx < 0 || gapVSizePx < 0) { // transformGapDefinition(parent, spanCount); // } // outRect.top = gapVSizePx; // outRect.bottom = 0; // // //下面的visualPos为单个Section内的视觉Pos // int visualPos = position + 1 - section.startPos; // if (visualPos % spanCount == 1) { // //第一列 // outRect.left = sectionEdgeHPaddingPx; // outRect.right = eachItemHPaddingPx - sectionEdgeHPaddingPx; // } else if (visualPos % spanCount == 0) { // //最后一列 // outRect.left = eachItemHPaddingPx - sectionEdgeHPaddingPx; // outRect.right = sectionEdgeHPaddingPx; // } else { // outRect.left = gapHSizePx - (eachItemHPaddingPx - sectionEdgeHPaddingPx); // outRect.right = eachItemHPaddingPx - outRect.left; // } // // if (visualPos - spanCount <= 0) { // //第一行 // outRect.top = sectionEdgeVPaddingPx; // } // // if (isLastRow(visualPos, spanCount, section.getCount())) { // //最后一行 // outRect.bottom = sectionEdgeVPaddingPx; //// Log.w("GridAverageGapItem", "last row pos=" + position); // } //// Log.w("GridAverageGapItem", "pos=" + position + ",vPos=" + visualPos + "," + outRect.toShortString()); // } else { // super.getItemOffsets(outRect, view, parent, state); // } // } // // private void setUpWithAdapter(BaseSectionQuickAdapter adapter) { // if (mAdapter != null) { // mAdapter.unregisterAdapterDataObserver(mDataObserver); // } // mAdapter = adapter; // mAdapter.registerAdapterDataObserver(mDataObserver); // markSections(); // } // // private void markSections() { // if (mAdapter != null) { // BaseSectionQuickAdapter adapter = mAdapter; // mSectionList.clear(); // SectionEntity sectionEntity = null; // Section section = new Section(); // for (int i = 0, size = adapter.getItemCount(); i < size; i++) { // sectionEntity = adapter.getItem(i); // if (sectionEntity != null && sectionEntity.isHeader()) { // //找到新Section起点 // if (section != null && i != 0) { // //已经有待添加的section // section.endPos = i - 1; // mSectionList.add(section); // } // section = new Section(); // section.startPos = i + 1; // } else { // section.endPos = i; // } // } // //处理末尾情况 // if (!mSectionList.contains(section)) { // mSectionList.add(section); // } // //// Log.w("GridAverageGapItem", "section list=" + mSectionList); // } // } // // private void transformGapDefinition(RecyclerView parent, int spanCount) { // DisplayMetrics displayMetrics = new DisplayMetrics(); // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // parent.getDisplay().getMetrics(displayMetrics); // } // gapHSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gapHorizontalDp, displayMetrics); // gapVSizePx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gapVerticalDp, displayMetrics); // sectionEdgeHPaddingPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, sectionEdgeHPaddingDp, displayMetrics); // sectionEdgeVPaddingPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, sectionEdgeVPaddingDp, displayMetrics); // eachItemHPaddingPx = (sectionEdgeHPaddingPx * 2 + gapHSizePx * (spanCount - 1)) / spanCount; // } // // private Section findSectionLastItemPos(int curPos) { // for (Section section : mSectionList) { // if (section.contains(curPos)) { // return section; // } // } // return null; // } // // private boolean isLastRow(int visualPos, int spanCount, int sectionItemCount) { // int lastRowCount = sectionItemCount % spanCount; // lastRowCount = lastRowCount == 0 ? spanCount : lastRowCount; // return visualPos > sectionItemCount - lastRowCount; // } //} ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/ClickEntity.java ================================================ /* ******************************* Copyright (c)*********************************\ ** ** (c) Copyright 2015, Allen, china, shanghai ** All Rights Reserved ** ** ** **-----------------------------------版本信息------------------------------------ ** 版 本: V0.1 ** **------------------------------------------------------------------------------ ********************************End of Head************************************\ */ package com.chad.baserecyclerviewadapterhelper.entity; /** * 文 件 名: ClickEntity * 创 建 人: Allen * 创建日期: 16/11/1 22:16 * 邮 箱: AllenCoder@126.com * 修改时间: * 修改备注: */ public class ClickEntity { public static final int CLICK_ITEM_VIEW = 1; public static final int CLICK_ITEM_CHILD_VIEW = 2; public static final int LONG_CLICK_ITEM_VIEW = 3; public static final int LONG_CLICK_ITEM_CHILD_VIEW = 4; private final int type; public ClickEntity(final int type) { this.type = type; } public int getItemType() { return type; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/DiffEntity.java ================================================ package com.chad.baserecyclerviewadapterhelper.entity; import java.util.Objects; public class DiffEntity { private int id; private String title; private String content; private String date; public DiffEntity(int id, String title, String content, String date) { this.id = id; this.title = title; this.content = content; this.date = date; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getDate() { return date; } public void setDate(String date) { this.date = date; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof DiffEntity)) return false; DiffEntity that = (DiffEntity) o; return id == that.id && Objects.equals(title, that.title) && Objects.equals(content, that.content) && Objects.equals(date, that.date); } @Override public int hashCode() { return Objects.hash(id, title, content, date); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/GroupDemoEntity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.entity import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class GroupDemoEntity( @Json(name = "group_name") val groupName: String, @Json(name = "group_list") val groupList: List, ) { @JsonClass(generateAdapter = true) data class Group( @Json(name = "title") val title: String, @Json(name = "content") val content: String, ) } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/HomeEntity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.entity /** * @author: limuyang * @date: 2019-12-06 * @Description: */ data class HomeEntity( val name: String = "", val activity: Class<*>? = null, val imageResource: Int = 0, val sectionTitle: String = "" ) { val isSection: Boolean get() = sectionTitle.isNotBlank() } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/Movie.java ================================================ package com.chad.baserecyclerviewadapterhelper.entity; /** * Created by luoxiongwen on 16/10/24. */ public class Movie { public String name; public int length; public int price; public String content; public Movie(String name, int length, int price, String content) { this.length = length; this.name = name; this.price = price; this.content=content; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/MoviePresenter.java ================================================ package com.chad.baserecyclerviewadapterhelper.entity; import android.view.View; import com.chad.baserecyclerviewadapterhelper.utils.Tips; /** * Created by luoxiongwen on 16/10/24. */ public class MoviePresenter { public void buyTicket(View view, Movie movie) { Tips.show("buy ticket: " + movie.name); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/NodeEntity.kt ================================================ package com.chad.baserecyclerviewadapterhelper.entity /** * @author LiMuYang * @date 2025/9/5 * @description */ data class NodeEntity( val title: String, val childNode: List?, ) { data class Level2NodeEntity( val title: String, val childNode: List?, ) { data class Level3NodeEntity( val title: String, ) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/entity/Status.java ================================================ package com.chad.baserecyclerviewadapterhelper.entity; /** * https://github.com/CymChad/BaseRecyclerViewAdapterHelper */ public class Status { private boolean isRetweet; private String text; private String userName; private int userAvatar; private String createdAt; public boolean isRetweet() { return isRetweet; } public void setRetweet(boolean retweet) { isRetweet = retweet; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getUserAvatar() { return userAvatar; } public void setUserAvatar(int userAvatar) { this.userAvatar = userAvatar; } public String getCreatedAt() { return createdAt; } public void setCreatedAt(String createdAt) { this.createdAt = createdAt; } @Override public String toString() { return "Status{" + "isRetweet=" + isRetweet + ", text='" + text + '\'' + ", userName='" + userName + '\'' + ", userAvatar='" + userAvatar + '\'' + ", createdAt='" + createdAt + '\'' + '}'; } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/utils/AppUtils.kt ================================================ package com.chad.baserecyclerviewadapterhelper.utils import android.app.Application object AppUtils { private lateinit var mApplication: Application val app: Application get() = mApplication fun init(application: Application) { mApplication = application } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/utils/ClickableMovementMethod.java ================================================ package com.chad.baserecyclerviewadapterhelper.utils; import android.text.Layout; import android.text.Selection; import android.text.Spannable; import android.text.method.BaseMovementMethod; import android.text.style.ClickableSpan; import android.view.MotionEvent; import android.widget.TextView; public class ClickableMovementMethod extends BaseMovementMethod { private static ClickableMovementMethod sInstance; public static ClickableMovementMethod getInstance() { if (sInstance == null) { sInstance = new ClickableMovementMethod(); } return sInstance; } @Override public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { int action = event.getActionMasked(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { int x = (int) event.getX(); int y = (int) event.getY(); x -= widget.getTotalPaddingLeft(); y -= widget.getTotalPaddingTop(); x += widget.getScrollX(); y += widget.getScrollY(); Layout layout = widget.getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); if (link.length > 0) { if (action == MotionEvent.ACTION_UP) { link[0].onClick(widget); } else { Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0])); } return true; } else { Selection.removeSelection(buffer); } } return false; } @Override public void initialize(TextView widget, Spannable text) { Selection.removeSelection(text); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/utils/Ext.kt ================================================ package com.chad.baserecyclerviewadapterhelper.utils import android.view.Window import androidx.core.view.WindowCompat /** * 设置状态栏高亮模式 */ inline var Window.statusBarLightMode: Boolean set(value) { WindowCompat.getInsetsController(this, decorView).isAppearanceLightStatusBars = value } get() { return WindowCompat.getInsetsController(this, decorView).isAppearanceLightStatusBars } /** * dp 转 px */ inline val Int.dp: Int get() { return (this * AppUtils.app.resources.displayMetrics.density + 0.5f).toInt() } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/utils/Tips.java ================================================ package com.chad.baserecyclerviewadapterhelper.utils; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.RoundRectShape; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; public class Tips { /** * 显示 Toast * @param message 提示信息 */ public static void show(String message) { show(message, Toast.LENGTH_SHORT); } /** * 显示 Toast * @param message 提示信息 * @param duration 显示时间长短 */ public static void show(String message, int duration) { Toast toast = new Toast(AppUtils.INSTANCE.getApp()); toast.setDuration(duration); toast.setGravity(Gravity.CENTER, 0, 0); toast.setView(createTextToastView(message)); toast.show(); } /** * 创建自定义 Toast View * * @param message 文本消息 * @return View */ private static View createTextToastView(String message) { // 画圆角矩形背景 float rc = dp2px(6); RoundRectShape shape = new RoundRectShape(new float[]{rc, rc, rc, rc, rc, rc, rc, rc}, null, null); ShapeDrawable drawable = new ShapeDrawable(shape); drawable.getPaint().setColor(Color.argb(225, 240, 240, 240)); drawable.getPaint().setStyle(Paint.Style.FILL); drawable.getPaint().setAntiAlias(true); drawable.getPaint().setFlags(Paint.ANTI_ALIAS_FLAG); // 创建View FrameLayout layout = new FrameLayout(AppUtils.INSTANCE.getApp()); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); layout.setLayoutParams(layoutParams); layout.setPadding(dp2px(16), dp2px(12), dp2px(16), dp2px(12)); layout.setBackground(drawable); TextView textView = new TextView(AppUtils.INSTANCE.getApp()); textView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT)); textView.setTextSize(15); textView.setText(message); textView.setLineSpacing(dp2px(4), 1f); textView.setTextColor(Color.BLACK); layout.addView(textView); return layout; } private static int dp2px(float dpValue) { final float scale = AppUtils.INSTANCE.getApp().getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/utils/VibratorUtils.kt ================================================ package com.chad.baserecyclerviewadapterhelper.utils import android.app.Service import android.content.Context import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager /** * 震动 */ fun Context.vibrate() { if (Build.VERSION.SDK_INT >= 31) { // android 12 及以上使用新的 VibratorManager,创建 EFFECT_TICK 轻微震动(需要线性震动马达硬件支持) val manager: VibratorManager = getSystemService(VibratorManager::class.java) manager.defaultVibrator.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)) } else if (Build.VERSION.SDK_INT >= 29) { // android 10 及以上使用原 Vibrator,创建 EFFECT_TICK 轻微震动(需要线性震动马达硬件支持) val vib = getSystemService(Vibrator::class.java) as Vibrator vib.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)) } else { // 10 以下的系统,没有系统 API 驱动线性震动马达,只能创建普通震动 val vib = getSystemService(Service.VIBRATOR_SERVICE) as Vibrator //震动70毫秒 vib.vibrate(70) } } ================================================ FILE: app/src/main/java/com/chad/baserecyclerviewadapterhelper/widget/BRVAHToolbar.kt ================================================ package com.chad.baserecyclerviewadapterhelper.widget import android.content.Context import android.os.Build import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout import android.widget.RelativeLayout import androidx.core.content.ContextCompat import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import com.chad.baserecyclerviewadapterhelper.R import com.chad.baserecyclerviewadapterhelper.databinding.LayoutToolBarBinding class BRVAHToolbar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { private val binding = LayoutToolBarBinding.inflate(LayoutInflater.from(context), this) var title: String? get() = binding.tvTitle.text.toString() set(value) { binding.tvTitle.text = value } init { orientation = VERTICAL setBackgroundColor(ContextCompat.getColor(context, R.color.spinner_bg)) elevation = context.dpF(10) } fun updateFakeBarHeight(h: Int) { binding.fakeBar.updateLayoutParams { this.height = h } } fun setOnBackListener(listener: OnClickListener) { binding.ivBack.setOnClickListener(listener) } private companion object { fun Context.dp(value: Int): Int { return (value * this.resources.displayMetrics.density + 0.5f).toInt() } fun Context.dpF(value: Int): Float { return value * this.resources.displayMetrics.density + 0.5f } } } ================================================ FILE: app/src/main/res/anim/item_animation_from_bottom.xml ================================================ ================================================ FILE: app/src/main/res/anim/layout_animation_from_bottom.xml ================================================ ================================================ FILE: app/src/main/res/drawable/actionbar_bottom_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/brvah_sample_footer_loading_progress.xml ================================================ ================================================ FILE: app/src/main/res/drawable/custom_text_state_color.xml ================================================ ================================================ FILE: app/src/main/res/drawable/gv_up_fetch.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_node_down.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_node_right.xml ================================================ ================================================ FILE: app/src/main/res/drawable/selector_item_child.xml ================================================ ================================================ FILE: app/src/main/res/drawable/shape_right_top_float_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/thumb_drawable.xml ================================================ ================================================ FILE: app/src/main/res/drawable/touch_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v21/touch_bg.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_animation_use.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_choose_multiple_item_use_type.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_choose_node_use_type.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_diffutil.xml ================================================