jlin il y a 1 mois
commit
60fd4b820c
100 fichiers modifiés avec 7088 ajouts et 0 suppressions
  1. 101 0
      .gitignore
  2. 164 0
      app/build.gradle
  3. 538 0
      app/proguard-rules.pro
  4. 60 0
      app/src/main/AndroidManifest.xml
  5. 44 0
      app/src/main/java/com/bmkj/tianka/App.kt
  6. 30 0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  7. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  8. 18 0
      app/src/main/res/layout/activity_main.xml
  9. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  10. 5 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  11. BIN
      app/src/main/res/mipmap-hdpi/app_logo.png
  12. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  13. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  14. BIN
      app/src/main/res/mipmap-mdpi/app_logo.png
  15. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  16. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  17. BIN
      app/src/main/res/mipmap-xhdpi/app_logo.png
  18. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  19. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  20. BIN
      app/src/main/res/mipmap-xxhdpi/app_logo.png
  21. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  22. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  23. BIN
      app/src/main/res/mipmap-xxxhdpi/app_logo.png
  24. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  25. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  26. 16 0
      app/src/main/res/values-night/themes.xml
  27. 10 0
      app/src/main/res/values/colors.xml
  28. 3 0
      app/src/main/res/values/strings.xml
  29. 16 0
      app/src/main/res/values/themes.xml
  30. 8 0
      app/src/main/res/xml/network_security_config.xml
  31. 46 0
      base_lib.gradle
  32. 81 0
      base_module.gradle
  33. 105 0
      baselibrary/build.gradle
  34. 0 0
      baselibrary/consumer-rules.pro
  35. 21 0
      baselibrary/proguard-rules.pro
  36. 20 0
      baselibrary/src/main/AndroidManifest.xml
  37. 33 0
      baselibrary/src/main/java/com/yc/baselibrary/BaseApplication.kt
  38. 69 0
      baselibrary/src/main/java/com/yc/baselibrary/LifecycleAplication.kt
  39. 359 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseAdapter.kt
  40. 132 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseGroupAdapter.kt
  41. 45 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseSingleAdapter.kt
  42. 49 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseSingleRecyclingPageAdapter.kt
  43. 30 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseStickyHeaderAdapter.kt
  44. 137 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/RecyclingPagerAdapter.java
  45. 33 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/anim/AlphaInItemAnimator.kt
  46. 40 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/anim/ScaleInItemAnimator.kt
  47. 13 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/const/AdapterConst.kt
  48. 18 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/listener/LoadMoreAble.java
  49. 11 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/listener/OnLoadMoreListener.java
  50. 386 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/CallbackRegistry.java
  51. 139 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/ListChangeRegistry.java
  52. 173 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/ObservableAdapterList.java
  53. 72 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/ObservableList.java
  54. 22 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/AlphaBaseViewHolder.kt
  55. 58 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/AnimationBaseViewHolder.kt
  56. 53 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/BaseMutableViewHolder.kt
  57. 61 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/BaseViewHolder.java
  58. 26 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/BgVH.kt
  59. 29 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/EmptyPlaceViewHolder.java
  60. 73 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/EmptyVH.kt
  61. 160 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/MutableAdapter.kt
  62. 69 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/ObservableBaseViewHolder.kt
  63. 38 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/PaginationTool.java
  64. 10 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/PagingException.java
  65. 81 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/TitleVH.kt
  66. 19 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/ViewHolderExt.kt
  67. 55 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/DefaultLoadMoreFooterCardView.kt
  68. 56 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/DefaultLoadMoreFooterView.kt
  69. 33 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/FooterViewHolder.kt
  70. 30 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/LoadMoreFooterView.kt
  71. 28 0
      baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/model/DefaultVHModule.kt
  72. 14 0
      baselibrary/src/main/java/com/yc/baselibrary/api/WebService.kt
  73. 146 0
      baselibrary/src/main/java/com/yc/baselibrary/behavior/AppBarLayoutFixedBehavior.java
  74. 163 0
      baselibrary/src/main/java/com/yc/baselibrary/behavior/AppBarLayoutSnapScrollViewBehavior.java
  75. 56 0
      baselibrary/src/main/java/com/yc/baselibrary/behavior/AppBarLinkedBehavior.java
  76. 581 0
      baselibrary/src/main/java/com/yc/baselibrary/behavior/BottomLinkedBehavior.java
  77. 546 0
      baselibrary/src/main/java/com/yc/baselibrary/behavior/RecyclerViewBehavior.java
  78. 43 0
      baselibrary/src/main/java/com/yc/baselibrary/cache/Cache.kt
  79. 23 0
      baselibrary/src/main/java/com/yc/baselibrary/core/IEmit.kt
  80. 21 0
      baselibrary/src/main/java/com/yc/baselibrary/core/ILiveEvent.kt
  81. 47 0
      baselibrary/src/main/java/com/yc/baselibrary/core/ILoading.kt
  82. 16 0
      baselibrary/src/main/java/com/yc/baselibrary/core/IRefresh.kt
  83. 16 0
      baselibrary/src/main/java/com/yc/baselibrary/core/ITimer.kt
  84. 21 0
      baselibrary/src/main/java/com/yc/baselibrary/core/IView.kt
  85. 15 0
      baselibrary/src/main/java/com/yc/baselibrary/core/IViewEvent.kt
  86. 80 0
      baselibrary/src/main/java/com/yc/baselibrary/coroutines/MainScopeDelegate.kt
  87. 22 0
      baselibrary/src/main/java/com/yc/baselibrary/event/Channel.kt
  88. 62 0
      baselibrary/src/main/java/com/yc/baselibrary/event/LiveBus.kt
  89. 102 0
      baselibrary/src/main/java/com/yc/baselibrary/event/LiveBusEvent.kt
  90. 36 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/AppBarExt.kt
  91. 13 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/DialogExt.kt
  92. 20 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/Extension.kt
  93. 16 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/HandlerExt.kt
  94. 428 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/ImgExt.kt
  95. 80 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/JsonExt.kt
  96. 23 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/LifecycleExt.kt
  97. 6 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/LottieExt.kt
  98. 67 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/OnClickExt.kt
  99. 236 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/RecyclerViewExt.kt
  100. 88 0
      baselibrary/src/main/java/com/yc/baselibrary/ext/ShapeDrawableExt.kt

+ 101 - 0
.gitignore

@@ -0,0 +1,101 @@
+# Built application files
+*.apk
+ 
+# Files for the ART/Dalvik VM
+*.dex
+ 
+# Java class files
+*.class
+ 
+# Generated files
+bin/
+gen/
+out/
+#  Uncomment the following line in case you need and you don't have the release build type files in your app
+# release/
+ 
+# Gradle files
+.gradle/
+build/
+library_audio/src/main/jniLibs/
+# Local configuration file (sdk path, etc)
+local.properties
+oneapm.properties
+ 
+# Proguard folder generated by Eclipse
+proguard/
+ 
+# Log Files
+*.log
+ 
+# Android Studio Navigation editor temp files
+.navigation/
+ 
+# Android Studio captures folder
+captures/
+ 
+# IntelliJ
+*.iml
+/.idea/
+# .idea/workspace.xml
+# .idea/tasks.xml
+# .idea/gradle.xml
+# .idea/assetWizardSettings.xml
+# .idea/dictionaries
+# .idea/libraries
+# Android Studio 3 in .gitignore file.
+# .idea/caches
+# .idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+# .idea/navEditor.xml
+ 
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+ 
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+.cxx/
+ 
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+ 
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+ 
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+ 
+# Version control
+vcs.xml
+ 
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+financeOpenCV/build/
+financeOpenCV/.cxx/
+/app/src/main/assets/oneapm.properties
+/projectFilesBackup/.idea/workspace.xml
+/projectFilesBackup1/.idea/workspace.xml
+/projectFilesBackup2/.idea/workspace.xml
+/projectFilesBackup3/.idea/workspace.xml
+/projectFilesBackup4/.idea/workspace.xml
+/projectFilesBackup5/.idea/workspace.xml
+/projectFilesBackup6/.idea/workspace.xml
+/projectFilesBackup7/.idea/workspace.xml
+/projectFilesBackup8/.idea/workspace.xml
+/projectFilesBackup9/.idea/workspace.xml
+/java_pid56680.hprof
+/java_pid22192.hprof
+/java_pid21080.hprof

+ 164 - 0
app/build.gradle

@@ -0,0 +1,164 @@
+//****************************************
+//************ app 壳的配置文件 ************
+//****************************************
+//apply plugin: 'com.android.application'
+//apply plugin: 'kotlin-android'
+//apply plugin: 'com.alibaba.arouter'
+//apply plugin: 'kotlin-android-extensions'
+//apply plugin: 'kotlin-kapt'
+//apply plugin: 'com.google.gms.google-services'
+//apply plugin: 'com.google.firebase.crashlytics'
+plugins {
+    id 'com.android.application'
+    id 'kotlin-android'
+    id 'com.alibaba.arouter'
+    id 'kotlin-android-extensions'
+    id 'kotlin-kapt'
+}
+import com.yc.buildsrc.*
+
+android {
+    compileSdkVersion ProjectBuildConfig.compileSdkVersion
+    buildToolsVersion ProjectBuildConfig.buildToolsVersion
+
+    defaultConfig {
+        applicationId ProjectBuildConfig.applicationId
+        minSdkVersion ProjectBuildConfig.minSdkVersion
+        targetSdkVersion ProjectBuildConfig.targetSdkVersion
+        versionCode ProjectBuildConfig.versionCode
+        versionName ProjectBuildConfig.versionName
+        testInstrumentationRunner DependencyConfig.AndroidX.AndroidJUnitRunner
+        multiDexEnabled true
+        flavorDimensions "versionCode"
+        manifestPlaceholders = [qqappid: ProjectBuildConfig.applicationId]
+        ndk {
+            // 设置支持的SO库架构
+            abiFilters 'armeabi-v7a', 'arm64-v8a'
+//            abiFilters 'armeabi-v7a',"arm64-v8a" ,'x86_64','x86'
+        }
+    }
+    signingConfigs {
+        release {
+            storeFile file('../keystores/myimsdk.jks')
+            keyAlias "myimsdk"
+            storePassword "123456"
+            keyPassword "123456"
+            v1SigningEnabled true
+            v2SigningEnabled true
+        }
+        debug {
+            storeFile file("../keystores/myimsdk.jks")
+            keyAlias "myimsdk"
+            storePassword "123456"
+            keyPassword "123456"
+            v1SigningEnabled true
+            v2SigningEnabled true
+        }
+    }
+
+    productFlavors {
+        office {}
+        vivo {}
+        huawei {}
+        oppo {}
+        huawei {}
+        xiaomi {}
+        wandoujia {}
+        yingyongbao {}
+        tianka360 {}
+    }
+
+    productFlavors.all {
+            //遍历替换所有渠道
+        flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
+    }
+
+    // 自定义打包apk的文件名
+    android.applicationVariants.all { variant ->
+        variant.outputs.all { output ->
+            if (outputFileName != null && outputFileName.endsWith('.apk')) {
+                outputFileName = "tianka" +
+                        "_${ProjectBuildConfig.versionCode}" +
+                        "_${ProjectBuildConfig.versionName}" +
+                        "_${variant.buildType.name}" +
+                        "_${variant.productFlavors[0].name}"+
+                        ".apk"
+            }
+        }
+    }
+
+    buildTypes {
+//        // 对应 ALPHA 版本
+        debug {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.ALPHA}\""
+            signingConfig signingConfigs.release
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        beta {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.BETA}\""
+//            signingConfig signingConfigs.releaseConfig
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            buildConfigField "String", "VERSION_TYPE", "\"${ProjectBuildConfig.Version.RELEASE}\""
+            signingConfig signingConfigs.release
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    lintOptions {
+        checkReleaseBuilds false
+        abortOnError false
+    }
+
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    packagingOptions {
+        exclude 'META-INF/DEPENDENCIES'
+        exclude 'META-INF/NOTICE'
+        exclude 'META-INF/LICENSE'
+        exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/NOTICE.txt'
+    }
+
+}
+repositories {
+    flatDir {
+        dirs 'libs', '../libs'
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+
+//    if (!ProjectBuildConfig.isAppMode) {
+//
+//    } else {
+    implementation project(":module_base")
+    implementation project(':module_main')
+    implementation project(':module_home')
+    implementation project(':module_me')
+    implementation project(':module_message')
+    implementation project(':module_live')
+    implementation project(':module_find')
+    implementation project(':module_follow')
+//            implementation 'com.meituan.android.walle:library:1.1.6'
+//    }
+//    implementation platform('com.google.firebase:firebase-bom:28.1.0')
+//    implementation 'com.google.firebase:firebase-crashlytics-ktx'
+//    implementation("com.google.firebase:firebase-analytics-ktx")
+    kapt DependencyConfig.GitHub.AutoServiceAnnotations
+//    implementation "io.github.didi.dokit:dokitx:3.5.0.1"
+//    implementation 'com.squareup.leakcanary:leakcanary-android:1.6.1'
+//    compile 'com.github.dreamlivemeng:Clog:1.0.2'
+//    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1'
+}

+ 538 - 0
app/proguard-rules.pro

@@ -0,0 +1,538 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+# androidX混淆
+-keep class com.google.android.material.** {*;}
+-keep class androidx.** {*;}
+-keep public class * extends androidx.**
+-keep interface androidx.** {*;}
+-dontwarn com.google.android.material.**
+-dontnote com.google.android.material.**
+-dontwarn androidx.**
+
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontskipnonpubliclibraryclassmembers
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepattributes Signature
+
+
+#qq
+-keep class * extends android.app.Dialog
+-keep class com.tencent.open.TDialog$*
+-keep class com.tencent.open.TDialog$* {*;}
+-keep class com.tencent.open.PKDialog
+-keep class com.tencent.open.PKDialog {*;}
+-keep class com.tencent.open.PKDialog$*
+-keep class com.tencent.open.PKDialog$* {*;}
+#umeng
+-keep class com.umeng.** {*;}
+-keep public class * extends com.umeng.**
+-keep public class com.umeng.fb.ui.ThreadView {
+}
+
+#R文件
+-keep class **.R$* {*;}
+#忘了可能是微博
+-dontwarn sdk.**
+-keep class sdk.** { *; }
+
+
+#微信
+-keep class com.tencent.mm.opensdk.** {
+   *;
+}
+-keep class com.tencent.wxop.** {
+   *;
+}
+-keep class com.tencent.mm.sdk.** {
+   *;
+}
+
+#jackson
+-keep class com.fasterxml.jackson.annotation.** {*;}
+
+#支付宝
+-keep class com.alipay.android.app.IAlixPay{*;}
+-keep class com.alipay.android.app.IAlixPay$Stub{*;}
+-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
+-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
+-keep class com.alipay.sdk.app.PayTask{ public *;}
+-keep class com.alipay.sdk.auth.AlipaySDK{ public *;}
+-keep class com.alipay.sdk.auth.APAuthInfo{ public *;}
+-keep class com.alipay.mobilesecuritysdk.*
+-keep class com.ut.*
+
+-keepattributes InnerClasses,EnclosingMethod
+
+
+-keep public class com.qianyu.diaochan.R$*{
+	public static final int *;
+}
+-dontwarn android.support.v4.**
+-keep class android.support.v4.** { *; }
+-dontwarn java.awt.**,javax.security.**,java.beans.**,javax.xml.**,java.util.**,org.w3c.dom.**
+-dontwarn android.net.http.**
+-keep public class * extends android.view.View {
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+    public void set*(...);
+}
+
+-keep class com.qianyu.diaochan.model.**{ *; }
+-keep class com.qianyu.diaochan.socket.**{ *; }
+
+-keepclasseswithmembernames class * {
+native <methods>;
+}
+
+-keepclasseswithmembers class * {
+public <init>(android.content.Context, android.util.AttributeSet);
+}
+-keepclasseswithmembers class * {
+public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * {
+   public <init>(org.json.JSONObject);
+}
+-keepclassmembers class * extends android.app.Activity {
+public void *(android.view.View);
+}
+-keepclassmembers enum * {
+public static **[] values();
+public static ** valueOf(java.lang.String);
+}
+-keep class * implements android.os.Parcelable {
+public static final android.os.Parcelable$Creator *;
+}
+
+#不混淆所有 Parcelable
+-keep class * implements android.os.Parcelable {*;
+}
+
+-keepclasseswithmembernames class * {
+    @butterknife.* <fields>;
+}
+
+-keepclasseswithmembernames class * {
+    @butterknife.* <methods>;
+}
+
+# Only required if you use AsyncExecutor
+-keepclassmembers class * extends de.greenrobot.event.util.ThrowableFailureEvent {
+    <init>(java.lang.Throwable);
+}
+
+-keepattributes  *Annotation*
+-keep class de.greenrobot.** {*;}
+
+
+-keepnames class * extends android.view.View
+
+-keep class * extends android.app.Fragment {
+ public void setUserVisibleHint(boolean);
+ public void onHiddenChanged(boolean);
+ public void onResume();
+ public void onPause();
+}
+-keep class androidx.fragment.app.Fragment {
+ public void setUserVisibleHint(boolean);
+ public void onHiddenChanged(boolean);
+ public void onResume();
+ public void onPause();
+}
+-keep class * extends androidx.fragment.app.Fragment {
+ public void setUserVisibleHint(boolean);
+ public void onHiddenChanged(boolean);
+ public void onResume();
+ public void onPause();
+}
+
+#mp4parser
+-keep class * implements com.coremedia.iso.boxes.Box { *; }
+-dontwarn com.coremedia.iso.boxes.**
+-dontwarn com.googlecode.mp4parser.authoring.tracks.mjpeg.**
+-dontwarn com.googlecode.mp4parser.authoring.tracks.ttml.**
+
+-assumenosideeffects class android.util.Log {
+
+public static boolean isLoggable(java.lang.String,int);
+
+public static int v(...);
+
+public static int i(...);
+
+public static int w(...);
+
+public static int d(...);
+
+public static int e(...);
+
+}
+
+#okhttp
+-dontwarn org.codehaus.**
+-dontwarn java.nio.**
+-keep class com.squareup.okhttp.** { *;}
+
+#Glide okhttp
+-keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule
+#Glide
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep class * extends com.bumptech.glide.module.AppGlideModule {
+ <init>(...);
+}
+-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
+  **[] $VALUES;
+  public *;
+}
+-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
+  *** rewind();
+}
+
+
+
+# Disabling obfuscation is useful if you collect stack traces from production crashes
+# (unless you are using a system that supports de-obfuscate the stack traces).
+-dontobfuscate
+-dontshrink
+
+# okhttp
+-keepattributes Signature
+-keepattributes *Annotation*
+-keep class okhttp3.** { *; }
+-keep interface okhttp3.** { *; }
+-dontwarn okhttp3.**
+
+# okio
+-keep class sun.misc.Unsafe { *; }
+-dontwarn java.nio.file.*
+-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
+-dontwarn okio.**
+
+##
+-dontwarn com.tencent.connect.avatar.**
+-keep class com.tencent.** {*;}
+-dontwarn com.umeng.**
+
+#RecyclerViewPager
+-keep class com.lsjwzh.widget.recyclerviewpager.**
+-dontwarn com.lsjwzh.widget.recyclerviewpager.**
+
+########--------Retrofit + RxJava--------#########
+-dontwarn retrofit2.**
+-keep class retrofit2.** { *; }
+-dontwarn sun.misc.Unsafe
+-dontwarn com.octo.android.robospice.retrofit.RetrofitJackson**
+-dontwarn retrofit2.appengine.UrlFetchClient
+-keepattributes Signature
+-keepattributes Exceptions
+-keepclasseswithmembers class * {
+    @retrofit.http.* <methods>;
+}
+-keep class com.google.gson.** { *; }
+-keep class com.google.inject.** { *; }
+-keep class org.apache.http.** { *; }
+-keep class org.apache.james.mime4j.** { *; }
+-keep class javax.inject.** { *; }
+-keep class retrofit2.** { *; }
+-keep class uk.co.senab.** { *; }
+-keep class rx.** { *; }
+-keep class retrofit2.** { *; }
+-keep class okio.** { *; }
+-keep class okhttp3.** { *; }
+-keep class u.aly.bo.** { *; }
+-dontnote org.apache.http.**
+-dontnote org.json.**
+-dontnote com.qianyu.diaochan.**
+-dontnote kankan.wheel.**
+-dontnote internal.org.apache.**
+-dontnote com.yintong.**
+-dontnote com.umeng.**
+-dontnote com.tendcloud.**
+-dontnote com.slider.library.**
+-dontnote com.sina.weibo.**
+-dontnote com.mp4parser.**
+-dontnote com.mobeta.android.**
+-dontnote org.apache.commons.**
+-dontnote com.asqw.android.**
+-dontnote fqcn.of.javascript.**
+-dontnote sun.misc.**
+-dontnote com.github.ksoichiro.**
+-dontnote com.alipay.**
+-dontnote com.baidu.**
+-dontnote com.facebook.**
+-dontnote com.google.gson.**
+-dontnote com.igexin.**
+-dontnote com.pingan.**
+-dontnote com.tencent.**
+-dontnote com.unionpay.**
+-dontnote okhttp3.**
+-dontnote org.jivesoftware.**
+-dontnote retrofit2.**
+-dontnote rx.internal.**
+-dontnote me.imid.**
+-dontnote u.aly.**
+-dontnote org.joda.**
+-dontnote android.inputmethodservice.**
+-dontnote com.handmark.pulltorefresh.**
+-dontnote com.googlecode.mp4parser.**
+-dontnote com.google.zxing.**
+-dontnote com.github.mikephil.**
+-dontnote com.example.wheelpickerlibrary.**
+-dontnote com.etsy.android.**
+-dontnote com.coremedia.iso.**
+-dontnote android.net.http.**
+-dontnote org.apache.commons.**
+
+-dontwarn org.apache.http.**
+-dontwarn android.net.http.AndroidHttpClient
+-dontwarn retrofit2.**
+
+-dontwarn sun.misc.**
+
+-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
+   long producerIndex;
+   long consumerIndex;
+}
+
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
+   rx.internal.util.atomic.LinkedQueueNode producerNode;
+   long producerNode;
+   long consumerNode;
+}
+
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
+   rx.internal.util.atomic.LinkedQueueNode consumerNode;
+ }
+
+-dontwarn okio.**
+-dontwarn retrofit2.Platform$Java8
+-keepattributes *Annotation*
+-keepattributes Signature
+-keepattributes *Annotation*,Signature
+
+#google
+-keep public class com.google.** {*;}
+
+# don't warn joda
+-dontwarn org.joda.**
+
+# ARouter
+-keep public class com.alibaba.android.arouter.routes.**{*;}
+-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
+
+
+
+#------------------  下方是共性的排除项目         ----------------
+# 方法名中含有“JNI”字符的,认定是Java Native Interface方法,自动排除
+# 方法名中含有“JRI”字符的,认定是Java Reflection Interface方法,自动排除
+
+-keepclasseswithmembers class * {
+    ... *JNI*(...);
+}
+
+-keepclasseswithmembernames class * {
+	... *JRI*(...);
+}
+
+-keep class **JNI* {*;}
+
+
+# --------------------------------------------------------------------------
+# Addidional for x5.sdk classes for apps
+-dontwarn dalvik.**
+-dontwarn com.tencent.smtt.**
+
+-keep class com.tencent.smtt.** {
+    *;
+}
+
+-keep class com.tencent.tbs.** {
+    *;
+}
+
+#protobuf
+-keep class com.google.protobuf.** {*;}
+-dontwarn com.google.protobuf.**
+
+#stetho
+-keep class com.facebook.stetho.** { *; }
+-dontwarn org.mozilla.javascript.**
+-dontwarn org.mozilla.classfile.**
+-keep class org.mozilla.javascript.** { *; }
+
+#json_path
+-keep class com.jayway.jsonpath.**{ *; }
+-dontwarn com.jayway.jsonpath.**
+-keep class org.slf4j.**{ *; }
+-dontwarn org.slf4j.**
+
+#adapter
+-keep class com.chad.library.adapter.** {
+*;
+}
+-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
+-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
+-keepclassmembers  class **$** extends com.chad.library.adapter.base.BaseViewHolder {
+     <init>(...);
+}
+
+#tutuSdk
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# keep setters in Views so that animations can still work.
+# see http://proguard.sourceforge.net/manual/examples.html#beans
+-keepclassmembers public class * extends android.view.View {
+   void set*(***);
+   *** get*();
+}
+
+# We want to keep methods in Activity that could be used in the XML attribute onClick
+-keepclassmembers class * extends android.app.Activity {
+   public void *(android.view.View);
+}
+
+# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
+
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version.  We know about them, and they are safe.
+-dontwarn android.support.**
+-dontwarn org.apache.commons.**
+
+-keep class android.support.**{ *; }
+-keep class org.apache.commons.**{ *; }
+-keep class com.nostra13.universalimageloader.**{ *; }
+-keep class it.sephiroth.android.library.exif2.**{ *; }
+
+-keep class org.lasque.tusdk.**{public *; protected *; }
+-keep class org.lasque.tusdk.core.utils.image.GifHelper{ *; }
+-keep class org.lasque.codec.**{public *; protected *; private *;}
+
+#MPAndroidChart
+-dontwarn com.github.mikephil.charting.data.
+
+##TONGDUN
+#-dontwarn android.os.**
+#-dontwarn com.android.internal.**
+#-keep class cn.tongdun.android.**{*;}
+#-keep class cn.tongdun.bugly.**{*;}
+
+
+# ========== chameleon 包下的类混淆配置 ==========
+-keep class com.didi.chameleon.** { *; }
+
+
+# ========== liveDataBus 混淆配置 ==========
+-dontwarn com.jeremyliao.liveeventbus.**
+-keep class com.jeremyliao.liveeventbus.** { *; }
+-keep class androidx.lifecycle.** { *; }
+-keep class androidx.arch.core.** { *; }
+
+ #实体类
+-keep class **.model.**{*;}
+ #实体类
+-keep class **.models.**{*;}
+
+#manifestPaser
+-keep class * implements com.xueyu.applicationproxy.ConfigModule {*;}
+
+#viewModel
+-keep class * extends com.yc.baselibrary.view.base.BaseVm{*;}
+
+# ServiceLoader support
+-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
+-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
+-keepnames class kotlinx.coroutines.android.AndroidExceptionPreHandler {}
+-keepnames class kotlinx.coroutines.android.AndroidDispatcherFactory {}
+
+# Most of volatile fields are updated with AFU and should not be mangled
+-keepclassmembernames class kotlinx.** {
+    volatile <fields>;
+}
+
+#PictureSelector 2.0
+-keep class com.luck.picture.lib.** { *; }
+
+#Ucrop
+-dontwarn com.yalantis.ucrop**
+-keep class com.yalantis.ucrop** { *; }
+-keep interface com.yalantis.ucrop** { *; }
+
+#Okio
+-dontwarn org.codehaus.mojo.animal_sniffer.*
+
+# 哪个包混淆失败,就取消对那个包的混淆
+
+-dontwarn com.android.internal.**
+-keep class com.android.internal.** { *;}
+
+-dontwarn com.android.server.**
+-keep class com.android.server.** { *;}
+
+-dontwarn com.mediatek.**
+-keep class com.mediatek.** { *;}
+
+-dontwarn android.**
+-keep class android.** { *;}
+
+-dontwarn org.apache.http.**
+-keep class org.apache.http.** { *;}
+
+-dontwarn com.dreamlive.cn.**
+-keep classcom.dreamlive.cn.**{*;}
+
+-keep public class com.bumptech.glide.integration.webp.WebpImage { *; }
+-keep public class com.bumptech.glide.integration.webp.WebpFrame { *; }
+-keep public class com.bumptech.glide.integration.webp.WebpBitmapFactory { *; }

+ 60 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.bmkj.tianka">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_LOGS" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+    <!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="com.android.vending.BILLING" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
+    <application
+        android:name=".App"
+        android:allowBackup="true"
+        android:icon="@mipmap/app_logo"
+        android:label="@string/app_name"
+        android:largeHeap = "true"
+        android:roundIcon="@mipmap/app_logo"
+        android:supportsRtl="true"
+        android:usesCleartextTraffic="true"
+        android:persistent="true"
+        android:killAfterRestore="true"
+        android:requestLegacyExternalStorage="true"
+        android:theme="@style/AppTheme.NoActionBar"
+        android:networkSecurityConfig="@xml/network_security_config"
+        tools:replace="android:theme,android:allowBackup">
+        <!-- 友盟APPKEY -->
+        <meta-data
+            android:name="UMENG_APPKEY"
+            android:value="6662bd38cac2a664de452a71" />
+        <!-- 渠道号 -->
+        <meta-data
+            android:name="UMENG_CHANNEL"
+            android:value="${UMENG_CHANNEL_VALUE}" />
+
+        <!-- 屏幕适配基准DP -->
+        <meta-data
+            android:name="design_width_in_dp"
+            android:value="360" />
+        <meta-data
+            android:name="design_height_in_dp"
+            android:value="640" />
+    </application>
+
+</manifest>

+ 44 - 0
app/src/main/java/com/bmkj/tianka/App.kt

@@ -0,0 +1,44 @@
+package com.bmkj.tianka
+
+import com.umeng.commonsdk.UMConfigure
+import com.xueyu.kotlinextlibrary.manifest
+import com.yc.module_base.ModuleApplication
+import com.yc.module_base.manager.ContextManager
+import im.zego.bytedance.BeautyManager
+
+
+/** Created by yc on 2021/9/10
+ **/
+class App : ModuleApplication() {
+    override fun onCreate() {
+        super.onCreate()
+//        ARouter.init(this) // 尽可能早,推荐在Application中初始化
+//        registerActivityLifecycleCallbacks(ActivityManager.instance)
+//        DoKit.Builder(this)
+//            .build()
+//        val logPath =
+//            FileUtil.getHljBookAlbumDir(this).absolutePath
+//         Log.e("loadftr",logPath)
+//        LogCook.getInstance() // 单例获取LogCook实例
+//            .setLogPath(logPath) //设置日志保存路径
+//            .setLogName("test.log") //设置日志文件名
+//            .isOpen(true) //是否开启输出日志
+//            .isSave(true) //是否保存日志
+//            .initialize() //完成初始化Crash监听
+//        LogCollector.getInstance(this).start();
+
+//        if (LeakCanary.isInAnalyzerProcess(this)) {
+//            // This process is dedicated to LeakCanary for heap analysis.
+//            // You should not init your app in this process.
+//            return;
+//        }
+//        LeakCanary.install(this);
+        UMConfigure.preInit(
+            this,
+            "69649a458560e3487222d4ef",
+            manifest("UMENG_CHANNEL")
+        )
+        ContextManager.appContext = this
+        BeautyManager.instance.init(this)
+    }
+}

+ 30 - 0
app/src/main/res/drawable-v24/ic_launcher_foreground.xml

@@ -0,0 +1,30 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="85.84757"
+                android:endY="92.4963"
+                android:startX="42.9492"
+                android:startY="49.59793"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>

+ 170 - 0
app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#3DDC84"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

+ 18 - 0
app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 5 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

+ 5 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>

BIN
app/src/main/res/mipmap-hdpi/app_logo.png


BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-mdpi/app_logo.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xhdpi/app_logo.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xxhdpi/app_logo.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
app/src/main/res/mipmap-xxxhdpi/app_logo.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 16 - 0
app/src/main/res/values-night/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.Soha" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_200</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_200</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 10 - 0
app/src/main/res/values/colors.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">甜咖直播</string>
+</resources>

+ 16 - 0
app/src/main/res/values/themes.xml

@@ -0,0 +1,16 @@
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.Soha" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
+        <!-- Customize your theme here. -->
+    </style>
+</resources>

+ 8 - 0
app/src/main/res/xml/network_security_config.xml

@@ -0,0 +1,8 @@
+<network-security-config>
+<!--    <base-config cleartextTrafficPermitted="true"/>-->
+    <base-config cleartextTrafficPermitted="true">
+        <trust-anchors>
+            <certificates src="system"/>
+        </trust-anchors>
+    </base-config>
+</network-security-config>

+ 46 - 0
base_lib.gradle

@@ -0,0 +1,46 @@
+import com.yc.buildsrc.*
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-android-extensions'
+android {
+    compileSdkVersion ProjectBuildConfig.compileSdkVersion
+    buildToolsVersion ProjectBuildConfig.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion ProjectBuildConfig.minSdkVersion
+        targetSdkVersion ProjectBuildConfig.targetSdkVersion
+        versionCode ProjectBuildConfig.versionCode
+        versionName ProjectBuildConfig.versionName
+        manifestPlaceholders = [qqappid: ProjectBuildConfig.applicationId]
+
+        consumerProguardFiles "consumer-rules.pro"
+
+        ndk {
+            // 设置支持的SO库架构
+//            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+            abiFilters 'armeabi-v7a',"arm64-v8a"
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = "1.8"
+    }
+
+}
+
+repositories {
+    flatDir {
+        dirs 'libs', '../libs'
+    }
+}
+kapt {
+    arguments {
+        arg("AROUTER_MODULE_NAME", project.getName())
+    }
+}

+ 81 - 0
base_module.gradle

@@ -0,0 +1,81 @@
+import com.yc.buildsrc.*
+
+if (ProjectBuildConfig.isAppMode) {
+    apply plugin: 'com.android.application'
+} else {
+    apply plugin: 'com.android.library'
+}
+
+
+apply plugin:'kotlin-android'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'kotlin-android-extensions'
+
+
+android {
+    compileSdkVersion ProjectBuildConfig.compileSdkVersion
+    buildToolsVersion ProjectBuildConfig.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion ProjectBuildConfig.minSdkVersion
+        targetSdkVersion ProjectBuildConfig.targetSdkVersion
+        versionCode ProjectBuildConfig.versionCode
+        versionName ProjectBuildConfig.versionName
+        testInstrumentationRunner DependencyConfig.AndroidX.AndroidJUnitRunner
+        manifestPlaceholders = [qqappid: ProjectBuildConfig.applicationId]
+        ndk {
+            // 设置支持的SO库架构
+            //abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+            abiFilters 'armeabi-v7a',"arm64-v8a"
+        }
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = [moduleName: project.getName()]
+            }
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = "1.8"
+    }
+
+    buildFeatures {
+        viewBinding = true
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile 'src/main/AndroidManifest.xml'
+            java {
+                //排除debug文件夹下的所有文件
+                exclude 'debug/**'
+            }
+        }
+    }
+}
+
+kapt {
+    arguments {
+        arg("AROUTER_MODULE_NAME", project.name)
+        arg("eventBusIndex", "${ProjectBuildConfig.applicationId}.eventbus.index.${project.name}EventIndex")
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+//    api project(path: ':commonlibrary')
+
+    testImplementation DependencyConfig.Android.Junit
+    androidTestImplementation DependencyConfig.AndroidX.TestExtJunit
+    androidTestImplementation DependencyConfig.AndroidX.TestEspresso
+
+    kapt DependencyConfig.GitHub.ARouteCompiler
+    kapt DependencyConfig.GitHub.AutoServiceAnnotations
+    kapt DependencyConfig.JetPack.LifecycleCompilerAPT
+}

+ 105 - 0
baselibrary/build.gradle

@@ -0,0 +1,105 @@
+apply from: '../base_lib.gradle'
+
+import com.yc.buildsrc.*
+
+android {
+    resourcePrefix "base_"
+
+    sourceSets {
+        main {
+            // 添加 libs 文件夹作为源文件夹
+            java.srcDirs += 'libs'
+        }
+    }
+    buildTypes {
+        release {
+            versionNameSuffix '1'
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar"])
+
+    api project(path: ':networklibrary')
+    api project(path: ':refreshlibrary')
+    api project(path: ':loadinglibrary')
+    api project(path: ':download')
+    api project(path: ':wheel-lib')
+    api project(path: ':libWebview')
+    api DependencyConfig.AndroidX.CoreKtx
+    api DependencyConfig.AndroidX.AppCompat
+    api DependencyConfig.AndroidX.ConstraintLayout
+    api DependencyConfig.AndroidX.ActivityKtx
+    api DependencyConfig.AndroidX.FragmentKtx
+    api DependencyConfig.AndroidX.MultiDex
+
+    api DependencyConfig.Android.Material
+
+    api DependencyConfig.Kotlin.Kotlin
+    api DependencyConfig.Kotlin.CoroutinesCore
+    api DependencyConfig.Kotlin.CoroutinesAndroid
+
+    api DependencyConfig.JetPack.ViewModel
+    api DependencyConfig.JetPack.ViewModelSavedState
+    api DependencyConfig.JetPack.LiveData
+    api DependencyConfig.JetPack.Lifecycle
+    api DependencyConfig.JetPack.Room
+    api DependencyConfig.JetPack.Worker
+    kapt DependencyConfig.JetPack.RoomCompiler
+    api DependencyConfig.JetPack.RoomKt
+    api DependencyConfig.GitHub.Gson
+    api DependencyConfig.GitHub.MMKV
+    api DependencyConfig.GitHub.AutoSize
+    api DependencyConfig.GitHub.RecyclerViewAdapter
+    api DependencyConfig.GitHub.PermissionX
+    api DependencyConfig.GitHub.AutoService
+    api DependencyConfig.GitHub.Glide
+    api DependencyConfig.GitHub.EventBus
+    api DependencyConfig.GitHub.Dialog
+    api DependencyConfig.GitHub.StickyList
+    api DependencyConfig.GitHub.MagicIndicator
+    api DependencyConfig.GitHub.KotlinExt
+    api DependencyConfig.GitHub.BlankUtils
+    api DependencyConfig.GitHub.GlobalApp
+    api DependencyConfig.GitHub.VideoView
+    api DependencyConfig.GitHub.Banner
+    api DependencyConfig.GitHub.LoadSir
+    api DependencyConfig.GitHub.Refresh
+    api DependencyConfig.GitHub.Refresh_header
+    api DependencyConfig.GitHub.PictureSelector
+    api DependencyConfig.GitHub.Ucrop
+    api DependencyConfig.GitHub.compress
+    api DependencyConfig.GitHub.JodaTime
+    api DependencyConfig.GitHub.Lottie
+//    api DependencyConfig.GitHub.Socket
+    api DependencyConfig.GitHub.Fresco
+    api DependencyConfig.GitHub.AlphaPlayer
+    api DependencyConfig.GitHub.zego
+    api DependencyConfig.GitHub.RoundImage
+    api DependencyConfig.GitHub.Webp
+    api DependencyConfig.GitHub.AppUpdate
+//    api DependencyConfig.GitHub.CityPicker
+    api DependencyConfig.GitHub.WheelPicker
+ //   api DependencyConfig.GitHub.AddressPicker
+    api DependencyConfig.GitHub.SVGAPlayer
+//    api DependencyConfig.GitHub.XBanner
+//    api DependencyConfig.GitHub.ProgressBar
+    api DependencyConfig.GitHub.ShapeBlur
+    api DependencyConfig.GitHub.EasyFloat
+//    api DependencyConfig.GitHub.Network
+    api DependencyConfig.SDK.UMSdkCommon
+    api DependencyConfig.SDK.UMSdkAsms
+    api DependencyConfig.SDK.UMSdkLink
+    api DependencyConfig.SDK.UAPM
+    api DependencyConfig.SDK.Share
+    api DependencyConfig.SDK.WxShare
+    api DependencyConfig.SDK.WXSDK
+    api DependencyConfig.SDK.QQShare
+    api DependencyConfig.SDK.QQSDK
+    api 'io.reactivex.rxjava2:rxjava:2.2.6'
+    api 'io.reactivex.rxjava2:rxandroid:2.1.1'
+    api 'com.google.android:flexbox:2.0.1'
+    api 'com.smallbuer:jsbridge:1.0.7'
+
+}

+ 0 - 0
baselibrary/consumer-rules.pro


+ 21 - 0
baselibrary/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# 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 *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 20 - 0
baselibrary/src/main/AndroidManifest.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.yc.baselibrary">
+    <uses-permission android:name="android.permission.CAMERA" />
+    <application>
+        <activity android:name=".view.webview.WebViewActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait"
+            android:windowSoftInputMode="adjustResize"  >
+        </activity>
+        <activity android:name=".view.webview.NormalWebView"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait"  >
+        </activity>
+        <activity android:name=".view.webview.WebViewJsActivity"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait"  >
+        </activity>
+    </application>
+</manifest>

+ 33 - 0
baselibrary/src/main/java/com/yc/baselibrary/BaseApplication.kt

@@ -0,0 +1,33 @@
+package com.yc.baselibrary
+
+import android.app.Application
+import android.content.Context
+
+/**
+ * 初始化应用程序
+ * @author zhouhuan
+ * @time 2020/11/30 23:04
+ */
+open class BaseApplication : Application() {
+    override fun onCreate() {
+        super.onCreate()
+        instance = this
+//        Thread.setDefaultUncaughtExceptionHandler(CrashHandler());
+
+    }
+
+
+    companion object {
+        const val TAG = "BaseApplication"
+
+        lateinit var instance: BaseApplication
+            private set
+
+        @JvmStatic
+        fun getContext(): Context = instance.applicationContext
+    }
+
+    fun getInstance(): BaseApplication {
+        return instance
+    }
+}

+ 69 - 0
baselibrary/src/main/java/com/yc/baselibrary/LifecycleAplication.kt

@@ -0,0 +1,69 @@
+package com.yc.baselibrary
+
+import android.app.Activity
+import android.os.Bundle
+import android.util.Log
+import com.jeremyliao.liveeventbus.LiveEventBus
+import com.yc.baselibrary.event.LiveBusEvent
+import com.yc.baselibrary.manager.ActivityManager
+
+/** Created by yc on 2021/11/22
+ **/
+open class LifecycleApplication : BaseApplication() {
+    private var activityAount = 0
+    private var isForeground: Boolean = false
+    override fun onCreate() {
+        super.onCreate()
+        registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
+            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
+
+            }
+
+            override fun onActivityStarted(activity: Activity) {
+                if (activityAount == 0) {
+                    Log.e("awasdawdsd", "onActivityStarted")
+                    LiveEventBus
+                        .get(LiveBusEvent::class.java)
+                        .post(
+                            LiveBusEvent(
+                                LiveBusEvent.LiveBusEventType.BACK_FOREGROUND,
+                                true
+                            )
+                        )
+                }
+                activityAount++;
+            }
+
+            override fun onActivityResumed(activity: Activity) {
+                ActivityManager.add(activity)
+                ActivityManager.setCurrentActivity(activity)
+            }
+
+            override fun onActivityPaused(activity: Activity) {
+            }
+
+            override fun onActivityStopped(activity: Activity) {
+                activityAount--;
+                if (activityAount == 0) {
+                    Log.e("awasdawdsd", "onActivityStopped")
+                    LiveEventBus
+                        .get(LiveBusEvent::class.java)
+                        .post(
+                            LiveBusEvent(
+                                LiveBusEvent.LiveBusEventType.ENTER_BACKGROUND,
+                                true
+                            )
+                        )
+                }
+            }
+
+            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
+            }
+
+            override fun onActivityDestroyed(activity: Activity) {
+            }
+
+        })
+    }
+
+}

+ 359 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseAdapter.kt

@@ -0,0 +1,359 @@
+package com.yc.baselibrary.adapter
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
+import com.yc.baselibrary.adapter.const.AdapterConst.LOAD_MORE_FLAG
+import com.yc.baselibrary.adapter.const.AdapterConst.LOAD_MORE_FOOTER
+import com.yc.baselibrary.adapter.listener.LoadMoreAble
+import com.yc.baselibrary.adapter.listener.OnLoadMoreListener
+import com.yc.baselibrary.adapter.observable.ObservableAdapterList
+import com.yc.baselibrary.adapter.observable.ObservableList
+import com.yc.baselibrary.adapter.viewHolder.AnimationBaseViewHolder
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+import com.yc.baselibrary.adapter.viewHolder.EmptyPlaceViewHolder
+import com.yc.baselibrary.adapter.viewHolder.PaginationTool
+import com.yc.baselibrary.adapter.viewHolder.footer.DefaultLoadMoreFooterView
+import com.yc.baselibrary.adapter.viewHolder.footer.FooterViewHolder
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_DEFAULT
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_END
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_ERROR
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_LOADING
+import com.yc.baselibrary.adapter.viewHolder.setFullSpan
+
+/**
+ * BaseAdapter
+ *
+ * @author wm
+ * @date 20-7-29
+ */
+
+fun Int.isFooterType() = (this == LOAD_MORE_FOOTER)
+
+abstract class BaseAdapter<VH : BaseViewHolder<*>>(
+    val observableList: ObservableAdapterList<*>,
+    val draggable: Boolean = false,
+    private val dragType: Int = ItemTouchHelper.UP or ItemTouchHelper.DOWN
+) :
+    RecyclerView.Adapter<BaseViewHolder<*>>(), LoadMoreAble {
+
+    open var isOpenAnimation = true
+
+    protected val extraData = hashMapOf<String, @JvmSuppressWildcards Any>()
+
+    private var lastPosition = -1
+
+    private var preLoadNumber = 5
+    private var loadingType = 0
+    private var onLoadMoreListener: OnLoadMoreListener? = null
+    private var hasLoad = false
+    private var loadMoreFooterView: LoadMoreFooterView = DefaultLoadMoreFooterView()
+
+    var recyclerView: RecyclerView? = null
+
+    var selectedItemPosition = RecyclerView.NO_POSITION
+
+    private val listener = object :
+        ObservableList.OnListChangedCallback<ObservableList<*>>() {
+        override fun onChanged(sender: ObservableList<*>?) {
+            onNotifyDataSetChanged(sender)
+        }
+
+        override fun onItemRangeRemoved(
+            sender: ObservableList<*>?,
+            positionStart: Int,
+            itemCount: Int
+        ) {
+            onNotifyItemRangeRemoved(sender, positionStart, itemCount)
+        }
+
+        override fun onItemRangeMoved(
+            sender: ObservableList<*>?,
+            fromPosition: Int,
+            toPosition: Int,
+            itemCount: Int
+        ) {
+            this@BaseAdapter.notifyItemMoved(fromPosition, toPosition)
+        }
+
+        override fun onItemRangeInserted(
+            sender: ObservableList<*>?,
+            positionStart: Int,
+            itemCount: Int
+        ) {
+            onNotifyItemRangeInserted(sender, positionStart, itemCount)
+        }
+
+        override fun onItemRangeChanged(
+            sender: ObservableList<*>?,
+            positionStart: Int,
+            itemCount: Int
+        ) {
+            onNotifyItemRangeChanged(sender, positionStart, itemCount)
+        }
+    }
+
+    /**
+     * @param sender
+     */
+    open fun onNotifyDataSetChanged(sender: ObservableList<*>?) {
+        notifyDataSetChanged()
+    }
+
+    /**
+     * @param sender
+     * @param positionStart
+     * @param itemCount
+     */
+    open fun onNotifyItemRangeRemoved(
+        sender: ObservableList<*>?,
+        positionStart: Int,
+        itemCount: Int
+    ) {
+        if (sender.isNullOrEmpty()) {
+            lastPosition = -1
+        }
+        notifyItemRangeRemoved(positionStart, itemCount)
+    }
+
+    /**
+     * @param sender
+     * @param positionStart
+     * @param itemCount
+     */
+    open fun onNotifyItemRangeInserted(
+        sender: ObservableList<*>?,
+        positionStart: Int,
+        itemCount: Int
+    ) {
+        notifyItemRangeInserted(positionStart, itemCount)
+    }
+
+    /**
+     * @param sender
+     * @param positionStart
+     * @param itemCount
+     */
+    open fun onNotifyItemRangeChanged(
+        sender: ObservableList<*>?,
+        positionStart: Int,
+        itemCount: Int
+    ) {
+        notifyItemRangeChanged(positionStart, itemCount)
+    }
+
+    init {
+        observableList.addOnListChangedCallback(listener)
+    }
+
+    override fun getItemCount() = observableList.size + getLoadMoreViewCount()
+
+    private fun getLoadMoreViewPosition() = itemCount - 1
+
+    protected open fun getLoadMoreViewCount() =
+        if (onLoadMoreListener == null || observableList.size == 0) 0 else 1
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> =
+        when (viewType) {
+            LOAD_MORE_FOOTER -> FooterViewHolder(parent, loadMoreFooterView).also {
+                it.setFullSpan()
+            }
+            else -> onCreateViewHolder(parent, viewType, LOAD_MORE_FLAG).also {
+                it.extras = extraData
+            }
+        }
+
+    fun setLoadMoreFooter(loadMore: LoadMoreFooterView) {
+        this.loadMoreFooterView = loadMore
+    }
+
+    open fun onCreateViewHolder(
+        parent: ViewGroup, viewType: Int, flag: Int = LOAD_MORE_FLAG
+    ): BaseViewHolder<*> = EmptyPlaceViewHolder(parent)
+
+    override fun getItemViewType(position: Int) =
+        if (getLoadMoreViewCount() != 0 && position == getLoadMoreViewPosition()) LOAD_MORE_FOOTER else getItemViewType(
+            position,
+            LOAD_MORE_FLAG
+        )
+
+    open fun getItemViewType(position: Int, flag: Int = LOAD_MORE_FLAG): Int = 0
+
+    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
+        if (isOpenAnimation) {
+            if (holder is AnimationBaseViewHolder) {
+                val animationPosition = holder.showAnimator(lastPosition)
+                if (animationPosition != -2) {
+                    lastPosition = animationPosition
+                }
+            }
+        }
+        autoLoadMore(position)
+        if (getLoadMoreViewCount() != 0 && getLoadMoreViewPosition() == position && holder is FooterViewHolder) {
+            holder.setView(loadingType)
+        } else {
+            onBindViewHolder(holder, position, LOAD_MORE_FLAG)
+        }
+    }
+
+    open fun onBindViewHolder(
+        holder: BaseViewHolder<*>,
+        position: Int,
+        flag: Int = LOAD_MORE_FLAG
+    ) {
+
+    }
+
+    private fun autoLoadMore(position: Int): Boolean {
+        if (!hasLoad) {
+            return false
+        }
+        if (onLoadMoreListener == null || observableList.size == 0) {
+            return false
+        }
+        if (position < itemCount - preLoadNumber) {
+            return false
+        }
+        if (loadingType != STATUS_DEFAULT) {
+            return false
+        }
+        loadingType = STATUS_LOADING
+        onLoadMoreListener?.onLoadMore()
+        return true
+    }
+
+    fun setPreLoadNumber(preLoadNumber: Int) {
+        if (preLoadNumber > 1) {
+            this.preLoadNumber = preLoadNumber
+        }
+    }
+
+    override fun setOnLoadMoreListener(listener: OnLoadMoreListener) {
+        this.onLoadMoreListener = listener
+    }
+
+    override fun finishLoadMore() {
+        hasLoad = true
+        loadingType = STATUS_DEFAULT
+        if (getLoadMoreViewCount() > 0) {
+            notifyItemChanged(getLoadMoreViewPosition())
+        }
+    }
+
+    override fun finishLoadMoreWithNoMoreData() {
+        hasLoad = true
+        loadingType = STATUS_END
+        if (getLoadMoreViewCount() > 0) {
+            notifyItemChanged(getLoadMoreViewPosition())
+        }
+    }
+
+    override fun finishLoadError() {
+        hasLoad = true
+        loadingType = STATUS_ERROR
+        if (getLoadMoreViewCount() > 0) {
+            notifyItemChanged(getLoadMoreViewPosition())
+        }
+    }
+
+    private var mSpanSizeLookup: SpanSizeLookup? = null
+
+    interface SpanSizeLookup {
+        fun getSpanSize(gridLayoutManager: GridLayoutManager, position: Int): Int
+    }
+
+    open fun setSpanSizeLookup(spanSizeLookup: SpanSizeLookup?) {
+        mSpanSizeLookup = spanSizeLookup
+    }
+
+    open fun isFixedViewType(type: Int): Boolean = type == LOAD_MORE_FOOTER
+
+    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
+        this.recyclerView = recyclerView
+        this.recyclerView?.removeOnScrollListener(onPageScrolledListener)
+        this.recyclerView?.addOnScrollListener(onPageScrolledListener)
+        val manager = recyclerView.layoutManager
+        if (manager is GridLayoutManager) {
+            manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+                override fun getSpanSize(position: Int): Int {
+                    val type = getItemViewType(position)
+                    return if (mSpanSizeLookup == null) {
+                        if (isFixedViewType(type)) manager.spanCount else 1
+                    } else {
+                        if (isFixedViewType(type)) manager.spanCount else mSpanSizeLookup?.getSpanSize(
+                            manager,
+                            position
+                        ) ?: 1
+                    }
+                }
+            }
+        }
+    }
+
+    fun addExtraData(key: String, value: Any) {
+        extraData[key] = value
+    }
+
+    fun removeExtraData(key: String) {
+        extraData.remove(key)
+    }
+
+    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
+        observableList.removeOnListChangedCallback(listener)
+        extraData.clear()
+//        this.touchHelper = null
+        this.recyclerView?.removeOnScrollListener(onPageScrolledListener)
+        this.recyclerView = null
+    }
+
+    fun resetLastPosition(lastPosition: Int = -1) {
+        this.lastPosition = lastPosition
+    }
+
+    /****************************************drag**************************************************/
+//    var touchHelper: ItemTouchHelper? = null
+//        private set
+//    private var dragHelper: DragHelper? = null
+//    var onDragStateChanged: RVDragStateChange? = null
+//    var onDragEnd: ((fromPos: Int, toPos: Int) -> Unit)? = null
+//    var onDragMove: ((fromPos: Int, toPos: Int) -> Boolean)? = null
+//
+//    fun openOrCloseLongPressDrag(boolean: Boolean) {
+//        dragHelper?.openOrCloseLongPressDrag(boolean)
+//    }
+//
+//    override fun move(currentPosition: Int, targetPosition: Int) {
+//        val intercept = onDragMove?.invoke(currentPosition, targetPosition) ?: false
+//        if (intercept) {
+//            return
+//        }
+//        observableList.move(currentPosition, targetPosition)
+//    }
+//
+//    override fun onDragEnd(fromPos: Int, toPos: Int) {
+//        onDragEnd?.invoke(fromPos, toPos)
+//    }
+//
+    /**
+     * 只在列表请求失败的情况下才去触发"滑动翻页"
+     */
+    private val onPageScrolledListener = object : RecyclerView.OnScrollListener() {
+        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+            if (newState == RecyclerView.SCROLL_STATE_IDLE && loadingType == STATUS_ERROR) {
+                loadingType = STATUS_DEFAULT
+                val position = PaginationTool.getLastVisibleItemPosition(recyclerView)
+                if (autoLoadMore(position) && getLoadMoreViewCount() > 0) {
+                    try {
+                        notifyItemChanged(getLoadMoreViewPosition())
+                    } catch (e: Exception) {
+                        e.printStackTrace()
+                    }
+                }
+            }
+        }
+    }
+
+
+}

+ 132 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseGroupAdapter.kt

@@ -0,0 +1,132 @@
+//package com.yc.core.adapter
+//
+//import android.view.ViewGroup
+//
+///**
+// * BaseGroupAdapter
+// *
+// * @author kou_zhong
+// * @date 2020/9/16
+// */
+//abstract class BaseGroupAdapter : GroupRecyclerAdapter<BaseViewHolder<*>>(), LoadMoreAble {
+//    open var isOpenAnimation = true
+//    private var lastPosition = -1
+//
+//    private var preLoadNumber = 5
+//    private var nextLoadEnable = true
+//    private var loading = false
+//    private var loadingType = 0
+//    private var onLoadMoreListener: OnLoadMoreListener? = null
+//    private var hasLoad = false
+//    private var loadMoreFooterView: LoadMoreFooterView = DefaultLoadMoreFooterView()
+//
+//    fun resetLastPosition(position: Int) {
+//        this.lastPosition = position
+//    }
+//
+//    override fun getItemCount() = super.getItemCount() + getLoadMoreViewCount()
+//
+//    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
+//        return when (viewType) {
+//            LOAD_MORE_FOOTER -> FooterViewHolder(parent, loadMoreFooterView).also {
+//                it.setFullSpan()
+//            }
+//            else -> onCreateViewHolder(parent, viewType, LOAD_MORE_FLAG)
+//        }
+//    }
+//
+//    fun setLoadMoreFooter(loadMore: LoadMoreFooterView) {
+//        this.loadMoreFooterView = loadMore
+//    }
+//
+//    open fun onCreateViewHolder(
+//        parent: ViewGroup, viewType: Int, flag: Int = LOAD_MORE_FLAG
+//    ): BaseViewHolder<*> = EmptyPlaceViewHolder(parent)
+//
+//    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
+//        if (isOpenAnimation) {
+//            if (holder is AnimationBaseViewHolder) {
+//                val animationPosition = holder.showAnimator(lastPosition)
+//                if (animationPosition != -2) {
+//                    lastPosition = animationPosition
+//                }
+//            }
+//        }
+//        autoLoadMore(position)
+//        if (getLoadMoreViewCount() != 0 && getLoadMoreViewPosition() == position && holder is FooterViewHolder) {
+//            holder.setView(loadingType)
+//        } else {
+//            super.onBindViewHolder(holder, position)
+//        }
+//    }
+//
+//    override fun getItemViewType(position: Int) =
+//        if (getLoadMoreViewCount() != 0 && position == getLoadMoreViewPosition()) LOAD_MORE_FOOTER else super.getItemViewType(
+//            position
+//        )
+//
+//    private fun getLoadMoreViewCount() =
+//        if (onLoadMoreListener == null || super.getItemCount() == 0) 0 else 1
+//
+//    private fun getLoadMoreViewPosition() = itemCount - 1
+//
+//    private fun autoLoadMore(position: Int) {
+//        if (!hasLoad) {
+//            return
+//        }
+//        if (getLoadMoreViewCount() == 0) {
+//            return
+//        }
+//        if (position < itemCount - preLoadNumber) {
+//            return
+//        }
+//        if (loadingType != STATUS_DEFAULT) {
+//            return
+//        }
+//        loadingType = STATUS_LOADING
+//        if (!loading) {
+//            loading = true
+//            onLoadMoreListener?.onLoadMore()
+//        }
+//    }
+//
+//    fun setPreLoadNumber(preLoadNumber: Int) {
+//        if (preLoadNumber > 1) {
+//            this.preLoadNumber = preLoadNumber
+//        }
+//    }
+//
+//    override fun setOnLoadMoreListener(listener: OnLoadMoreListener) {
+//        this.onLoadMoreListener = listener
+//        nextLoadEnable = true
+//        loading = false
+//    }
+//
+//    override fun resetNoMoreData() {
+//        nextLoadEnable = true
+//        loading = false
+//        loadingType = STATUS_DEFAULT
+//    }
+//
+//    override fun finishLoadMore() {
+//        hasLoad = true
+//        if (getLoadMoreViewCount() == 0) {
+//            return
+//        }
+//        loading = false
+//        nextLoadEnable = true
+//        loadingType = STATUS_DEFAULT
+//        notifyItemChanged(getLoadMoreViewPosition())
+//    }
+//
+//    override fun finishLoadMoreWithNoMoreData() {
+//        hasLoad = true
+//        if (getLoadMoreViewCount() == 0) {
+//            return
+//        }
+//        loading = false
+//        nextLoadEnable = false
+//        loadingType = STATUS_END
+//        notifyItemChanged(getLoadMoreViewPosition())
+//    }
+//}

+ 45 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseSingleAdapter.kt

@@ -0,0 +1,45 @@
+package com.yc.baselibrary.adapter
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.ItemTouchHelper
+import com.yc.baselibrary.adapter.observable.ObservableAdapterList
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+import com.yc.baselibrary.adapter.viewHolder.EmptyPlaceViewHolder
+
+/**
+ * BaseSingleAdapter
+ *
+ * @author wm
+ * @date 20-8-12
+ */
+@Suppress("UNCHECKED_CAST")
+open class BaseSingleAdapter<T>(
+    observableList: ObservableAdapterList<T>,
+    draggable: Boolean = false,
+    dragType: Int = ItemTouchHelper.UP or ItemTouchHelper.DOWN,
+    headerVh: ((parent: ViewGroup) -> BaseViewHolder<T>)? = null,
+    private val onBind: ((position: Int, vh: BaseViewHolder<*>) -> Unit)? = null,
+    val vh: (parent: ViewGroup) -> BaseViewHolder<T>
+) : BaseStickyHeaderAdapter<BaseViewHolder<T>>(observableList, draggable, dragType, headerVh) {
+    override fun getItemViewType(position: Int, flag: Int) = ITEM
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int, flag: Int) = when (viewType) {
+        ITEM -> vh.invoke(parent)
+        else -> EmptyPlaceViewHolder(parent)
+    }
+
+    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int, flag: Int) {
+        val viewType = getItemViewType(position)
+        if (viewType == ITEM) {
+            onBind?.invoke(position, holder)
+            (holder as? BaseViewHolder<T>)?.setView((observableList[position] as? T), position)
+        }
+    }
+
+    companion object {
+        const val ITEM = 1
+    }
+
+    override fun onBindHeaderViewHolder(holder: BaseViewHolder<*>, position: Int) {
+        (holder as? BaseViewHolder<T>)?.setView((observableList[position] as? T), position)
+    }
+}

+ 49 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseSingleRecyclingPageAdapter.kt

@@ -0,0 +1,49 @@
+package com.yc.baselibrary.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+
+/**
+ * BaseSingleRecyclingPageAdapter
+ *
+ * @author wm
+ * @date 20-8-27
+ */
+@Suppress("UNCHECKED_CAST")
+class BaseSingleRecyclingPageAdapter<T>(
+    val list: MutableList<T>,
+    val res: Int,
+    var vh: (view: View) -> BaseViewHolder<T>
+) : RecyclingPagerAdapter() {
+    override fun getView(position: Int, convertView: View?, container: ViewGroup): View? {
+        var holder: BaseViewHolder<T>
+        var view = convertView
+        if (view == null) {
+            view = LayoutInflater.from(container.context).inflate(res, container, false)
+            holder = vh.invoke(view!!)
+            view.tag = holder
+        }
+        holder = view.tag as BaseViewHolder<T>
+        holder.setView(list[position])
+        return view
+    }
+
+    override fun getCount() = list.size
+
+    fun setNewData(list: MutableList<T>?) {
+        this.list.clear()
+        if (!list.isNullOrEmpty()) {
+            this.list.addAll(list)
+        }
+        notifyDataSetChanged()
+    }
+
+    fun addData(list: MutableList<T>?) {
+        if (!list.isNullOrEmpty()) {
+            this.list.addAll(list)
+        }
+        notifyDataSetChanged()
+    }
+}

+ 30 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/BaseStickyHeaderAdapter.kt

@@ -0,0 +1,30 @@
+package com.yc.baselibrary.adapter
+
+import android.view.ViewGroup
+import androidx.recyclerview.widget.ItemTouchHelper
+import com.yc.baselibrary.adapter.observable.ObservableAdapterList
+import com.yc.baselibrary.adapter.viewHolder.BaseViewHolder
+import com.yc.baselibrary.adapter.viewHolder.EmptyPlaceViewHolder
+import com.yc.baselibrary.adapter.viewHolder.model.StickyHeaderInterface
+import com.yc.stickylistviewlibrary.StickyRecyclerHeadersAdapter
+
+/**
+ * BaseStickyHeaderAdapter
+ *
+ * @author kou_zhong
+ * @date 2021/1/8
+ */
+abstract class BaseStickyHeaderAdapter<VH : BaseViewHolder<*>>(
+    observableList: ObservableAdapterList<*>,
+    draggable: Boolean = false,
+    dragType: Int = ItemTouchHelper.UP or ItemTouchHelper.DOWN,
+    private val headerVh: ((parent: ViewGroup) -> BaseViewHolder<*>)? = null
+) : BaseAdapter<BaseViewHolder<*>>(observableList, draggable, dragType),
+    StickyRecyclerHeadersAdapter<BaseViewHolder<*>> {
+
+    override fun getHeaderId(position: Int) =
+        (observableList.getOrNull(position) as? StickyHeaderInterface)?.getHeaderId() ?: -1
+
+    override fun onCreateHeaderViewHolder(parent: ViewGroup) =
+        headerVh?.invoke(parent) ?: EmptyPlaceViewHolder(parent)
+}

+ 137 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/RecyclingPagerAdapter.java

@@ -0,0 +1,137 @@
+package com.yc.baselibrary.adapter;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+
+
+import com.yc.baselibrary.models.RecycleBin;
+
+import androidx.annotation.NonNull;
+import androidx.viewpager.widget.PagerAdapter;
+
+
+/**
+ * A {@link PagerAdapter} which behaves like an {@link android.widget
+ * .Adapter}
+ * with view types and view recycling.
+ */
+public abstract class RecyclingPagerAdapter extends PagerAdapter {
+    static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
+
+    private final RecycleBin recycleBin;
+    private int mChildCount;
+
+    public RecyclingPagerAdapter() {
+        this(new RecycleBin());
+    }
+
+    RecyclingPagerAdapter(RecycleBin recycleBin) {
+        this.recycleBin = recycleBin;
+        recycleBin.setViewTypeCount(getViewTypeCount());
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        recycleBin.scrapActiveViews();
+        mChildCount = getCount();
+        super.notifyDataSetChanged();
+    }
+
+    @NonNull
+    @Override
+    public final Object instantiateItem(@NonNull ViewGroup container, int position) {
+        int viewType = getItemViewType(position);
+        View view = null;
+        if (viewType != IGNORE_ITEM_VIEW_TYPE) {
+            view = recycleBin.getScrapView(position, viewType);
+        }
+        view = getView(position, view, container);
+        container.addView(view);
+        return view;
+    }
+
+    @Override
+    public final void destroyItem(ViewGroup container, int position, @NonNull Object object) {
+        View view = (View) object;
+        container.removeView(view);
+        int viewType = position < getCount() - 1 ? getItemViewType(position) : IGNORE_ITEM_VIEW_TYPE;
+        if (viewType != IGNORE_ITEM_VIEW_TYPE) {
+            recycleBin.addScrapView(view, position, viewType);
+        }
+    }
+
+    @Override
+    public final boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+        return view == object;
+    }
+
+    @Override
+    public int getItemPosition(@NonNull Object object) {
+        if (mChildCount > 0) {
+            mChildCount--;
+            return POSITION_NONE;
+        }
+        return super.getItemPosition(object);
+    }
+
+    /**
+     * <p>
+     * Returns the number of types of Views that will be created by
+     * {@link #getView}. Each type represents a set of views that can be
+     * converted in {@link #getView}. If the adapter always returns the same
+     * type of View for all items, this method should return 1.
+     * </p>
+     * <p>
+     * This method will only be called when when the adapter is set on the the
+     * {@link AdapterView}.
+     * </p>
+     *
+     * @return The number of types of Views that will be created by this adapter
+     */
+    public int getViewTypeCount() {
+        return 1;
+    }
+
+    /**
+     * Get the type of View that will be created by {@link #getView} for the
+     * specified item.
+     *
+     * @param position The position of the item within the adapter's data set whose
+     *                 view type we want.
+     * @return An integer representing the type of View. Two views should share
+     * the same type if one can be converted to the other in
+     * {@link #getView}. Note: Integers must be in the range 0 to
+     * {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
+     * also be returned.
+     * @see #IGNORE_ITEM_VIEW_TYPE
+     */
+    @SuppressWarnings("UnusedParameters")
+    // Argument potentially used by subclasses.
+    public int getItemViewType(int position) {
+        return 0;
+    }
+
+    /**
+     * Get a View that displays the data at the specified position in the data
+     * set. You can either create a View manually or inflate it from an XML
+     * layout file. When the View is inflated, the parent View (GridView,
+     * ListView...) will apply default layout parameters unless you use
+     * {@link android.view.LayoutInflater#inflate(int, ViewGroup, boolean)}
+     * to specify a root view and to prevent attachment to the root.
+     *
+     * @param position    The position of the item within the adapter's data set of the
+     *                    item whose view we want.
+     * @param convertView The old view to reuse, if possible. Note: You should check
+     *                    that this view is non-null and of an appropriate type before
+     *                    using. If it is not possible to convert this view to display
+     *                    the correct data, this method can create a new view.
+     *                    Heterogeneous lists can specify their number of view types, so
+     *                    that this View is always of the right type (see
+     *                    {@link #getViewTypeCount()} and {@link #getItemViewType(int)}
+     *                    ).
+     * @param container   The parent that this view will eventually be attached to
+     * @return A View corresponding to the data at the specified position.
+     */
+    public abstract View getView(int position, View convertView, ViewGroup container);
+}

+ 33 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/anim/AlphaInItemAnimator.kt

@@ -0,0 +1,33 @@
+package com.yc.baselibrary.adapter.anim
+
+import android.animation.ObjectAnimator
+import android.animation.TimeInterpolator
+import android.view.View
+import android.view.animation.LinearInterpolator
+import com.yc.baselibrary.adapter.viewHolder.ItemAnimator
+
+/**
+ * AlphaInItemAnimator
+ *
+ * @author wm
+ * @date 20-7-29
+ */
+class AlphaInItemAnimator(private val from :Float=0.5f,private val duration: Long = 500L, private val interpolator: TimeInterpolator = LinearInterpolator()) :
+    ItemAnimator {
+    override fun scrollUpAnim(v: View) {
+        ObjectAnimator.ofFloat(v, "alpha", from, 1f)
+            .setDuration(duration).apply {
+                interpolator = this@AlphaInItemAnimator.interpolator
+            }
+            .start()
+    }
+
+    override fun scrollDownAnim(v: View) {
+        ObjectAnimator.ofFloat(v, "alpha", from, 1f)
+            .setDuration(duration).apply {
+                interpolator = this@AlphaInItemAnimator.interpolator
+            }
+            .start()
+    }
+
+}

+ 40 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/anim/ScaleInItemAnimator.kt

@@ -0,0 +1,40 @@
+package com.yc.baselibrary.adapter.anim
+
+import android.animation.Animator
+import android.animation.ObjectAnimator
+import android.animation.TimeInterpolator
+import android.view.View
+import android.view.animation.LinearInterpolator
+import com.yc.baselibrary.adapter.viewHolder.ItemAnimator
+
+
+/**
+ * ScaleInItemAnimator
+ *
+ * @author wm
+ * @date 20-7-29
+ */
+class ScaleInItemAnimator(private val from: Float = .5f, private val duration: Long = 500L, private val interpolator: TimeInterpolator = LinearInterpolator()) :
+    ItemAnimator {
+    override fun scrollUpAnim(v: View) {
+        getAnimators(v).forEach {
+            it.setDuration(duration).apply {
+                interpolator = this@ScaleInItemAnimator.interpolator
+            }.start()
+        }
+    }
+
+    override fun scrollDownAnim(v: View) {
+        getAnimators(v).forEach {
+            it.setDuration(duration).apply {
+                interpolator = this@ScaleInItemAnimator.interpolator
+            }.start()
+        }
+    }
+
+    private fun getAnimators(view: View): Array<Animator> {
+        val scaleX = ObjectAnimator.ofFloat(view, "scaleX", from, 1f)
+        val scaleY = ObjectAnimator.ofFloat(view, "scaleY", from, 1f)
+        return arrayOf(scaleX, scaleY)
+    }
+}

+ 13 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/const/AdapterConst.kt

@@ -0,0 +1,13 @@
+package com.yc.baselibrary.adapter.const
+
+/**
+ * AdapterConst
+ *
+ * @author kou_zhong
+ * @date 2020/9/25
+ */
+object AdapterConst {
+    const val LOAD_MORE_FLAG = 1001
+    const val LOAD_MORE_FOOTER = 542
+}
+

+ 18 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/listener/LoadMoreAble.java

@@ -0,0 +1,18 @@
+package com.yc.baselibrary.adapter.listener;
+
+
+/**
+ * LoadMoreAble
+ *
+ * @author kou_zhong
+ * @date 2020/9/23
+ */
+public interface LoadMoreAble {
+    void finishLoadMore();
+
+    void finishLoadMoreWithNoMoreData();
+
+    void finishLoadError();
+
+    void setOnLoadMoreListener(OnLoadMoreListener listener);
+}

+ 11 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/listener/OnLoadMoreListener.java

@@ -0,0 +1,11 @@
+package com.yc.baselibrary.adapter.listener;
+
+/**
+ * OnLoadMoreListener
+ *
+ * @author kou_zhong
+ * @date 2020/9/23
+ */
+public interface OnLoadMoreListener {
+    void onLoadMore();
+}

+ 386 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/CallbackRegistry.java

@@ -0,0 +1,386 @@
+package com.yc.baselibrary.adapter.observable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CallbackRegistry
+ *
+ * @author wm
+ * @date 20-7-30
+ */
+public class CallbackRegistry<C, T, A> implements Cloneable {
+    private static final String TAG = "CallbackRegistry";
+
+    /** An ordered collection of listeners waiting to be notified. */
+    private List<C> mCallbacks = new ArrayList<C>();
+
+    /**
+     * A bit flag for the first 64 listeners that are removed during notification.
+     * The lowest significant bit corresponds to the 0th index into mCallbacks.
+     * For a small number of callbacks, no additional array of objects needs to
+     * be allocated.
+     */
+    private long mFirst64Removed = 0x0;
+
+    /**
+     * Bit flags for the remaining callbacks that are removed during notification.
+     * When there are more than 64 callbacks and one is marked for removal, a dynamic
+     * array of bits are allocated for the callbacks.
+     */
+    private long[] mRemainderRemoved;
+
+    /** The recursion level of the notification */
+    private int mNotificationLevel;
+
+    /** The notification mechanism for notifying an event. */
+    private final NotifierCallback<C, T, A> mNotifier;
+
+    /**
+     * Creates an EventRegistry that notifies the event with notifier.
+     * @param notifier The class to use to notify events.
+     */
+    public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
+        mNotifier = notifier;
+    }
+
+    /**
+     * Notify all callbacks.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg2 An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     */
+    public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
+        mNotificationLevel++;
+        notifyRecurse(sender, arg, arg2);
+        mNotificationLevel--;
+        if (mNotificationLevel == 0) {
+            if (mRemainderRemoved != null) {
+                for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
+                    final long removedBits = mRemainderRemoved[i];
+                    if (removedBits != 0) {
+                        removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
+                        mRemainderRemoved[i] = 0;
+                    }
+                }
+            }
+            if (mFirst64Removed != 0) {
+                removeRemovedCallbacks(0, mFirst64Removed);
+                mFirst64Removed = 0;
+            }
+        }
+    }
+
+    /**
+     * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg2 An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     */
+    private void notifyFirst64(T sender, int arg, A arg2) {
+        final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
+        notifyCallbacks(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
+    }
+
+    /**
+     * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
+     * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
+     * removal before it recurses into {@link #notifyRemainder(Object, int, A, int)}.
+     *
+     * <p>Recursion is used to avoid allocating temporary state on the heap.</p>
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg2 An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     */
+    private void notifyRecurse(T sender, int arg, A arg2) {
+        final int callbackCount = mCallbacks.size();
+        final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
+
+        // Now we've got all callbakcs that have no mRemainderRemoved value, so notify the
+        // others.
+        notifyRemainder(sender, arg, arg2, remainderIndex);
+
+        // notifyRemainder notifies all at maxIndex, so we'd normally start at maxIndex + 1
+        // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
+        final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
+
+        // The remaining have no bit set
+        notifyCallbacks(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
+    }
+
+    /**
+     * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
+     * remainderIndex is -1, the first 64 will be notified instead.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg2 An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param remainderIndex The index into mRemainderRemoved that should be notified.
+     */
+    private void notifyRemainder(T sender, int arg, A arg2, int remainderIndex) {
+        if (remainderIndex < 0) {
+            notifyFirst64(sender, arg, arg2);
+        } else {
+            final long bits = mRemainderRemoved[remainderIndex];
+            final int startIndex = (remainderIndex + 1) * Long.SIZE;
+            final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
+            notifyRemainder(sender, arg, arg2, remainderIndex - 1);
+            notifyCallbacks(sender, arg, arg2, startIndex, endIndex, bits);
+        }
+    }
+
+    /**
+     * Notify callbacks from startIndex to endIndex, using bits as the bit status
+     * for whether they have been removed or not. bits should be from mRemainderRemoved or
+     * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
+     * endIndex should be notified.
+     *
+     * @param sender The originator. This is an opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param arg2 An opaque parameter passed to
+     * {@link NotifierCallback#onNotifyCallback(Object, Object, int, Object)}
+     * @param startIndex The index into the mCallbacks to start notifying.
+     * @param endIndex One past the last index into mCallbacks to notify.
+     * @param bits A bit field indicating which callbacks have been removed and shouldn't
+     *             be notified.
+     */
+    private void notifyCallbacks(T sender, int arg, A arg2, final int startIndex,
+                                 final int endIndex, final long bits) {
+        long bitMask = 1;
+        for (int i = startIndex; i < endIndex; i++) {
+            if ((bits & bitMask) == 0) {
+                mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
+            }
+            bitMask <<= 1;
+        }
+    }
+
+    /**
+     * Add a callback to be notified. If the callback is already in the list, another won't
+     * be added. This does not affect current notifications.
+     * @param callback The callback to add.
+     */
+    public synchronized void add(C callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null");
+        }
+        int index = mCallbacks.lastIndexOf(callback);
+        if (index < 0 || isRemoved(index)) {
+            mCallbacks.add(callback);
+        }
+    }
+
+    /**
+     * Returns true if the callback at index has been marked for removal.
+     *
+     * @param index The index into mCallbacks to check.
+     * @return true if the callback at index has been marked for removal.
+     */
+    private boolean isRemoved(int index) {
+        if (index < Long.SIZE) {
+            // It is in the first 64 callbacks, just check the bit.
+            final long bitMask = 1L << index;
+            return (mFirst64Removed & bitMask) != 0;
+        } else if (mRemainderRemoved == null) {
+            // It is after the first 64 callbacks, but nothing else was marked for removal.
+            return false;
+        } else {
+            final int maskIndex = (index / Long.SIZE) - 1;
+            if (maskIndex >= mRemainderRemoved.length) {
+                // There are some items in mRemainderRemoved, but nothing at the given index.
+                return false;
+            } else {
+                // There is something marked for removal, so we have to check the bit.
+                final long bits = mRemainderRemoved[maskIndex];
+                final long bitMask = 1L << (index % Long.SIZE);
+                return (bits & bitMask) != 0;
+            }
+        }
+    }
+
+    /**
+     * Removes callbacks from startIndex to startIndex + Long.SIZE, based
+     * on the bits set in removed.
+     *
+     * @param startIndex The index into the mCallbacks to start removing callbacks.
+     * @param removed The bits indicating removal, where each bit is set for one callback
+     *                to be removed.
+     */
+    private void removeRemovedCallbacks(int startIndex, long removed) {
+        // The naive approach should be fine. There may be a better bit-twiddling approach.
+        final int endIndex = startIndex + Long.SIZE;
+
+        long bitMask = 1L << (Long.SIZE - 1);
+        for (int i = endIndex - 1; i >= startIndex; i--) {
+            if ((removed & bitMask) != 0) {
+                mCallbacks.remove(i);
+            }
+            bitMask >>>= 1;
+        }
+    }
+
+    /**
+     * Remove a callback. This callback won't be notified after this call completes.
+     *
+     * @param callback The callback to remove.
+     */
+    public synchronized void remove(C callback) {
+        if (mNotificationLevel == 0) {
+            mCallbacks.remove(callback);
+        } else {
+            int index = mCallbacks.lastIndexOf(callback);
+            if (index >= 0) {
+                setRemovalBit(index);
+            }
+        }
+    }
+
+    private void setRemovalBit(int index) {
+        if (index < Long.SIZE) {
+            // It is in the first 64 callbacks, just check the bit.
+            final long bitMask = 1L << index;
+            mFirst64Removed |= bitMask;
+        } else {
+            final int remainderIndex = (index / Long.SIZE) - 1;
+            if (mRemainderRemoved == null) {
+                mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
+            } else if (mRemainderRemoved.length <= remainderIndex) {
+                // need to make it bigger
+                long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
+                System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
+                mRemainderRemoved = newRemainders;
+            }
+            final long bitMask = 1L << (index % Long.SIZE);
+            mRemainderRemoved[remainderIndex] |= bitMask;
+        }
+    }
+
+    /**
+     * Makes a copy of the registered callbacks and returns it.
+     *
+     * @return a copy of the registered callbacks.
+     */
+    public synchronized ArrayList<C> copyCallbacks() {
+        ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size());
+        int numListeners = mCallbacks.size();
+        for (int i = 0; i < numListeners; i++) {
+            if (!isRemoved(i)) {
+                callbacks.add(mCallbacks.get(i));
+            }
+        }
+        return callbacks;
+    }
+
+    /**
+     * Modifies <code>callbacks</code> to contain all callbacks in the CallbackRegistry.
+     *
+     * @param callbacks modified to contain all callbacks registered to receive events.
+     */
+    public synchronized void copyCallbacks(List<C> callbacks) {
+        callbacks.clear();
+        int numListeners = mCallbacks.size();
+        for (int i = 0; i < numListeners; i++) {
+            if (!isRemoved(i)) {
+                callbacks.add(mCallbacks.get(i));
+            }
+        }
+    }
+
+    /**
+     * Returns true if there are no registered callbacks or false otherwise.
+     *
+     * @return true if there are no registered callbacks or false otherwise.
+     */
+    public synchronized boolean isEmpty() {
+        if (mCallbacks.isEmpty()) {
+            return true;
+        } else if (mNotificationLevel == 0) {
+            return false;
+        } else {
+            int numListeners = mCallbacks.size();
+            for (int i = 0; i < numListeners; i++) {
+                if (!isRemoved(i)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Removes all callbacks from the list.
+     */
+    public synchronized void clear() {
+        if (mNotificationLevel == 0) {
+            mCallbacks.clear();
+        } else if (!mCallbacks.isEmpty()) {
+            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                setRemovalBit(i);
+            }
+        }
+    }
+
+    /**
+     * @return A copy of the CallbackRegistry with all callbacks listening to both instances.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public synchronized CallbackRegistry<C, T, A> clone() {
+        CallbackRegistry<C, T, A> clone = null;
+        try {
+            clone = (CallbackRegistry<C, T, A>) super.clone();
+            clone.mFirst64Removed = 0;
+            clone.mRemainderRemoved = null;
+            clone.mNotificationLevel = 0;
+            clone.mCallbacks = new ArrayList<C>();
+            final int numListeners = mCallbacks.size();
+            for (int i = 0; i < numListeners; i++) {
+                if (!isRemoved(i)) {
+                    clone.mCallbacks.add(mCallbacks.get(i));
+                }
+            }
+        } catch (CloneNotSupportedException e) {
+            e.printStackTrace();
+        }
+        return clone;
+    }
+
+    /**
+     * Class used to notify events from CallbackRegistry.
+     *
+     * @param <C> The callback type.
+     * @param <T> The notification sender type. Typically this is the containing class.
+     * @param <A> An opaque argument to pass to the notifier
+     */
+    public abstract static class NotifierCallback<C, T, A> {
+        /**
+         * Called by CallbackRegistry during
+         * {@link CallbackRegistry#notifyCallbacks(Object, int, Object)}} to notify the callback.
+         *
+         * @param callback The callback to notify.
+         * @param sender The opaque sender object.
+         * @param arg The opaque notification parameter.
+         * @param arg2 An opaque argument passed in
+         *        {@link CallbackRegistry#notifyCallbacks}
+         * @see CallbackRegistry#CallbackRegistry( NotifierCallback)
+         */
+        public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
+    }
+}

+ 139 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/ListChangeRegistry.java

@@ -0,0 +1,139 @@
+package com.yc.baselibrary.adapter.observable;
+
+import androidx.annotation.NonNull;
+import androidx.core.util.Pools;
+
+/**
+ * ListChangeRegistry
+ *
+ * @author wm
+ * @date 20-7-30
+ */
+public class ListChangeRegistry
+        extends
+        CallbackRegistry<ObservableList.OnListChangedCallback, ObservableList,
+                        ListChangeRegistry.ListChanges> {
+    private static final Pools.SynchronizedPool<ListChanges> sListChanges =
+            new Pools.SynchronizedPool<ListChanges>(10);
+
+    private static final int ALL = 0;
+    private static final int CHANGED = 1;
+    private static final int INSERTED = 2;
+    private static final int MOVED = 3;
+    private static final int REMOVED = 4;
+
+    private static final CallbackRegistry.NotifierCallback<ObservableList.OnListChangedCallback,
+            ObservableList, ListChanges> NOTIFIER_CALLBACK = new CallbackRegistry.NotifierCallback<
+            ObservableList.OnListChangedCallback, ObservableList, ListChanges>() {
+        @Override
+        public void onNotifyCallback(ObservableList.OnListChangedCallback callback,
+                                     ObservableList sender, int notificationType, ListChanges listChanges) {
+            switch (notificationType) {
+                case CHANGED:
+                    callback.onItemRangeChanged(sender, listChanges.start, listChanges.count);
+                    break;
+                case INSERTED:
+                    callback.onItemRangeInserted(sender, listChanges.start, listChanges.count);
+                    break;
+                case MOVED:
+                    callback.onItemRangeMoved(sender, listChanges.start, listChanges.to,
+                            listChanges.count);
+                    break;
+                case REMOVED:
+                    callback.onItemRangeRemoved(sender, listChanges.start, listChanges.count);
+                    break;
+                default:
+                    callback.onChanged(sender);
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Notify registered callbacks that there was an unknown or whole-list change.
+     *
+     * @param list The list that changed.
+     */
+    public void notifyChanged(@NonNull ObservableList list) {
+        notifyCallbacks(list, ALL, null);
+    }
+
+    /**
+     * Notify registered callbacks that some elements have changed.
+     *
+     * @param list The list that changed.
+     * @param start The index of the first changed element.
+     * @param count The number of changed elements.
+     */
+    public void notifyChanged(@NonNull ObservableList list, int start, int count) {
+        ListChanges listChanges = acquire(start, 0, count);
+        notifyCallbacks(list, CHANGED, listChanges);
+    }
+
+    /**
+     * Notify registered callbacks that elements were inserted.
+     *
+     * @param list The list that changed.
+     * @param start The index where the elements were inserted.
+     * @param count The number of elements that were inserted.
+     */
+    public void notifyInserted(@NonNull ObservableList list, int start, int count) {
+        ListChanges listChanges = acquire(start, 0, count);
+        notifyCallbacks(list, INSERTED, listChanges);
+    }
+
+    /**
+     * Notify registered callbacks that elements were moved.
+     *
+     * @param list The list that changed.
+     * @param from The index of the first element moved.
+     * @param to The index of where the element was moved to.
+     * @param count The number of elements moved.
+     */
+    public void notifyMoved(@NonNull ObservableList list, int from, int to, int count) {
+        ListChanges listChanges = acquire(from, to, count);
+        notifyCallbacks(list, MOVED, listChanges);
+    }
+
+    /**
+     * Notify registered callbacks that elements were deleted.
+     *
+     * @param list The list that changed.
+     * @param start The index of the first element to be removed.
+     * @param count The number of elements removed.
+     */
+    public void notifyRemoved(@NonNull ObservableList list, int start, int count) {
+        ListChanges listChanges = acquire(start, 0, count);
+        notifyCallbacks(list, REMOVED, listChanges);
+    }
+
+    private static ListChanges acquire(int start, int to, int count) {
+        ListChanges listChanges = sListChanges.acquire();
+        if (listChanges == null) {
+            listChanges = new ListChanges();
+        }
+        listChanges.start = start;
+        listChanges.to = to;
+        listChanges.count = count;
+        return listChanges;
+    }
+
+    @Override
+    public synchronized void notifyCallbacks(@NonNull ObservableList sender, int notificationType,
+                                             ListChanges listChanges) {
+        super.notifyCallbacks(sender, notificationType, listChanges);
+        if (listChanges != null) {
+            sListChanges.release(listChanges);
+        }
+    }
+
+    public ListChangeRegistry() {
+        super(NOTIFIER_CALLBACK);
+    }
+
+    static class ListChanges {
+        public int start;
+        public int count;
+        public int to;
+    }
+}

+ 173 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/ObservableAdapterList.java

@@ -0,0 +1,173 @@
+package com.yc.baselibrary.adapter.observable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * ObservableAdapterList
+ *
+ * @author wm
+ * @date 20-7-30
+ */
+public class ObservableAdapterList<T> extends ArrayList<T> implements ObservableList<T> {
+    private transient ListChangeRegistry mListeners = new ListChangeRegistry();
+
+    @Override
+    public void addOnListChangedCallback(OnListChangedCallback listener) {
+        if (mListeners == null) {
+            mListeners = new ListChangeRegistry();
+        }
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeOnListChangedCallback(OnListChangedCallback listener) {
+        if (mListeners != null) {
+            mListeners.remove(listener);
+        }
+    }
+
+    public boolean setNewData(Collection<? extends T> collection) {
+        super.clear();
+        boolean added = super.addAll(collection);
+        if (added) {
+            notifySetNewData();
+        }
+        return added;
+    }
+
+    @Override
+    public boolean add(T object) {
+        super.add(object);
+        notifyAdd(size() - 1, 1);
+        return true;
+    }
+
+    @Override
+    public void add(int index, T object) {
+        super.add(index, object);
+        notifyAdd(index, 1);
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends T> collection) {
+        int oldSize = size();
+        boolean added = super.addAll(collection);
+        if (added) {
+            notifyAdd(oldSize, size() - oldSize);
+        }
+        return added;
+    }
+
+    @Override
+    public boolean addAll(int index, Collection<? extends T> collection) {
+        boolean added = super.addAll(index, collection);
+        if (added) {
+            notifyAdd(index, collection.size());
+        }
+        return added;
+    }
+
+    public void move(int fromPosition, int toPosition) {
+        T from = get(fromPosition);
+        int movedCount = Math.abs(fromPosition - toPosition) + 1;
+        if (movedCount == 2) {
+            super.set(fromPosition, get(toPosition));
+            super.set(toPosition, from);
+        } else {
+            super.remove(fromPosition);
+            super.add(toPosition, from);
+        }
+
+        if (mListeners != null) {
+            mListeners.notifyMoved(this, fromPosition, toPosition, movedCount);
+        }
+    }
+
+    @Override
+    public void clear() {
+        int oldSize = size();
+        super.clear();
+        if (oldSize != 0) {
+            notifyRemove(0, oldSize);
+        }
+    }
+
+    @Override
+    public T remove(int index) {
+        T val = super.remove(index);
+        notifyRemove(index, 1);
+        return val;
+    }
+
+    @Override
+    public boolean remove(Object object) {
+        int index = indexOf(object);
+        if (index >= 0) {
+            remove(index);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public T set(int index, T object) {
+        T val = super.set(index, object);
+        notifyRangeChanged(index, 1);
+        return val;
+    }
+
+    public void setRange(List<? extends T> list, int start) {
+        if (list == null || start < 0) {
+            return;
+        }
+        for (int i = 0; i < list.size(); i++) {
+            super.set(i + start, list.get(i));
+        }
+        notifyRangeChanged(start, list.size());
+    }
+
+    @Override
+    public void removeRange(int fromIndex, int toIndex) {
+        super.removeRange(fromIndex, toIndex);
+        notifyRemove(fromIndex, toIndex - fromIndex);
+    }
+
+    private void notifyAdd(int start, int count) {
+        if (mListeners != null) {
+            mListeners.notifyInserted(this, start, count);
+        }
+    }
+
+    private void notifyRemove(int start, int count) {
+        if (mListeners != null) {
+            mListeners.notifyRemoved(this, start, count);
+        }
+    }
+
+    private void notifySetNewData() {
+        if (mListeners != null) {
+            mListeners.notifyChanged(this);
+        }
+    }
+
+    public void notifyRangeChanged(int start, int count) {
+        if (mListeners != null) {
+            mListeners.notifyChanged(this, start, count);
+        }
+    }
+
+    public void addData(Collection<? extends T> data) {
+        addData(data, false);
+    }
+
+    public void addData(Collection<? extends T> data, Boolean overrideData) {
+        if (overrideData || this.isEmpty()) {
+            setNewData(data);
+        } else {
+            addAll(data);
+        }
+    }
+}

+ 72 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/observable/ObservableList.java

@@ -0,0 +1,72 @@
+package com.yc.baselibrary.adapter.observable;
+
+import java.util.List;
+
+/**
+ * ObservableList
+ *
+ * @author wm
+ * @date 20-7-30
+ */
+public interface ObservableList<T> extends List<T> {
+
+    /**
+     * Adds a callback to be notified when changes to the list occur.
+     * @param callback The callback to be notified on list changes
+     */
+    void addOnListChangedCallback(OnListChangedCallback<? extends ObservableList<T>> callback);
+
+    /**
+     * Removes a callback previously added.
+     * @param callback The callback to remove.
+     */
+    void removeOnListChangedCallback(OnListChangedCallback<? extends ObservableList<T>> callback);
+
+    /**
+     * The callback that is called by ObservableList when the list has changed.
+     */
+    abstract class OnListChangedCallback<T extends ObservableList> {
+
+        /**
+         * Called whenever a change of unknown type has occurred, such as the entire list being
+         * set to new values.
+         *
+         * @param sender The changing list.
+         */
+        public abstract void onChanged(T sender);
+
+        /**
+         * Called whenever one or more items in the list have changed.
+         * @param sender The changing list.
+         * @param positionStart The starting index that has changed.
+         * @param itemCount The number of items that have changed.
+         */
+        public abstract void onItemRangeChanged(T sender, int positionStart, int itemCount);
+
+        /**
+         * Called whenever items have been inserted into the list.
+         * @param sender The changing list.
+         * @param positionStart The insertion index
+         * @param itemCount The number of items that have been inserted
+         */
+        public abstract void onItemRangeInserted(T sender, int positionStart, int itemCount);
+
+        /**
+         * Called whenever items in the list have been moved.
+         * @param sender The changing list.
+         * @param fromPosition The position from which the items were moved
+         * @param toPosition The destination position of the items
+         * @param itemCount The number of items moved
+         */
+        public abstract void onItemRangeMoved(T sender, int fromPosition, int toPosition,
+                                              int itemCount);
+
+        /**
+         * Called whenever items in the list have been deleted.
+         * @param sender The changing list.
+         * @param positionStart The starting index of the deleted items.
+         * @param itemCount The number of items removed.
+         */
+        public abstract void onItemRangeRemoved(T sender, int positionStart, int itemCount);
+    }
+}

+ 22 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/AlphaBaseViewHolder.kt

@@ -0,0 +1,22 @@
+package com.yc.baselibrary.adapter.viewHolder
+
+import android.animation.TimeInterpolator
+import android.view.View
+import android.view.animation.LinearInterpolator
+import com.yc.baselibrary.adapter.anim.AlphaInItemAnimator
+
+/**
+ * AlphaBaseViewHolder
+ *
+ * @author wm
+ * @date 20-7-29
+ */
+abstract class AlphaBaseViewHolder<T>(
+    itemView: View,
+    from: Float = 0.5f, duration: Long = 500L, interpolator: TimeInterpolator = LinearInterpolator()
+) : AnimationBaseViewHolder<T>(itemView) {
+
+    override var itemAnimator: ItemAnimator? = AlphaInItemAnimator(from, duration, interpolator)
+    override var showItemAnimator: Boolean = false
+    override var isFirstOnly = true
+}

+ 58 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/AnimationBaseViewHolder.kt

@@ -0,0 +1,58 @@
+package com.yc.baselibrary.adapter.viewHolder
+
+import android.view.View
+import com.yc.baselibrary.adapter.anim.ScaleInItemAnimator
+
+/**
+ * AnimationBaseViewHolder
+ *
+ * @author wm
+ * @date 20-7-29
+ */
+abstract class AnimationBaseViewHolder<T>(itemView: View) : BaseMutableViewHolder<T>(itemView) {
+
+    open var itemAnimator: ItemAnimator? = ScaleInItemAnimator()
+    open var isFirstOnly = true
+    open var showItemAnimator = true
+
+    fun showAnimator(lastPosition: Int): Int {
+        var currentPosition = -2
+        itemAnimator?.let {
+            if (!showItemAnimator) {
+                return currentPosition
+            }
+            currentPosition = if (!isFirstOnly || adapterPosition > lastPosition) {
+                if (adapterPosition >= lastPosition) {
+                    it.scrollDownAnim(itemView)
+                } else {
+                    it.scrollUpAnim(itemView)
+                }
+                adapterPosition
+            } else {
+                clear(itemView)
+                -2
+            }
+        }
+        return currentPosition
+    }
+
+    fun clear(v: View) {
+        v.alpha = 1f
+        v.scaleY = 1f
+        v.scaleX = 1f
+        v.translationY = 0f
+        v.translationX = 0f
+        v.rotation = 0f
+        v.rotationX = 0f
+        v.rotationY = 0f
+        v.pivotX = v.measuredWidth.toFloat() / 2
+        v.pivotY = v.measuredHeight.toFloat() / 2
+        v.animate().setInterpolator(null).startDelay = 0
+    }
+}
+
+interface ItemAnimator {
+    fun scrollUpAnim(v: View)
+
+    fun scrollDownAnim(v: View)
+}

+ 53 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/BaseMutableViewHolder.kt

@@ -0,0 +1,53 @@
+package com.yc.baselibrary.adapter.viewHolder
+import android.content.Context
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import com.yc.baselibrary.adapter.observable.ObservableAdapterList
+
+/**
+ * BaseMutableAdapter
+ *
+ * @author kou_zhong
+ * @date 2020/10/14
+ */
+open class BaseMutableViewHolder<T>(itemView: View) : ObservableBaseViewHolder<T>(itemView) {
+
+    fun setView(
+        context: Context = mContext,
+        item: T?,
+        position: Int,
+        viewType: Int = itemViewType,
+        recyclerView: RecyclerView?,
+        adapter: MutableAdapter,
+        map: HashMap<String, @JvmSuppressWildcards Any> = hashMapOf(),
+        observableList: ObservableAdapterList<Any>
+    ) {
+        itemPosition = position
+        this.item = item
+        try {
+            setViewData(
+                context, item, position, viewType, recyclerView, adapter, map, observableList
+            )
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    override fun setViewData(
+        context: Context, item: T?, position: Int, viewType: Int
+    ) {
+    }
+
+    open fun setViewData(
+        context: Context,
+        item: T?,
+        position: Int,
+        viewType: Int,
+        recyclerView: RecyclerView?,
+        mutableAdapter: MutableAdapter,
+        map: HashMap<String, @JvmSuppressWildcards Any>,
+        observableList: ObservableAdapterList<Any>
+    ) {
+
+    }
+}

+ 61 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/BaseViewHolder.java

@@ -0,0 +1,61 @@
+package com.yc.baselibrary.adapter.viewHolder;
+
+import android.content.Context;
+import android.view.View;
+
+import java.util.Map;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * Created by werther on 16/7/27.
+ */
+public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder {
+
+    protected Context mContext;
+    protected T item;
+    protected int itemPosition;
+    @Nullable
+    public Map<String, Object> extras;
+
+    public BaseViewHolder(View itemView) {
+        super(itemView);
+        mContext = itemView.getContext();
+    }
+
+    public void setView(Context mContext, T item, int position, int viewType) {
+        this.itemPosition = position;
+        this.item = item;
+        try {
+            setViewData(mContext, item, position, viewType);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void setView(T item) {
+        setView(item, getAdapterPosition());
+    }
+
+    public void setView(T item, int position) {
+        setView(itemView.getContext(), item, position, getItemViewType());
+    }
+
+
+    public T getItem() {
+        return item;
+    }
+
+    public int getItemPosition() {
+        return itemPosition;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    protected abstract void setViewData(Context mContext, T item, int position, int viewType);
+
+}
+

+ 26 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/BgVH.kt

@@ -0,0 +1,26 @@
+//package com.yc.core.adapter.viewholder.cell
+//
+//import android.view.View
+//import android.view.ViewGroup
+//import com.hunliji.hljcorewraplibrary.R
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.viewholder.AlphaBaseViewHolder
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.viewholder.cell.com.yc.module_base.model.DefaultRVBg
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.viewholder.setFullSpan
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.createItemView
+//import com.yc.core.R
+//import com.yc.core.adapter.viewholder.AlphaBaseViewHolder
+//import com.yc.core.adapter.viewholder.cell.com.yc.module_base.model.DefaultRVBg
+//
+///**
+// * BgVH
+// *
+// * @author kou_zhong
+// * @date 2020/12/2
+// */
+//class BgVH(itemView: View) : AlphaBaseViewHolder<DefaultRVBg>(itemView) {
+//    constructor(itemView: ViewGroup) : this(itemView.createItemView(R.layout.module_core_wrap_vh_bg))
+//
+//    init {
+//        setFullSpan()
+//    }
+//}

+ 29 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/EmptyPlaceViewHolder.java

@@ -0,0 +1,29 @@
+package com.yc.baselibrary.adapter.viewHolder;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.yc.baselibrary.R;
+
+/**
+ * 空视图占位
+ * Created by chen_bin on 2018/4/28 0028.
+ */
+public class EmptyPlaceViewHolder extends BaseViewHolder {
+
+    public EmptyPlaceViewHolder(ViewGroup parent) {
+        this(LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.empty_place_holder___cm, parent, false));
+    }
+
+    EmptyPlaceViewHolder(View itemView) {
+        super(itemView);
+    }
+
+    @Override
+    protected void setViewData(Context mContext, Object item, int position, int viewType) {
+
+    }
+}

+ 73 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/EmptyVH.kt

@@ -0,0 +1,73 @@
+//package com.yc.core.adapter.viewholder.cell
+//import android.content.Context
+//import android.text.TextUtils
+//import android.view.View
+//import android.view.ViewGroup
+//import androidx.core.view.updatePadding
+//import androidx.recyclerview.widget.RecyclerView
+//import com.hunliji.hljcorewraplibrary.R
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.MutableAdapter
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.observable.ObservableAdapterList
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.viewholder.AlphaBaseViewHolder
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.viewholder.cell.com.yc.module_base.model.DefaultRVEmpty
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.adapter.viewholder.setFullSpan
+//import com.hunliji.hljcorewraplibrary.mvvm.ext.createItemView
+//import kotlinx.android.synthetic.main.module_core_wrap_vh_empty.view.*
+//
+///**
+// * EmptyVHKit
+// *
+// * @author kou_zhong
+// * @date 2020/11/13
+// */
+//class EmptyVH(itemView: View) : AlphaBaseViewHolder<DefaultRVEmpty>(itemView) {
+//    constructor(itemView: ViewGroup, onRequestReload: (() -> Unit)? = null)
+//            : this(itemView.createItemView(R.layout.module_core_wrap_vh_empty)) {
+//        this.onRequestReload = onRequestReload
+//    }
+//
+//    private var onRequestReload: (() -> Unit)? = null
+//
+//    init {
+//        setFullSpan()
+//        itemView.setOnClickListener {
+//            onRequestReload?.invoke()
+//        }
+//    }
+//
+//    override fun setViewData(
+//        context: Context,
+//        item: DefaultRVEmpty?,
+//        position: Int,
+//        viewType: Int
+//    ) {
+//        item ?: return
+//        if (!TextUtils.isEmpty(item.hint)) {
+//            itemView.module_core_wrap_vh_empty_hint.text = item.hint
+//        }
+//        itemView.updatePadding(
+//            top = item.paddingTop,
+//            bottom = item.paddingBottom
+//        )
+//    }
+//
+//    override fun setViewData(
+//        context: Context,
+//        item: DefaultRVEmpty?,
+//        position: Int,
+//        viewType: Int,
+//        recyclerView: RecyclerView?,
+//        mutableAdapter: MutableAdapter,
+//        map: HashMap<String, Any>,
+//        observableList: ObservableAdapterList<Any>
+//    ) {
+//        item ?: return
+//        if (!TextUtils.isEmpty(item.hint)) {
+//            itemView.module_core_wrap_vh_empty_hint.text = item.hint
+//        }
+//        itemView.updatePadding(
+//            top = item.paddingTop,
+//            bottom = item.paddingBottom
+//        )
+//    }
+//}

+ 160 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/MutableAdapter.kt

@@ -0,0 +1,160 @@
+package com.yc.baselibrary.adapter.viewHolder
+
+import android.util.SparseArray
+import android.view.ViewGroup
+import androidx.collection.ArrayMap
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.RecyclerView
+import com.yc.baselibrary.adapter.BaseStickyHeaderAdapter
+import com.yc.baselibrary.adapter.observable.ObservableAdapterList
+
+/**
+ * MultiTypeAdapter
+ *
+ * @author kou_zhong
+ * @date 2020/10/12
+ */
+@Suppress("UNCHECKED_CAST")
+open class MutableAdapter(
+    val list: ObservableAdapterList<Any>,
+    draggable: Boolean = false,
+    dragType: Int = ItemTouchHelper.UP or ItemTouchHelper.DOWN,
+    headerVh: ((parent: ViewGroup) -> BaseViewHolder<*>)? = null,
+    private val onBind: ((position: Int, vh: BaseViewHolder<*>) -> Unit)? = null
+) : BaseStickyHeaderAdapter<BaseViewHolder<*>>(list, draggable, dragType, headerVh) {
+
+    private val itemTypeToViewHolderMap = SparseArray<ViewHolderVm<*>>()
+    private val javaClassToItemType = hashMapOf<Class<*>, Int>()
+    private val itemTypeOffsets = ArrayMap<Class<*>, ItemTypeOffset<*>>()
+
+    private var itemTypeCounter = 1
+
+    /**
+     * 添加新类型, 或覆盖原有类型
+     */
+    fun <T> addVhDelegate(javaClass: Class<*>, vh: CreateVh<T>) {
+        val itemType = javaClassToItemType[javaClass]
+        if (itemType == null) {
+            javaClassToItemType[javaClass] = itemTypeCounter
+            itemTypeToViewHolderMap.put(itemTypeCounter, ViewHolderVm(vh))
+            itemTypeCounter++
+        } else {
+            // 原本是 multi-type 的 item, 则移除 offset
+            // 但该 item 的其他 type 不会被覆盖, 仍然保存在 itemTypeToViewHolderMap 中
+            itemTypeOffsets.remove(javaClass)
+            itemTypeToViewHolderMap.put(itemType, ViewHolderVm(vh))
+        }
+    }
+
+    fun getItemType(javaClass: Class<*>): Int = javaClassToItemType[javaClass] ?: -1
+
+    inline fun <reified T> addVhDelegate(noinline vh: CreateVh<T>) {
+        addVhDelegate(T::class.java, vh)
+    }
+
+    fun <T> addAlternativeVhDelegates(
+        clz: Class<*>, offset: ItemTypeOffset<Any>, vararg vh: CreateVh<T>
+    ) {
+        if (javaClassToItemType.containsKey(clz) && itemTypeOffsets.containsKey(clz)) {
+            return
+        }
+        itemTypeOffsets[clz] = offset
+        javaClassToItemType[clz] = itemTypeCounter
+        vh.forEach {
+            itemTypeToViewHolderMap.put(itemTypeCounter++, ViewHolderVm(it))
+        }
+    }
+
+    /**
+     * @param offset 当存在 multi-type item 时, 对应的 view type 的偏移 (从 0 开始)
+     */
+    fun <T> addVhDelegates(clz: Class<*>, offset: ItemTypeOffset<T>, vararg vh: CreateVh<T>) {
+        if (javaClassToItemType.containsKey(clz) && itemTypeOffsets.containsKey(clz)) {
+            return
+        }
+        itemTypeOffsets[clz] = offset
+        javaClassToItemType[clz] = itemTypeCounter
+        vh.forEach {
+            itemTypeToViewHolderMap.put(itemTypeCounter++, ViewHolderVm(it))
+        }
+    }
+
+    inline fun <reified T> addVhDelegates(
+        vararg vh: CreateVh<T>, noinline offset: ItemTypeOffset<T>
+    ) {
+        addVhDelegates(T::class.java, offset, *vh)
+    }
+
+    override fun getItemViewType(position: Int, flag: Int): Int {
+        val item = list[position]
+        val clz = item.javaClass
+
+        val offset = itemTypeOffsets[clz]
+        // 如果存在多个 data 同类型的 item
+        // 需要通过 offset 计算 delegate 的偏移
+        val offsetValue = if (offset != null) {
+            offset as ItemTypeOffset<Any>
+            offset(item, position)
+        } else {
+            0
+        }
+
+        return (javaClassToItemType[clz] ?: 0) + offsetValue
+    }
+
+    override fun onCreateViewHolder(
+        parent: ViewGroup, viewType: Int, flag: Int
+    ): BaseViewHolder<out Any?> =
+        itemTypeToViewHolderMap.get(viewType)?.getVh(parent) ?: EmptyPlaceViewHolder(parent)
+
+    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int, flag: Int) {
+        onBind?.invoke(position, holder)
+        if (holder is BaseMutableViewHolder<*>) {
+            (holder as? BaseMutableViewHolder<Any>)?.setView(
+                item = (list[position]),
+                position = position,
+                adapter = this@MutableAdapter,
+                recyclerView = recyclerView,
+                map = extraData,
+                observableList = list
+            )
+        } else {
+            (holder as? BaseViewHolder<Any>)?.setView((list[position]), position)
+        }
+    }
+
+    override fun onBindHeaderViewHolder(holder: BaseViewHolder<*>, position: Int) {
+        if (holder is BaseMutableViewHolder<*>) {
+            (holder as? BaseMutableViewHolder<Any>)?.setView(
+                item = (list[position]),
+                position = position,
+                adapter = this@MutableAdapter,
+                recyclerView = recyclerView,
+                map = extraData,
+                observableList = list
+            )
+        } else {
+            (holder as? BaseViewHolder<Any>)?.setView((list[position]), position)
+        }
+    }
+
+    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
+        super.onDetachedFromRecyclerView(recyclerView)
+        itemTypeToViewHolderMap.clear()
+        javaClassToItemType.clear()
+        itemTypeCounter = 1
+    }
+}
+
+typealias CreateVh<T> = (parent: ViewGroup) -> BaseViewHolder<T>
+
+/**
+ * 根据 item 与 item view 的位置, 返回 offset (从 0 开始)
+ */
+typealias ItemTypeOffset<T> = (item: T, pos: Int) -> Int
+
+class ViewHolderVm<T>(
+    val vh: CreateVh<T>
+) {
+    fun getVh(parent: ViewGroup): BaseViewHolder<T> = vh.invoke(parent)
+}

+ 69 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/ObservableBaseViewHolder.kt

@@ -0,0 +1,69 @@
+package com.yc.baselibrary.adapter.viewHolder
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import kotlinx.coroutines.*
+
+/**
+ * ObservableBaseViewHolder
+ *
+ * @author wm
+ * @date 20-7-31
+ */
+abstract class ObservableBaseViewHolder<T>(itemView: View) : BaseViewHolder<T>(itemView),
+    LifecycleOwner, LifecycleObserver, CoroutineScope by MainScope(),
+    View.OnClickListener {
+
+    private val registry by lazy {
+        LifecycleRegistry(this)
+    }
+
+    override fun getLifecycle(): Lifecycle = registry
+
+    fun onAttach() {
+        registry.currentState = Lifecycle.State.STARTED
+    }
+
+    fun onDetach() {
+        registry.currentState = Lifecycle.State.DESTROYED
+    }
+
+//    /**
+//     * VH中请求接口
+//     *
+//     * @param block
+//     * @param build
+//     */
+//    fun <T> requestMix(
+//        block: suspend CoroutineScope.() -> T,
+//        build: RequestBuilder<T>.() -> Unit = {}
+//    ): Job = launch {
+//        val builder = RequestBuilder<T>().also(build)
+//        try {
+//            val data = withContext(Dispatchers.IO) {
+//                block()
+//            }
+//            builder.getSuccess()?.invoke(data)
+//        } catch (e: Exception) {
+//            builder.getError()?.invoke(e)
+//        }
+//    }
+
+    /**
+     * 给views添加点击事件
+     *
+     * @param views
+     */
+    fun addClickView(vararg views: View?) {
+        views.forEach {
+            it?.setOnClickListener(this)
+        }
+    }
+
+    override fun onClick(v: View?) {
+
+    }
+
+}

+ 38 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/PaginationTool.java

@@ -0,0 +1,38 @@
+package com.yc.baselibrary.adapter.viewHolder;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.StaggeredGridLayoutManager;
+
+/**
+ * Created by yc on 2021/9/14
+ **/
+public class PaginationTool {
+    public static int getLastVisibleItemPosition(RecyclerView recyclerView) {
+        Class recyclerViewLMClass = recyclerView.getLayoutManager()
+                .getClass();
+        if (recyclerViewLMClass == LinearLayoutManager.class || LinearLayoutManager.class
+                .isAssignableFrom(
+                        recyclerViewLMClass)) {
+            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
+                    .getLayoutManager();
+            return linearLayoutManager.findLastVisibleItemPosition();
+        } else if (recyclerViewLMClass == StaggeredGridLayoutManager.class ||
+                StaggeredGridLayoutManager.class.isAssignableFrom(
+                        recyclerViewLMClass)) {
+            StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager)
+                    recyclerView.getLayoutManager();
+            int[] into = staggeredGridLayoutManager.findLastVisibleItemPositions(null);
+            List<Integer> intoList = new ArrayList<>();
+            for (int i : into) {
+                intoList.add(i);
+            }
+            return Collections.max(intoList);
+        }
+        throw new PagingException("Unknown LayoutManager class: " + recyclerViewLMClass.toString());
+    }
+}

+ 10 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/PagingException.java

@@ -0,0 +1,10 @@
+package com.yc.baselibrary.adapter.viewHolder;
+
+/**
+ * Created by werther on 16/8/2.
+ */
+public class PagingException extends RuntimeException {
+    public PagingException(String detailMessage) {
+        super(detailMessage);
+    }
+}

+ 81 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/TitleVH.kt

@@ -0,0 +1,81 @@
+//package com.yc.core.adapter.viewholder.cell
+//import android.content.Context
+//import android.graphics.Typeface
+//import android.view.View
+//import android.view.ViewGroup
+//import android.widget.TextView
+//import androidx.core.view.updatePadding
+//import androidx.recyclerview.widget.RecyclerView
+//import com.yc.core.adapter.viewholder.AlphaBaseViewHolder
+//import kotlinx.android.synthetic.main.module_core_wrap_vh_title.view.*
+//
+///**
+// * TitleVH
+// *
+// * @author kou_zhong
+// * @date 2020/11/13
+// */
+//class TitleVH(itemView: View) : AlphaBaseViewHolder<DefaultRVTitle>(itemView) {
+//    constructor(itemView: ViewGroup, onBind: ((textView: TextView) -> Unit)? = null)
+//            : this(itemView.createItemView(R.layout.module_core_wrap_vh_title)) {
+//        this.onBind = onBind
+//    }
+//
+//    init {
+//        setFullSpan()
+//    }
+//
+//    private var onBind: ((textView: TextView) -> Unit)? = null
+//
+//    override fun setViewData(
+//        context: Context,
+//        item: DefaultRVTitle?,
+//        position: Int,
+//        viewType: Int
+//    ) {
+//        item ?: return
+//        itemView.module_core_wrap_vh_title.apply {
+//            if (item.customTypefaceOpen) {
+//                setTextColor(item.color)
+//                textSize = item.textSize.toFloat()
+//                typeface = if (item.isBold) {
+//                    Typeface.defaultFromStyle(Typeface.BOLD)
+//                } else {
+//                    Typeface.defaultFromStyle(Typeface.NORMAL)
+//                }
+//                updatePadding(top = item.paddingTop, bottom = item.paddingBottom)
+//            } else {
+//                onBind?.invoke(this)
+//            }
+//            text = item.title
+//        }
+//    }
+//
+//    override fun setViewData(
+//        context: Context,
+//        item: DefaultRVTitle?,
+//        position: Int,
+//        viewType: Int,
+//        recyclerView: RecyclerView?,
+//        mutableAdapter: MutableAdapter,
+//        map: HashMap<String, Any>,
+//        observableList: ObservableAdapterList<Any>
+//    ) {
+//        item ?: return
+//        itemView.module_core_wrap_vh_title.apply {
+//            if (item.customTypefaceOpen) {
+//                setTextColor(item.color)
+//                textSize = item.textSize.toFloat()
+//                typeface = if (item.isBold) {
+//                    Typeface.defaultFromStyle(Typeface.BOLD)
+//                } else {
+//                    Typeface.defaultFromStyle(Typeface.NORMAL)
+//                }
+//                updatePadding(top = item.paddingTop, bottom = item.paddingBottom)
+//            } else {
+//                onBind?.invoke(this)
+//            }
+//            text = item.title
+//        }
+//    }
+//}

+ 19 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/ViewHolderExt.kt

@@ -0,0 +1,19 @@
+package com.yc.baselibrary.adapter.viewHolder
+
+import android.annotation.SuppressLint
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+
+/**
+ * ViewHolderExt
+ *
+ * @author wm
+ * @date 20-7-29
+ */
+@SuppressLint("CheckResult")
+fun BaseViewHolder<*>.setFullSpan() {
+    val lp = itemView.layoutParams
+    if (lp is StaggeredGridLayoutManager.LayoutParams){
+        lp.isFullSpan = true
+        itemView.layoutParams = lp
+    }
+}

+ 55 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/DefaultLoadMoreFooterCardView.kt

@@ -0,0 +1,55 @@
+package com.yc.baselibrary.adapter.viewHolder.footer
+
+import android.graphics.drawable.AnimationDrawable
+import android.view.View
+import androidx.core.content.ContextCompat
+import com.xueyu.kotlinextlibrary.gone
+import com.xueyu.kotlinextlibrary.visible
+import com.yc.baselibrary.R
+import kotlinx.android.synthetic.main.module_core_wrap_footer_card_cell.view.*
+
+/**
+ * DefaultLoadMoreFooterView
+ *
+ * @author kou_zhong
+ * @date 2020/9/25
+ */
+open class DefaultLoadMoreFooterCardView(val noMore: String = "没有更多了") : LoadMoreFooterView {
+
+    protected open val textColor: Int? = null
+
+    override fun onViewCreated(itemView: View) {
+
+        itemView.no_more_hint.setTextColor(ContextCompat.getColor(itemView.context,R.color.colorBlack2))
+        itemView.loading_hint.setTextColor(ContextCompat.getColor(itemView.context,R.color.colorBlack2))
+    }
+
+    override fun onDefault(itemView: View) {
+        gone(itemView.no_more_hint, itemView.loading_content, itemView.footer_content)
+    }
+
+    override fun onLoading(itemView: View) {
+        visible(itemView.footer_content, itemView.loading_content)
+        gone(itemView.no_more_hint)
+        itemView.load_more_image.setImageResource(R.drawable.progress_indeterminate_s)
+        val drawable = itemView.load_more_image.drawable
+        if (drawable is AnimationDrawable) {
+            drawable.start()
+        }
+    }
+
+    override fun onEnd(itemView: View) {
+        visible(itemView.footer_content, itemView.no_more_hint)
+        itemView.no_more_hint.text = noMore
+        gone(itemView.loading_content)
+    }
+
+    override fun onError(itemView: View) {
+        visible(itemView.footer_content, itemView.no_more_hint)
+        itemView.no_more_hint.text = "加载出错了,请上滑重试"
+        gone(itemView.loading_content)
+    }
+
+    override fun getLayoutId() = R.layout.module_core_wrap_footer_card_cell
+
+}

+ 56 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/DefaultLoadMoreFooterView.kt

@@ -0,0 +1,56 @@
+package com.yc.baselibrary.adapter.viewHolder.footer
+
+import android.graphics.drawable.AnimationDrawable
+import android.view.View
+import androidx.core.content.ContextCompat
+import com.xueyu.kotlinextlibrary.gone
+import com.xueyu.kotlinextlibrary.visible
+import com.yc.baselibrary.R
+import kotlinx.android.synthetic.main.module_core_wrap_footer_cell.view.footer_content
+import kotlinx.android.synthetic.main.module_core_wrap_footer_cell.view.load_more_image
+import kotlinx.android.synthetic.main.module_core_wrap_footer_cell.view.loading_content
+import kotlinx.android.synthetic.main.module_core_wrap_footer_cell.view.no_more_hint
+
+/**
+ * DefaultLoadMoreFooterView
+ *
+ * @author kou_zhong
+ * @date 2020/9/25
+ */
+open class DefaultLoadMoreFooterView(val noMore: String = "没有更多了") : LoadMoreFooterView {
+
+    protected open val textColor: Int? = null
+
+    override fun onViewCreated(itemView: View) {
+        itemView.no_more_hint.setTextColor(ContextCompat.getColor(itemView.context,R.color.colorBlack2))
+    }
+
+    override fun onDefault(itemView: View) {
+        gone(itemView.no_more_hint, itemView.loading_content, itemView.footer_content)
+    }
+
+    override fun onLoading(itemView: View) {
+        visible(itemView.footer_content, itemView.loading_content)
+        gone(itemView.no_more_hint)
+        itemView.load_more_image.setImageResource(R.drawable.progress_indeterminate_s)
+        val drawable = itemView.load_more_image.drawable
+        if (drawable is AnimationDrawable) {
+            drawable.start()
+        }
+    }
+
+    override fun onEnd(itemView: View) {
+        visible(itemView.footer_content, itemView.no_more_hint)
+        itemView.no_more_hint.text = noMore
+        gone(itemView.loading_content)
+    }
+
+    override fun onError(itemView: View) {
+        visible(itemView.footer_content, itemView.no_more_hint)
+        itemView.no_more_hint.text = "加载出错了,请上滑重试"
+        gone(itemView.loading_content)
+    }
+
+    override fun getLayoutId() = R.layout.module_core_wrap_footer_cell_68
+
+}

+ 33 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/FooterViewHolder.kt

@@ -0,0 +1,33 @@
+package com.yc.baselibrary.adapter.viewHolder.footer
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.yc.baselibrary.adapter.viewHolder.AlphaBaseViewHolder
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_DEFAULT
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_END
+import com.yc.baselibrary.adapter.viewHolder.footer.LoadMoreFooterView.Companion.STATUS_LOADING
+
+class FooterViewHolder(itemView: View) : AlphaBaseViewHolder<Int>(itemView) {
+    private var loadMore: LoadMoreFooterView? = null
+
+    constructor(itemView: ViewGroup, loadMore: LoadMoreFooterView) : this(
+        LayoutInflater.from(itemView.context).inflate(loadMore.getLayoutId(), itemView, false)
+    ) {
+        this.loadMore = loadMore
+    }
+
+    override fun setViewData(
+        context: Context, item: Int?, position: Int, viewType: Int
+    ) {
+        if (item == null) {
+            return
+        }
+        when (item) {
+            STATUS_DEFAULT -> loadMore?.onDefault(itemView)
+            STATUS_LOADING -> loadMore?.onLoading(itemView)
+            STATUS_END -> loadMore?.onEnd(itemView)
+        }
+    }
+}

+ 30 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/footer/LoadMoreFooterView.kt

@@ -0,0 +1,30 @@
+package com.yc.baselibrary.adapter.viewHolder.footer
+
+import android.view.View
+
+/**
+ * LoadMoreFooterView
+ *
+ * @author kou_zhong
+ * @date 2020/9/25
+ */
+interface LoadMoreFooterView {
+    companion object {
+        const val STATUS_DEFAULT = 0
+        const val STATUS_LOADING = 1
+        const val STATUS_END = 2
+        const val STATUS_ERROR = 3
+    }
+
+    fun onDefault(itemView: View)
+
+    fun onLoading(itemView: View)
+
+    fun onEnd(itemView: View)
+
+    fun onError(itemView: View)
+
+    fun getLayoutId(): Int
+
+    fun onViewCreated(itemView: View)
+}

+ 28 - 0
baselibrary/src/main/java/com/yc/baselibrary/adapter/viewHolder/model/DefaultVHModule.kt

@@ -0,0 +1,28 @@
+package com.yc.baselibrary.adapter.viewHolder.model
+
+
+/**
+ * @author kou_zhong
+ * @date 2020/11/13
+ */
+data class DefaultRVEmpty(
+    val hint: String = "",
+    val paddingTop: Int = 120,
+    val paddingBottom: Int = 120
+)
+
+data class DefaultRVTitle(
+    val customTypefaceOpen: Boolean = false,
+    val title: String = "",
+    val desc: String = "",
+    val isBold: Boolean = true,
+    val textSize: Int = 20,
+    val paddingTop: Int = 40,
+    val paddingBottom: Int = 0
+)
+
+class DefaultRVBg
+
+interface StickyHeaderInterface {
+    fun getHeaderId(): Long
+}

+ 14 - 0
baselibrary/src/main/java/com/yc/baselibrary/api/WebService.kt

@@ -0,0 +1,14 @@
+package com.yc.baselibrary.api
+
+import com.yc.baselibrary.net.model.Response
+import retrofit2.http.POST
+
+/** Created by yc on 2021/2/24
+ **/
+interface WebService{
+
+    @POST("api/user/getToken")
+    suspend fun getH5Token(): Response<String>
+
+
+}

+ 146 - 0
baselibrary/src/main/java/com/yc/baselibrary/behavior/AppBarLayoutFixedBehavior.java

@@ -0,0 +1,146 @@
+package com.yc.baselibrary.behavior;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.OverScroller;
+
+import com.google.android.material.appbar.AppBarLayout;
+import com.yc.baselibrary.widget.DisInterceptNestedScrollView;
+
+import java.lang.reflect.Field;
+
+import androidx.annotation.NonNull;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.view.ViewCompat;
+
+public class AppBarLayoutFixedBehavior extends AppBarLayout.Behavior {
+
+    /**
+     * 是否跳过嵌套拦截
+     */
+    private boolean skipNestedIntercept;
+
+    public AppBarLayoutFixedBehavior() {
+        super();
+    }
+
+    public AppBarLayoutFixedBehavior(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onNestedScroll(CoordinatorLayout coordinatorLayout,
+                               @NonNull AppBarLayout child,
+                               View target,
+                               int dxConsumed,
+                               int dyConsumed,
+                               int dxUnconsumed,
+                               int dyUnconsumed,
+                               int type,
+                               int[] consumed) {
+        super.onNestedScroll(coordinatorLayout,
+                child,
+                target,
+                dxConsumed,
+                dyConsumed,
+                dxUnconsumed,
+                dyUnconsumed,
+                type,
+                consumed);
+        stopNestedScrollIfNeeded(dyUnconsumed, child, target, type);
+    }
+
+    @Override
+    public void onNestedPreScroll(
+            CoordinatorLayout coordinatorLayout,
+            @NonNull AppBarLayout child,
+            View target,
+            int dx,
+            int dy,
+            int[] consumed,
+            int type) {
+        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
+        stopNestedScrollIfNeeded(dy, child, target, type);
+    }
+
+    private void stopNestedScrollIfNeeded(int dy, AppBarLayout child, View target, int type) {
+        if (type == ViewCompat.TYPE_NON_TOUCH) {
+            final int currOffset = getTopAndBottomOffset();
+            if ((dy < 0 && currOffset == 0) || (dy > 0 && currOffset == -child.getTotalScrollRange())) {
+                ViewCompat.stopNestedScroll(target, ViewCompat.TYPE_NON_TOUCH);
+            }
+        }
+    }
+
+    @Override
+    public boolean onStartNestedScroll(
+            @NonNull CoordinatorLayout parent,
+            @NonNull AppBarLayout child,
+            @NonNull View directTargetChild,
+            View target,
+            int nestedScrollAxes,
+            int type) {
+        if (skipNestedIntercept) {
+            return false;
+        }
+        if (target instanceof DisInterceptNestedScrollView) {
+            return true;
+        }
+        return super.onStartNestedScroll(parent,
+                child,
+                directTargetChild,
+                target,
+                nestedScrollAxes,
+                type);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(
+            @NonNull CoordinatorLayout parent, @NonNull AppBarLayout child, MotionEvent ev) {
+        if (skipNestedIntercept) {
+            return false;
+        }
+        //防止fling时,AppBarLayout页面抖动
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            Object scroller = getSuperSuperField(this);
+            if (scroller instanceof OverScroller) {
+                OverScroller overScroller = (OverScroller) scroller;
+                overScroller.abortAnimation();
+            }
+        }
+        return super.onInterceptTouchEvent(parent, child, ev);
+    }
+
+    private Object getSuperSuperField(Object paramClass) {
+        Field field;
+        Object object = null;
+        try {
+            Class<?> behavior = paramClass.getClass();
+            while (behavior != AppBarLayout.Behavior.class) {
+                behavior = behavior.getSuperclass();
+                if(behavior==null){
+                    return null;
+                }
+            }
+            Class<?> headerBehavior = behavior.getSuperclass().getSuperclass();
+            field = headerBehavior.getDeclaredField("scroller");
+            field.setAccessible(true);
+            object = field.get(paramClass);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return object;
+    }
+
+
+    /**
+     * 设置跳过嵌套拦截
+     * @param skipNestedIntercept
+     */
+    public void setSkipNestedIntercept(boolean skipNestedIntercept) {
+        this.skipNestedIntercept = skipNestedIntercept;
+    }
+
+}

+ 163 - 0
baselibrary/src/main/java/com/yc/baselibrary/behavior/AppBarLayoutSnapScrollViewBehavior.java

@@ -0,0 +1,163 @@
+package com.yc.baselibrary.behavior;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import com.google.android.material.appbar.AppBarLayout;
+
+import androidx.annotation.NonNull;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.view.ViewCompat;
+
+/**
+ * Created by chen_bin on 2017/12/29.
+ */
+public class AppBarLayoutSnapScrollViewBehavior extends AppBarLayoutFixedBehavior {
+
+    private int mDirection;
+    private int mLastMotionY;
+    private int mTouchSlop = -1;
+    private boolean mIsBeingDragged;
+
+    private final static int DIRECTION_INIT = 0;
+    private final static int DIRECTION_UP = 1;
+    private final static int DIRECTION_DOWN = 2;
+
+    private static final int INVALID_POINTER = -1;
+    private int mActivePointerId = INVALID_POINTER;
+
+    public AppBarLayoutSnapScrollViewBehavior() {
+
+    }
+
+    public AppBarLayoutSnapScrollViewBehavior(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        this.mTouchSlop = ViewConfiguration.get(context)
+                .getScaledTouchSlop();
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(
+            @NonNull CoordinatorLayout parent, @NonNull AppBarLayout child, MotionEvent ev) {
+        final int action = ev.getAction();
+
+        // Shortcut since we're being dragged
+        if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) {
+            return true;
+        }
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN: {
+                mIsBeingDragged = false;
+                final int x = (int) ev.getX();
+                final int y = (int) ev.getY();
+                if (parent.isPointInChildBounds(child, x, y)) {
+                    mLastMotionY = y;
+                    mActivePointerId = ev.getPointerId(0);
+                }
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                final int activePointerId = mActivePointerId;
+                if (activePointerId == INVALID_POINTER) {
+                    break;
+                }
+                final int pointerIndex = ev.findPointerIndex(activePointerId);
+                if (pointerIndex == -1) {
+                    break;
+                }
+                final int y = (int) ev.getY();
+                final int yDiff = Math.abs(y - mLastMotionY);
+                if (yDiff > mTouchSlop) {
+                    mIsBeingDragged = true;
+                    mLastMotionY = y;
+                }
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP: {
+                mIsBeingDragged = false;
+                mActivePointerId = INVALID_POINTER;
+                break;
+            }
+        }
+        return mIsBeingDragged;
+    }
+
+    @Override
+    public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_UP:
+                if (!mIsBeingDragged) {
+                    break;
+                }
+                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                if (activePointerIndex == -1) {
+                    return false;
+                }
+                final int y = (int) ev.getY(activePointerIndex);
+                int dy = mLastMotionY - y;
+                if (Math.abs(dy) > mTouchSlop) {
+                    child.setExpanded(mDirection == (dy < 0 ? DIRECTION_DOWN : DIRECTION_UP), true);
+                }
+            case MotionEvent.ACTION_CANCEL: {
+                mIsBeingDragged = false;
+                mActivePointerId = INVALID_POINTER;
+                break;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onNestedPreScroll(
+            CoordinatorLayout coordinatorLayout,
+            @NonNull AppBarLayout child,
+            View target,
+            int dx,
+            int dy,
+            int[] consumed,
+            int type) {
+        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
+        mDirection = dy == 0 ? DIRECTION_INIT : dy < 0 ? DIRECTION_DOWN : DIRECTION_UP;
+    }
+
+    @Override
+    public boolean onNestedPreFling(
+            @NonNull CoordinatorLayout coordinatorLayout,
+            @NonNull AppBarLayout child,
+            @NonNull View target,
+            float velocityX,
+            float velocityY) {
+        return true;
+    }
+
+    @Override
+    public void onStopNestedScroll(
+            CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {
+        if (type == ViewCompat.TYPE_TOUCH) {
+            snapScroll(abl);
+        }
+    }
+
+    private void snapScroll(final AppBarLayout abl) {
+        if (mDirection == DIRECTION_INIT) {
+            return;
+        }
+        final int top = Math.abs(abl.getTop());
+        final int ablHeight = abl.getMeasuredHeight();
+        if (top == 0 || top >= ablHeight) {
+            return;
+        }
+        if (top <= 120) {
+            abl.setExpanded(true, true);
+        } else if (top <= ablHeight - 120) {
+            abl.setExpanded(mDirection == DIRECTION_DOWN, true);
+        } else {
+            abl.setExpanded(false, true);
+        }
+    }
+
+}

+ 56 - 0
baselibrary/src/main/java/com/yc/baselibrary/behavior/AppBarLinkedBehavior.java

@@ -0,0 +1,56 @@
+package com.yc.baselibrary.behavior;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.google.android.material.appbar.AppBarLayout;
+
+import androidx.annotation.NonNull;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
+public class AppBarLinkedBehavior extends AppBarLayoutFixedBehavior {
+
+    private boolean intercepted = true;
+
+    public AppBarLinkedBehavior() {
+        super();
+    }
+
+    public AppBarLinkedBehavior(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setIntercepted(boolean intercepted) {
+        this.intercepted = intercepted;
+    }
+
+    @Override
+    public boolean onStartNestedScroll(@NonNull CoordinatorLayout parent, @NonNull AppBarLayout child,
+                                       @NonNull View directTargetChild, View target,
+                                       int nestedScrollAxes, int type) {
+        return super.onStartNestedScroll(parent, child, directTargetChild, target,
+                nestedScrollAxes, type) && intercepted;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull AppBarLayout child, MotionEvent ev) {
+        return super.onInterceptTouchEvent(parent, child, ev) && intercepted;
+    }
+
+    public static AppBarLinkedBehavior from(View view) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        if (!(params instanceof CoordinatorLayout.LayoutParams)) {
+            throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
+        }
+        CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
+                .getBehavior();
+        if (!(behavior instanceof AppBarLinkedBehavior)) {
+            throw new IllegalArgumentException(
+                    "The view is not associated with AppBarLinkedBehavior");
+        }
+        return (AppBarLinkedBehavior) behavior;
+    }
+}

+ 581 - 0
baselibrary/src/main/java/com/yc/baselibrary/behavior/BottomLinkedBehavior.java

@@ -0,0 +1,581 @@
+package com.yc.baselibrary.behavior;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.Scroller;
+
+
+import com.yc.baselibrary.R;
+import com.yc.baselibrary.utils.FlingHelper;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.math.MathUtils;
+import androidx.core.view.ViewCompat;
+import androidx.customview.widget.ViewDragHelper;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+public class BottomLinkedBehavior extends CoordinatorLayout.Behavior {
+
+    public interface BottomLinkedCallback {
+
+        /**
+         * Called when the bottom sheet changes its state.
+         *
+         * @param bottomSheet The bottom sheet view.
+         * @param newState    The new state. This will be one of {@link #STATE_DRAGGING},
+         *                    {@link #STATE_EXPANDED},{@link #STATE_COLLAPSED}.
+         */
+        void onStateChanged(@NonNull View bottomSheet, @RecyclerViewBehavior.State int newState);
+
+        /**
+         * 用来判断其子View是否滑到顶部
+         *
+         * @return 是否滑动顶部
+         */
+        boolean isChildScrollTop();
+
+        /**
+         * 当前view的位置变化
+         *
+         * @param dy 竖直距离
+         */
+        void onPositionChanged(int dy);
+    }
+
+    /**
+     * The bottom sheet is first.
+     */
+    public static final int STATE_FIRST = 0;
+    /**
+     * The bottom sheet is dragging.
+     */
+    public static final int STATE_DRAGGING = 1;
+
+    /**
+     * The bottom sheet is expanded.
+     */
+    public static final int STATE_EXPANDED = 2;
+
+    /**
+     * The bottom sheet is collapsed.
+     */
+    public static final int STATE_COLLAPSED = 3;
+    /**
+     * The bottom sheet is settling.
+     */
+    public static final int STATE_SETTLING = 4;
+
+
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_FIRST})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+    }
+
+    private static final int THRESHOLD = 200;
+    private static final float FRICTION = 0.1f;
+
+    private int mPeekHeight;
+
+    private int mMinOffset;
+
+    private int mMaxOffset;
+
+    private int mFirstOffset;
+
+    @State
+    private int mState = STATE_COLLAPSED;
+
+    private ViewDragHelper mViewDragHelper;
+
+    private boolean mIgnoreEvents;
+
+    private int mParentHeight;
+
+    private WeakReference<View> mViewRef;
+
+    private int mActivePointerId;
+
+    private int mInitialY;
+
+    private boolean mTouchingScrollingChild;
+
+    /**
+     * 是否进行联动
+     */
+    private boolean isLinked;
+
+    private BottomLinkedCallback mCallback;
+
+    private Scroller mScroller;
+    private FlingRunnable mFlingRunnable;
+    private FlingHelper mFlingHelper;
+
+    public BottomLinkedBehavior() {
+    }
+
+    public BottomLinkedBehavior(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.BottomSheetBehavior_Layout);
+        setPeekHeight(a.getDimensionPixelSize(
+                R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, 0));
+        a.recycle();
+        mScroller = new Scroller(context);
+        mFlingHelper = new FlingHelper(context);
+    }
+
+    public Scroller getScroller() {
+        return mScroller;
+    }
+
+    public void fling(float velocityY) {
+        if (mViewRef.get() == null) {
+            return;
+        }
+        if (isLinked) {
+            if (mFlingRunnable != null) {
+                mViewRef.get().removeCallbacks(mFlingRunnable);
+                mFlingRunnable = null;
+            }
+            mScroller.fling(
+                    0, mViewRef.get().getTop(),
+                    0, Math.round(velocityY),
+                    0, 0,
+                    mMinOffset, mMaxOffset);
+
+            if (mScroller.computeScrollOffset()) {
+                mFlingRunnable = new FlingRunnable(mViewRef.get(), true);
+                ViewCompat.postOnAnimation(mViewRef.get(), mFlingRunnable);
+            } else {
+                onFlingFinished(mViewRef.get());
+            }
+        } else {
+            double distance = mFlingHelper.getSplineFlingDistance(Math.round(velocityY));
+            int newTop = (int) (mViewRef.get().getTop() + Math.round(distance));
+            if (newTop <= mMinOffset + THRESHOLD) {
+                startSettlingAnimation(mViewRef.get(), STATE_EXPANDED);
+            } else {
+                startSettlingAnimation(mViewRef.get(), STATE_COLLAPSED);
+            }
+        }
+    }
+
+    public boolean isLinked() {
+        return isLinked;
+    }
+
+    public void setLinked(boolean linked) {
+        isLinked = linked;
+    }
+
+    public int getMaxOffset() {
+        return mMaxOffset;
+    }
+
+    public void setFirstOffset(int mFirstOffset) {
+        this.mFirstOffset = mFirstOffset;
+    }
+
+    @Override
+    public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec,
+                                  int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
+        final int heightMode = View.MeasureSpec.getMode(parentHeightMeasureSpec);
+        final int heightSize = View.MeasureSpec.getSize(parentHeightMeasureSpec);
+        parentHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
+                heightSize - mMinOffset, heightMode);
+        final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
+        final int childWidthMeasureSpec = ViewGroup.getChildMeasureSpec(parentWidthMeasureSpec,
+                lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
+        final int childHeightMeasureSpec = ViewGroup.getChildMeasureSpec(parentHeightMeasureSpec,
+                lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+        return true;
+    }
+
+    @Override
+    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
+        if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) {
+            child.setFitsSystemWindows(true);
+        }
+        int savedTop = child.getTop();
+        // First let the parent lay it out
+        parent.onLayoutChild(child, layoutDirection);
+        // Offset the bottom sheet
+        mParentHeight = parent.getHeight();
+        mMaxOffset = Math.max(mParentHeight - mPeekHeight, mMinOffset);
+        if (mState == STATE_EXPANDED) {
+            ViewCompat.offsetTopAndBottom(child, mMinOffset);
+        } else if (mState == STATE_COLLAPSED) {
+            ViewCompat.offsetTopAndBottom(child, mMaxOffset);
+        } else if (mState == STATE_DRAGGING || mState == STATE_SETTLING) {
+            ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop());
+        }
+        if (mViewDragHelper == null) {
+            mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
+        }
+        mViewRef = new WeakReference<>(child);
+        return true;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
+        if (!child.isShown()) {
+            mIgnoreEvents = true;
+            return false;
+        }
+        int action = event.getActionMasked();
+        switch (action) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mTouchingScrollingChild = false;
+                mActivePointerId = MotionEvent.INVALID_POINTER_ID;
+                // Reset the ignore flag
+                if (mIgnoreEvents) {
+                    mIgnoreEvents = false;
+                    return false;
+                }
+                break;
+            case MotionEvent.ACTION_DOWN:
+                int initialX = (int) event.getX();
+                mScroller.abortAnimation();
+                mInitialY = (int) event.getY();
+                if (parent.isPointInChildBounds(child, initialX, mInitialY)) {
+                    mActivePointerId = event.getPointerId(event.getActionIndex());
+                    mTouchingScrollingChild = true;
+                } else {
+                    mActivePointerId = MotionEvent.INVALID_POINTER_ID;
+                }
+                mIgnoreEvents =
+                        mActivePointerId == MotionEvent.INVALID_POINTER_ID
+                                && !parent.isPointInChildBounds(child, initialX, mInitialY);
+                break;
+            default:
+                break;
+        }
+        if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) {
+            return true;
+        }
+        // child未到parent顶部或者child滑动顶部,进行拦截
+        return action == MotionEvent.ACTION_MOVE
+                && !mIgnoreEvents
+                && mState != STATE_DRAGGING
+                && Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()
+                && (mCallback != null && mCallback.isChildScrollTop())
+                && !(mState == STATE_EXPANDED && mInitialY > event.getY());
+    }
+
+    @Override
+    public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) {
+        if (!child.isShown()) {
+            return false;
+        }
+        int action = event.getActionMasked();
+        if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
+            return true;
+        }
+        if (mViewDragHelper != null) {
+            mViewDragHelper.processTouchEvent(event);
+        }
+        // Record the velocity
+        if (action == MotionEvent.ACTION_DOWN) {
+            mActivePointerId = MotionEvent.INVALID_POINTER_ID;
+        }
+        // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
+        // to capture the bottom sheet in case it is not captured and the touch slop is passed.
+        if (action == MotionEvent.ACTION_MOVE && !mIgnoreEvents) {
+            if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) {
+                mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
+            }
+        }
+        return !mIgnoreEvents;
+    }
+
+    public final void setState(final @State int state) {
+        if (state == mState) {
+            return;
+        }
+        if (mViewRef == null) {
+            // The view is not laid out yet; modify mState and let onLayoutChild handle it later
+            if (state == STATE_COLLAPSED || state == STATE_EXPANDED) {
+                mState = state;
+            }
+            return;
+        }
+        final View child = mViewRef.get();
+        if (child == null) {
+            return;
+        }
+        // Start the animation; wait until a pending layout if there is one.
+        ViewParent parent = child.getParent();
+        if (parent != null && parent.isLayoutRequested() && ViewCompat.isAttachedToWindow(child)) {
+            child.post(new Runnable() {
+                @Override
+                public void run() {
+                    startSettlingAnimation(child, state);
+                }
+            });
+        } else {
+            startSettlingAnimation(child, state);
+        }
+    }
+
+    @State
+    public final int getState() {
+        return mState;
+    }
+
+    private void setStateInternal(@State int state) {
+        if (mState == state) {
+            return;
+        }
+        mState = state;
+        View bottomSheet = mViewRef.get();
+        if (bottomSheet != null && mCallback != null) {
+            mCallback.onStateChanged(bottomSheet, state);
+        }
+    }
+
+    private void startSettlingAnimation(View child, int state) {
+        int top;
+        if (state == STATE_COLLAPSED) {
+            top = mMaxOffset;
+        } else if (state == STATE_EXPANDED) {
+            top = mMinOffset;
+        } else if (state == STATE_FIRST) {
+            top = mParentHeight - mFirstOffset;
+        } else {
+            top = child.getTop();
+        }
+        if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
+            setStateInternal(STATE_SETTLING);
+            ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
+        } else {
+            setStateInternal(state);
+        }
+    }
+
+    private void onFlingFinished(View child) {
+        @State int targetState;
+        if (child.getTop() <= mMinOffset) {
+            targetState = STATE_EXPANDED;
+        } else if (child.getTop() >= mMaxOffset) {
+            targetState = STATE_COLLAPSED;
+        } else {
+            targetState = STATE_SETTLING;
+        }
+        startSettlingAnimation(child, targetState);
+    }
+
+    public void setBottomSheetCallback(BottomLinkedCallback callback) {
+        mCallback = callback;
+    }
+
+    private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
+
+        @Override
+        public boolean tryCaptureView(@NonNull View child, int pointerId) {
+            if (mState == STATE_DRAGGING) {
+                return false;
+            }
+            if (mTouchingScrollingChild) {
+                return false;
+            }
+            if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
+                if (child.canScrollVertically(-1)) {
+                    // Let the content scroll up
+                    return false;
+                }
+            }
+            return mViewRef != null && mViewRef.get() == child;
+        }
+
+        @Override
+        public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
+            if (mCallback != null && isLinked) {
+                mCallback.onPositionChanged(dy);
+            }
+        }
+
+        @Override
+        public void onViewDragStateChanged(int state) {
+            if (state == ViewDragHelper.STATE_DRAGGING) {
+                setStateInternal(STATE_DRAGGING);
+            }
+        }
+
+        @Override
+        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
+            int top;
+            @State int targetState;
+            final float newTop = releasedChild.getTop() + yvel * FRICTION;
+            if (isLinked) {
+                mViewDragHelper.flingCapturedView(0, mMinOffset, 0, mMaxOffset);
+                if (mViewDragHelper.continueSettling(true)) {
+                    ViewCompat.postOnAnimation(releasedChild, new FlingRunnable(releasedChild, false));
+                } else {
+                    onFlingFinished(releasedChild);
+                }
+                return;
+            } else if (yvel < 0) {
+                // 向上滑
+                if (newTop >= mMaxOffset - THRESHOLD) {
+                    top = mMaxOffset;
+                    targetState = STATE_COLLAPSED;
+                } else {
+                    top = mMinOffset;
+                    targetState = STATE_EXPANDED;
+                }
+            } else if (yvel == 0.f) {
+                // 匀速滑动
+                int currentTop = releasedChild.getTop();
+                if (Math.abs(currentTop - mMinOffset) < Math.abs(currentTop - mMaxOffset)) {
+                    top = mMinOffset;
+                    targetState = STATE_EXPANDED;
+                } else {
+                    top = mMaxOffset;
+                    targetState = STATE_COLLAPSED;
+                }
+            } else {
+                // 向下滑
+                if (newTop <= mMinOffset + THRESHOLD) {
+                    top = mMinOffset;
+                    targetState = STATE_EXPANDED;
+                } else {
+                    top = mMaxOffset;
+                    targetState = STATE_COLLAPSED;
+                }
+            }
+            if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
+                setStateInternal(STATE_SETTLING);
+                ViewCompat.postOnAnimation(releasedChild,
+                        new SettleRunnable(releasedChild, targetState));
+            } else {
+                setStateInternal(targetState);
+            }
+        }
+
+        @Override
+        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
+            return MathUtils.clamp(top, mMinOffset, mMaxOffset);
+        }
+
+        @Override
+        public int clampViewPositionHorizontal(View child, int left, int dx) {
+            return child.getLeft();
+        }
+
+        @Override
+        public int getViewVerticalDragRange(@NonNull View child) {
+            return mParentHeight;
+        }
+    };
+
+    public final void setPeekHeight(int peekHeight) {
+        mPeekHeight = Math.max(0, peekHeight);
+        mMaxOffset = mParentHeight - mPeekHeight;
+        if (mState == STATE_COLLAPSED && mViewRef != null) {
+            View view = mViewRef.get();
+            if (view != null) {
+                view.requestLayout();
+            }
+        }
+    }
+
+    public void setMinOffset(int minOffset) {
+        mMinOffset = minOffset;
+    }
+
+    private class SettleRunnable implements Runnable {
+
+        private final View mView;
+
+        @State
+        private final int mTargetState;
+
+        SettleRunnable(View view, @State int targetState) {
+            mView = view;
+            mTargetState = targetState;
+        }
+
+        @Override
+        public void run() {
+            if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
+                ViewCompat.postOnAnimation(mView, this);
+            } else {
+                setStateInternal(mTargetState);
+            }
+        }
+    }
+
+    private class FlingRunnable implements Runnable {
+
+        private final View mLayout;
+        private final boolean mHandleSelf;
+
+        FlingRunnable(View layout, boolean handleSelf) {
+            mLayout = layout;
+            mHandleSelf = handleSelf;
+        }
+
+        @Override
+        public void run() {
+            if (mLayout != null && mViewDragHelper != null) {
+                if (mHandleSelf && mScroller != null) {
+                    if (mScroller.computeScrollOffset()) {
+                        final int y = mScroller.getCurrY();
+                        final int dy = y - mLayout.getTop();
+                        if (dy != 0) {
+                            ViewCompat.offsetTopAndBottom(mLayout, dy);
+                        }
+                        if (dy != 0) {
+                            mDragCallback.onViewPositionChanged(mLayout, 0, y, 0, dy);
+                        }
+                        if (y == mScroller.getFinalY()) {
+                            mScroller.abortAnimation();
+                        }
+                        setStateInternal(STATE_SETTLING);
+                        ViewCompat.postOnAnimation(mLayout, this);
+                    } else {
+                        onFlingFinished(mLayout);
+                    }
+                } else {
+                    if (mViewDragHelper.continueSettling(true)) {
+                        setStateInternal(STATE_SETTLING);
+                        ViewCompat.postOnAnimation(mLayout, this);
+                    } else {
+                        onFlingFinished(mLayout);
+                    }
+                }
+            }
+        }
+    }
+
+    public static BottomLinkedBehavior from(View view) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        if (!(params instanceof CoordinatorLayout.LayoutParams)) {
+            throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
+        }
+        CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
+                .getBehavior();
+        if (!(behavior instanceof BottomLinkedBehavior)) {
+            throw new IllegalArgumentException(
+                    "The view is not associated with BottomLinkedBehavior");
+        }
+        return (BottomLinkedBehavior) behavior;
+    }
+
+}

+ 546 - 0
baselibrary/src/main/java/com/yc/baselibrary/behavior/RecyclerViewBehavior.java

@@ -0,0 +1,546 @@
+package com.yc.baselibrary.behavior;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+
+import com.yc.baselibrary.R;
+import com.yc.baselibrary.utils.FlingHelper;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.core.math.MathUtils;
+import androidx.core.view.ViewCompat;
+import androidx.customview.view.AbsSavedState;
+import androidx.customview.widget.ViewDragHelper;
+import androidx.recyclerview.widget.RecyclerView;
+import kankan.wheel.widget.CommonUtil;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+public class RecyclerViewBehavior extends CoordinatorLayout.Behavior<RecyclerView> {
+
+    public interface BottomSheetCallback {
+
+        /**
+         * Called when the bottom sheet changes its state.
+         *
+         * @param bottomSheet The bottom sheet view.
+         * @param newState    The new state. This will be one of {@link #STATE_DRAGGING},
+         *                    {@link #STATE_SETTLING}, {@link #STATE_EXPANDED},
+         *                    {@link #STATE_COLLAPSED}, or {@link #STATE_HIDDEN}.
+         */
+        void onStateChanged(@NonNull RecyclerView bottomSheet, @State int newState);
+    }
+
+    /**
+     * The bottom sheet is dragging.
+     */
+    public static final int STATE_DRAGGING = 1;
+
+    /**
+     * The bottom sheet is settling.
+     */
+    public static final int STATE_SETTLING = 2;
+
+    /**
+     * The bottom sheet is expanded.
+     */
+    public static final int STATE_EXPANDED = 3;
+
+    /**
+     * The bottom sheet is collapsed.
+     */
+    public static final int STATE_COLLAPSED = 4;
+
+    /**
+     * The bottom sheet is hidden.
+     */
+    public static final int STATE_HIDDEN = 5;
+
+    @RestrictTo(LIBRARY_GROUP)
+    @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+    }
+
+    private static final int THRESHOLD = 200;
+
+    private int mPeekHeight;
+
+    private int mMinOffset;
+
+    private int mMaxOffset;
+
+    @State
+    private int mState = STATE_EXPANDED;
+
+    private ViewDragHelper mViewDragHelper;
+
+    private boolean mIgnoreEvents;
+
+    private int mParentHeight;
+    private int mExpandedHeight;
+
+    private WeakReference<RecyclerView> mViewRef;
+
+    private BottomSheetCallback mCallback;
+
+    private VelocityTracker mVelocityTracker;
+
+    private int mActivePointerId;
+
+    private int mInitialY;
+
+    private boolean mTouchingScrollingChild;
+
+    private FlingHelper mFlingHelper;
+
+    public RecyclerViewBehavior() {
+    }
+
+    public RecyclerViewBehavior(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.BottomSheetBehavior_Layout);
+        setPeekHeight(a.getDimensionPixelSize(
+                R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, 0));
+        a.recycle();
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState(CoordinatorLayout parent, RecyclerView child) {
+        return new SavedState(super.onSaveInstanceState(parent, child), mState);
+    }
+
+    @Override
+    public void onRestoreInstanceState(CoordinatorLayout parent, RecyclerView child, Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(parent, child, ss.getSuperState());
+        // Intermediate states are restored as collapsed state
+        if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) {
+            mState = STATE_COLLAPSED;
+        } else {
+            mState = ss.state;
+        }
+    }
+
+    @Override
+    public boolean onMeasureChild(CoordinatorLayout parent, RecyclerView child,
+                                  int parentWidthMeasureSpec, int widthUsed,
+                                  int parentHeightMeasureSpec, int heightUsed) {
+        final int heightMode = View.MeasureSpec.getMode(parentHeightMeasureSpec);
+        final int heightSize = View.MeasureSpec.getSize(parentHeightMeasureSpec);
+        parentHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
+                heightSize - mMinOffset, heightMode);
+        final CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
+        final int childWidthMeasureSpec = ViewGroup.getChildMeasureSpec(parentWidthMeasureSpec,
+                lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
+        final int childHeightMeasureSpec = ViewGroup.getChildMeasureSpec(parentHeightMeasureSpec,
+                lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+        return true;
+    }
+
+    @Override
+    public boolean onLayoutChild(CoordinatorLayout parent, RecyclerView child, int layoutDirection) {
+        if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) {
+            child.setFitsSystemWindows(true);
+        }
+        int savedTop = child.getTop();
+        // First let the parent lay it out
+        parent.onLayoutChild(child, layoutDirection);
+        // Offset the bottom sheet
+        mParentHeight = parent.getHeight();
+        mMaxOffset = Math.max(mParentHeight - mPeekHeight, mMinOffset);
+        mExpandedHeight = mParentHeight / 2 - CommonUtil.dp2px(child.getContext(), 100);
+        if (mState == STATE_EXPANDED) {
+            ViewCompat.offsetTopAndBottom(child, mExpandedHeight);
+        } else if (mState == STATE_HIDDEN) {
+            ViewCompat.offsetTopAndBottom(child, mParentHeight);
+        } else if (mState == STATE_COLLAPSED) {
+            ViewCompat.offsetTopAndBottom(child, mMaxOffset);
+        } else if (mState == STATE_DRAGGING || mState == STATE_SETTLING) {
+            ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop());
+        }
+        if (mViewDragHelper == null) {
+            mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
+        }
+        if (mFlingHelper == null) {
+            mFlingHelper = new FlingHelper(parent.getContext());
+        }
+        mViewRef = new WeakReference<>(child);
+        return true;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(CoordinatorLayout parent, RecyclerView child, MotionEvent event) {
+        if (!child.isShown()) {
+            mIgnoreEvents = true;
+            return false;
+        }
+        int action = event.getActionMasked();
+        final int actionIndex = event.getActionIndex();
+        // Record the velocity
+        if (action == MotionEvent.ACTION_DOWN) {
+            reset();
+        }
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(event);
+        switch (action) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mTouchingScrollingChild = false;
+                mActivePointerId = MotionEvent.INVALID_POINTER_ID;
+                // Reset the ignore flag
+                if (mIgnoreEvents) {
+                    mIgnoreEvents = false;
+                    return false;
+                }
+                break;
+            case MotionEvent.ACTION_DOWN:
+                int initialX = (int) event.getX();
+                mInitialY = (int) event.getY();
+                if (parent.isPointInChildBounds(child, initialX, mInitialY)) {
+                    mActivePointerId = event.getPointerId(actionIndex);
+                    mTouchingScrollingChild = true;
+                }
+                mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID &&
+                        !parent.isPointInChildBounds(child, initialX, mInitialY);
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+                mActivePointerId = event.getPointerId(actionIndex);
+                mInitialY = (int) (event.getY(actionIndex) + 0.5f);
+                break;
+            default:
+                break;
+        }
+        if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) {
+            return true;
+        }
+        // child未到parent顶部或者child滑动顶部,进行拦截
+        return action == MotionEvent.ACTION_MOVE
+                && !mIgnoreEvents
+                && mState != STATE_DRAGGING
+                && Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()
+                && (child.getTop() > mMinOffset
+                || (mInitialY - event.getY() < 0 && !child.canScrollVertically(-1)));
+    }
+
+    @Override
+    public boolean onTouchEvent(CoordinatorLayout parent, RecyclerView child, MotionEvent event) {
+        if (!child.isShown()) {
+            return false;
+        }
+        int action = event.getActionMasked();
+        if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
+            return true;
+        }
+        if (mViewDragHelper != null) {
+            mViewDragHelper.processTouchEvent(event);
+        }
+        // Record the velocity
+        if (action == MotionEvent.ACTION_DOWN) {
+            reset();
+        }
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(event);
+        // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
+        // to capture the bottom sheet in case it is not captured and the touch slop is passed.
+        if (action == MotionEvent.ACTION_MOVE && !mIgnoreEvents) {
+            if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) {
+                mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
+            }
+        }
+        return !mIgnoreEvents;
+    }
+
+    public final void setState(final @State int state) {
+        if (state == mState) {
+            return;
+        }
+        if (mViewRef == null) {
+            // The view is not laid out yet; modify mState and let onLayoutChild handle it later
+            if (state == STATE_COLLAPSED || state == STATE_EXPANDED || state == STATE_HIDDEN) {
+                mState = state;
+            }
+            return;
+        }
+        final RecyclerView child = mViewRef.get();
+        if (child == null) {
+            return;
+        }
+        // Start the animation; wait until a pending layout if there is one.
+        ViewParent parent = child.getParent();
+        if (parent != null && parent.isLayoutRequested() && ViewCompat.isAttachedToWindow(child)) {
+            child.post(new Runnable() {
+                @Override
+                public void run() {
+                    startSettlingAnimation(child, state);
+                }
+            });
+        } else {
+            startSettlingAnimation(child, state);
+        }
+    }
+
+    @State
+    public final int getState() {
+        return mState;
+    }
+
+    private void setStateInternal(@State int state) {
+        if (mState == state) {
+            return;
+        }
+        mState = state;
+        RecyclerView bottomSheet = mViewRef.get();
+        if (bottomSheet != null && mCallback != null) {
+            mCallback.onStateChanged(bottomSheet, state);
+        }
+    }
+
+    private void reset() {
+        mActivePointerId = ViewDragHelper.INVALID_POINTER;
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    private void startSettlingAnimation(View child, int state) {
+        int top;
+        if (state == STATE_COLLAPSED) {
+            top = mMaxOffset;
+        } else if (state == STATE_EXPANDED) {
+            top = mExpandedHeight;
+        } else if (state == STATE_HIDDEN) {
+            top = mParentHeight;
+        } else {
+            throw new IllegalArgumentException("Illegal state argument: " + state);
+        }
+        if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
+            setStateInternal(STATE_SETTLING);
+            ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
+        } else {
+            setStateInternal(state);
+        }
+    }
+
+    private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
+
+        @Override
+        public boolean tryCaptureView(@NonNull View child, int pointerId) {
+            if (mState == STATE_DRAGGING) {
+                return false;
+            }
+            if (mTouchingScrollingChild) {
+                return false;
+            }
+            if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
+                if (child.canScrollVertically(-1)) {
+                    // Let the content scroll up
+                    return false;
+                }
+            }
+            return mViewRef != null && mViewRef.get() == child;
+        }
+
+        @Override
+        public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
+        }
+
+        @Override
+        public void onViewDragStateChanged(int state) {
+            if (state == ViewDragHelper.STATE_DRAGGING) {
+                setStateInternal(STATE_DRAGGING);
+            }
+        }
+
+        @Override
+        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
+            int top = releasedChild.getTop();
+            @State int targetState;
+            final int distance = (int) mFlingHelper.getSplineFlingDistance((int) yvel);
+            final int newTop;
+            if (yvel < 0) {
+                newTop = top - distance;
+                if (newTop >= mMaxOffset - THRESHOLD) {
+                    top = mMaxOffset;
+                    targetState = STATE_COLLAPSED;
+                } else if (newTop >= mExpandedHeight - THRESHOLD) {
+                    top = mExpandedHeight;
+                    targetState = STATE_EXPANDED;
+                } else {
+                    top = Math.max(mMinOffset, newTop);
+                    targetState = STATE_SETTLING;
+                    if (distance > top - mMinOffset) {
+                        int velocity = mFlingHelper.getVelocityByDistance(distance - top - mMinOffset);
+                        if (releasedChild instanceof RecyclerView) {
+                            ((RecyclerView) releasedChild).fling(0, velocity);
+                        }
+                    }
+                }
+            } else if (yvel == 0.f) {
+                if (Math.abs(top - mExpandedHeight) < Math.abs(top - mMaxOffset)) {
+                    top = mExpandedHeight;
+                    targetState = STATE_EXPANDED;
+                } else {
+                    top = mMaxOffset;
+                    targetState = STATE_COLLAPSED;
+                }
+            } else {
+                newTop = top + distance;
+                if (newTop <= mMinOffset + THRESHOLD) {
+                    top = newTop;
+                    targetState = STATE_SETTLING;
+                } else if (newTop >= mMaxOffset - THRESHOLD) {
+                    top = mMaxOffset;
+                    targetState = STATE_COLLAPSED;
+                } else {
+                    top = mExpandedHeight;
+                    targetState = STATE_EXPANDED;
+                }
+            }
+            if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
+                setStateInternal(STATE_SETTLING);
+                ViewCompat.postOnAnimation(releasedChild,
+                        new SettleRunnable(releasedChild, targetState));
+            } else {
+                setStateInternal(targetState);
+            }
+        }
+
+        @Override
+        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
+            return MathUtils.clamp(top, mMinOffset, mParentHeight);
+        }
+
+        @Override
+        public int clampViewPositionHorizontal(View child, int left, int dx) {
+            return child.getLeft();
+        }
+
+        @Override
+        public int getViewVerticalDragRange(@NonNull View child) {
+            return mParentHeight;
+        }
+    };
+
+    public void setBottomSheetCallback(BottomSheetCallback callback) {
+        mCallback = callback;
+    }
+
+    public final void setPeekHeight(int peekHeight) {
+        mPeekHeight = Math.max(0, peekHeight);
+        mMaxOffset = mParentHeight - mPeekHeight;
+        if (mState == STATE_COLLAPSED && mViewRef != null) {
+            RecyclerView view = mViewRef.get();
+            if (view != null) {
+                view.requestLayout();
+            }
+        }
+    }
+
+    public void setMinOffset(int minOffset) {
+        mMinOffset = minOffset;
+    }
+
+    private class SettleRunnable implements Runnable {
+
+        private final View mView;
+
+        @State
+        private final int mTargetState;
+
+        SettleRunnable(View view, @State int targetState) {
+            mView = view;
+            mTargetState = targetState;
+        }
+
+        @Override
+        public void run() {
+            if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
+                ViewCompat.postOnAnimation(mView, this);
+            } else {
+                setStateInternal(mTargetState);
+            }
+        }
+    }
+
+    protected static class SavedState extends AbsSavedState {
+        @State
+        final int state;
+
+        public SavedState(Parcel source) {
+            this(source, null);
+        }
+
+        SavedState(Parcel source, ClassLoader loader) {
+            super(source, loader);
+            //noinspection ResourceType
+            state = source.readInt();
+        }
+
+        SavedState(Parcelable superState, @State int state) {
+            super(superState);
+            this.state = state;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(state);
+        }
+
+        public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+            @Override
+            public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+                return new SavedState(in, loader);
+            }
+
+            @Override
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in, null);
+            }
+
+            @Override
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    public static RecyclerViewBehavior from(RecyclerView view) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        if (!(params instanceof CoordinatorLayout.LayoutParams)) {
+            throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
+        }
+        CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
+                .getBehavior();
+        if (!(behavior instanceof RecyclerViewBehavior)) {
+            throw new IllegalArgumentException(
+                    "The view is not associated with RecyclerViewBehavior");
+        }
+        return (RecyclerViewBehavior) behavior;
+    }
+
+}

+ 43 - 0
baselibrary/src/main/java/com/yc/baselibrary/cache/Cache.kt

@@ -0,0 +1,43 @@
+package com.yc.baselibrary.cache
+
+import android.os.Parcelable
+import com.tencent.mmkv.MMKV
+
+object Cache {
+    private val kv by lazy {
+        MMKV.defaultMMKV()
+    }
+
+    fun <T> put(key: String, value: T) {
+        when (value) {
+            is String -> kv?.encode(key, value)
+            is Boolean -> kv?.encode(key, value)
+            is Double -> kv?.encode(key, value)
+            is Float -> kv?.encode(key, value)
+            is Int -> kv?.encode(key, value)
+            is Long -> kv?.encode(key, value)
+            is Parcelable -> kv?.encode(key, value)
+            is ByteArray -> kv?.encode(key, value)
+        }
+    }
+
+    fun getString(key: String, default: String = ""): String = kv?.decodeString(key, default)?:""
+    fun getBoolean(key: String, default: Boolean = false) = kv?.decodeBool(key, default)
+    fun getDouble(key: String, default: Double = 0.0) = kv?.decodeDouble(key, default)
+    fun getFloat(key: String, default: Float = 0f) = kv?.decodeFloat(key, default)
+    fun getInt(key: String, default: Int = 0) = kv?.decodeInt(key, default)
+    fun getLong(key: String, default: Long = 0L) = kv?.decodeLong(key, default)
+    fun <T : Parcelable> getParcelable(key: String, classType: Class<T>) =
+        kv?.decodeParcelable(key, classType)
+    fun getSetString(key:String) = kv?.decodeStringSet(key)
+
+    fun getByteArray(key: String, default: ByteArray = byteArrayOf()) = kv?.decodeBytes(key, default)
+
+    fun removeValueForKey(key: String) = kv?.removeValueForKey(key)
+
+    fun removeValuesForKeys(vararg keys: String) = kv?.removeValuesForKeys(keys)
+
+    fun clear() {
+        kv?.clearAll()
+    }
+}

+ 23 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/IEmit.kt

@@ -0,0 +1,23 @@
+package com.yc.baselibrary.core
+
+/**
+ * IEmit
+ *
+ * @author kou_zhong
+ * @date 2020/11/3
+ */
+interface IEmit {
+    fun setUpEmit() {
+    }
+
+    fun onReceiveEmit(emit: Emit) {
+    }
+
+    /**
+     * 页面主请求可用此方法,其余请求都使用onReceiveEmit
+     */
+    fun onFetch(){
+    }
+}
+
+data class Emit(val type: Int, val value: Any?)

+ 21 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/ILiveEvent.kt

@@ -0,0 +1,21 @@
+package com.yc.baselibrary.core
+
+import com.yc.baselibrary.event.LiveBusEvent
+
+
+/**
+ * ILiveEvent
+ *
+ * @author wm
+ * @date 20-6-30
+ */
+interface ILiveEvent {
+    fun isSupportLiveBus(): Boolean = false
+    fun setUpLiveBus() {
+    }
+
+    fun onReceiveLiveEvent(liveBusEvent: LiveBusEvent) {
+    }
+    fun postLiveEvent(type: LiveBusEvent.LiveBusEventType, any: Any? = null){
+    }
+}

+ 47 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/ILoading.kt

@@ -0,0 +1,47 @@
+package com.yc.baselibrary.core
+
+import android.view.View
+import androidx.annotation.IntegerRes
+import com.kingja.loadsir.callback.Callback
+import com.kingja.loadsir.core.LoadSir
+import com.yc.loadinglibrary.loading.page.ILoadSirPage
+import com.yc.loadinglibrary.loading.page.LoadSirPageWrapper
+
+/**
+ * ILoading
+ *
+ * @author wm
+ * @date 19-8-26
+ */
+interface ILoading : ILoadingElement, IBindLoadSirPage {
+    fun isSupportLoading(): Boolean = true
+    fun setUpLoading()
+    fun getRegisterLoading(): Any? = null
+    fun switchLoading(value: Int)
+    fun onRequestReload(view: View) {
+    }
+
+    fun showLoading()
+    fun hideLoading()
+    fun showEmpty()
+    fun showError()
+    fun extraLoading(callback: LoadSir.Builder)
+}
+
+interface ILoadingElement {
+    fun emptyHint(): String = ""
+    fun emptyColor(): Int = -1
+    fun errorColor(): Int = -1
+    @IntegerRes
+    fun emptyImage(): Int = -1
+    fun errorHint(): String = ""
+}
+
+interface IBindLoadSirPage : ILoadSirPage {
+    val loadSirPage: ILoadSirPage
+    override var emptyPage: LoadSirPageWrapper<out Callback>
+    override var errorPage: LoadSirPageWrapper<out Callback>
+    override var progressPage: LoadSirPageWrapper<out Callback>
+    override var loadingShowPage: LoadSirPageWrapper<out Callback>
+    override var loadingHidePage: LoadSirPageWrapper<out Callback>
+}

+ 16 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/IRefresh.kt

@@ -0,0 +1,16 @@
+package com.yc.baselibrary.core
+
+import com.scwang.smart.refresh.layout.SmartRefreshLayout
+
+
+/**
+ * IRefresh
+ *
+ * @author wm
+ * @date 20-6-29
+ */
+interface IRefresh {
+    fun getRefreshLayout(): SmartRefreshLayout? = null
+    fun setUpRefreshLayout()
+    fun switchRefresh(refreshLayout: SmartRefreshLayout?,refreshType: Int)
+}

+ 16 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/ITimer.kt

@@ -0,0 +1,16 @@
+package com.yc.baselibrary.core
+
+/**
+ * ITimer
+ *
+ * @author wm
+ * @date 19-9-5
+ */
+interface ITimer {
+    fun isSupportTimer(): Boolean = false
+    fun startTimer()
+    fun cancelTimer()
+    fun timerDelay(): Long = 2L
+    fun timerInterval(): Long = 1L
+    fun onTimer()
+}

+ 21 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/IView.kt

@@ -0,0 +1,21 @@
+package com.yc.baselibrary.core
+
+import androidx.annotation.LayoutRes
+
+/**
+ * IView
+ *
+ * @author wm
+ * @date 19-8-13
+ */
+interface IView {
+    @LayoutRes
+    fun getLayoutId(): Int
+
+    fun initView()
+    fun ready() {
+    }
+
+    fun fitScreen() {
+    }
+}

+ 15 - 0
baselibrary/src/main/java/com/yc/baselibrary/core/IViewEvent.kt

@@ -0,0 +1,15 @@
+package com.yc.baselibrary.core
+
+import android.view.View
+
+/**
+ * View事件处理
+ *
+ * @author wm
+ * @date 19-8-27
+ */
+interface IViewEvent  {
+    fun addClickView(vararg views: View?)
+    fun observe() {
+    }
+}

+ 80 - 0
baselibrary/src/main/java/com/yc/baselibrary/coroutines/MainScopeDelegate.kt

@@ -0,0 +1,80 @@
+package com.yc.baselibrary.coroutines
+
+import android.util.Log
+import androidx.lifecycle.Lifecycle
+import com.yc.baselibrary.ext.lifecycleScope
+import com.yc.baselibrary.ext.toast
+import com.yc.baselibrary.net.exception.Errors
+import com.yc.baselibrary.net.model.RequestBuilder
+import com.yc.networklibrary.remote.RetrofitManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+
+interface MainScopeDelegate {
+
+    /**
+     * 接口请求作用域,不跟生命周期绑定,需要自己处理job.cancel()
+     *
+     * @param block
+     * @param build
+     */
+    fun <T> requestMix(
+        block: suspend CoroutineScope.() -> T,
+        build: RequestBuilder<T>.() -> Unit = {}
+    ): Job = MainScope().launch {
+        val builder = RequestBuilder<T>().also(build)
+        try {
+            val data = withContext(Dispatchers.IO) {
+                block()
+            }
+            builder.getSuccess()?.invoke(data)
+        } catch (e: Exception) {
+            if (e is Errors.ErrorException) {
+                val message = e.message
+                if (!message.isNullOrEmpty()) {
+                    Log.e("MainScopeDelegate", "requestMix: $message")
+                    toast(message)
+                }
+            }
+            builder.getError()?.invoke(e)
+        }
+    }
+
+    /**
+     * 接口请求作用域,跟fragment,activity生命周期绑定,不需要自己处理job.cancel()
+     *
+     * @param block
+     * @param build
+     */
+    fun <T> Lifecycle.requestMix(
+        block: suspend CoroutineScope.() -> T,
+        build: RequestBuilder<T>.() -> Unit = {}
+    ): Job = lifecycleScope.launch {
+        val builder = RequestBuilder<T>().also(build)
+        try {
+            val data = withContext(Dispatchers.IO) {
+                block()
+            }
+            builder.getSuccess()?.invoke(data)
+        } catch (e: Exception) {
+            if (e is Errors.ErrorException) {
+                val message = e.message
+                if (!message.isNullOrEmpty()) {
+                    Log.e("MainScopeDelegate", "requestMix: $message")
+                    toast(message)
+                }
+            }
+            builder.getError()?.invoke(e)
+        }
+    }
+
+}
+
+inline fun <reified T> lazyRetrofit() = lazy(LazyThreadSafetyMode.NONE) {
+    RetrofitManager().create(T::class.java)
+}

+ 22 - 0
baselibrary/src/main/java/com/yc/baselibrary/event/Channel.kt

@@ -0,0 +1,22 @@
+package com.yc.baselibrary.event
+
+/** Created by yc on 2021/2/24
+ **/
+
+const val USER_LOGIN_STATE_CHANGED = "user_login_state_changed"
+const val USER_COLLECT_UPDATED = "user_collect_updated"
+const val CONNECT_VPN = "connect_vpn"
+const val CONNECT_MIND_VPN = "connect_mind_vpn"
+const val CONNECT_VPN_STOP = "connect_vpn_stop"
+const val CONNECT_VPN_TEST = "connect_vpn_test"
+const val CONNECT_MODEL_UPDATE = "connect_model_update"
+const val CONNECT_CLIENT_STATUS = "connect_client_status"
+const val ADD_SERVICE_IP= "add_service_ip"
+const val EDIT_SERVICE_IP= "edit_service_ip"
+const val UPDATE_USER_INFO= "update_user_info"
+const val NET_WORK_STATUS= "net_work_status"
+const val CHANGE_USER= "change_user"
+const val UPDATE_USER= "update_user"
+const val QUIT_LOGIN= "quit_login"
+const val IS_OPEN_TEENAGERS= "is_open_teenagers"
+const val GOTO_ROOM= "goto_room"

+ 62 - 0
baselibrary/src/main/java/com/yc/baselibrary/event/LiveBus.kt

@@ -0,0 +1,62 @@
+package com.yc.baselibrary.event
+
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.Observer
+import com.jeremyliao.liveeventbus.LiveEventBus
+
+/** Created by yc on 2021/2/24
+ **/
+object LiveBus{
+
+    /**
+     * 发布LiveDataEventBus消息
+     */
+   public inline fun <reified T> post(channel: String, value: T){
+        LiveEventBus.get(channel, T::class.java).post(value)
+    }
+    /**
+     * 发布LiveDataEventBus消息
+     */
+   public inline fun <reified T> postAcrossProcess(channel: String, value: T){
+        LiveEventBus.get(channel, T::class.java).postAcrossProcess(value)
+    }
+
+    /**
+     * 订阅LiveDataEventBus消息
+     * @param channel 渠道
+     * @param owner 生命周期owner
+     * @param observer 观察者
+     */
+    inline fun <reified T> observe(channel: String, owner: LifecycleOwner,observer: Observer<T>){
+        LiveEventBus.get(channel, T::class.java).observe(owner,observer)
+    }
+
+    /**
+     * 应用进程生命周期内订阅LiveDataEventBus消息
+     * @param channel 渠道
+     * @param observer 观察者
+     */
+    inline fun <reified T> observeForever(channel: String, observer: Observer<T>){
+        LiveEventBus.get(channel, T::class.java).observeForever(observer)
+    }
+
+    /**
+     * 订阅粘性LiveDataEventBus消息
+     * @param channel 渠道
+     * @param owner 生命周期owner
+     * @param observer 观察者
+     */
+    inline fun <reified T> observeSticky(channel: String, owner: LifecycleOwner,observer: Observer<T>){
+        LiveEventBus.get(channel,T::class.java).observeSticky(owner,observer)
+    }
+
+    /**
+     * 应用进程生命周期内订阅粘性LiveDataEventBus消息
+     * @param channel 渠道
+     * @param observer 观察者
+     */
+    inline fun <reified T> observeStickyForever(channel: String,observer: Observer<T>){
+        LiveEventBus.get(channel,T::class.java).observeForever(observer)
+    }
+
+}

+ 102 - 0
baselibrary/src/main/java/com/yc/baselibrary/event/LiveBusEvent.kt

@@ -0,0 +1,102 @@
+package com.yc.baselibrary.event
+
+import com.jeremyliao.liveeventbus.core.LiveEvent
+
+/**
+ * LiveBusEvent
+ *
+ * @author wm
+ * @date 20-6-30
+ */
+class LiveBusEvent(val type: LiveBusEventType, val any: Any? = null) : LiveEvent {
+
+    enum class LiveBusEventType {
+
+        BACK_FOREGROUND,
+        ENTER_BACKGROUND,
+        RECEIVE_SOCKET_MSG,
+
+        SWITCH_CAMERA,
+        OPEN_BEAUTY,
+        STATUS_VOICE,
+
+        REFRESH_SYSTEM_NOTICE,
+        UPDATE_SYSTEM_NOTICE,
+        REFRESH_DYNAMIC_NOTICE,
+        PULL_NEW_MESSAGE,
+        UPDATE_CHAT_MESSAGE_NOTICE,
+        UPDATE_CHAT_ROOM_UNREAD,
+
+        CLOSE_ROOM,
+
+        FINISH_ROOM,
+
+        LOGIN_AGAIN,
+
+        SOCKET_CONNECT_SUCCESS,
+
+        UPDATE_ANCHOR_STATUS,
+
+        UPDATE_FAMILY_STATUS,
+        ANCHOR_REMOTE_DESTROY,
+        ANCHOR_REMOTE_RECOVERY,
+        INIT_VIDEO_VIDEO,
+        SHOW_USER_LIST_DIALOG,
+
+        DISCONNECTION_LEAVE_ROOM,
+        ROOM_CONNECT_SUCCESS,
+        CLOSE_LIAN_MAI,
+        KICK_OUT_ROOM,
+        BALANCE_NO,
+        DISCONNECT_DOWN_LIVE,
+        OPEN_HAND_IN_HAND,
+        CLOSE_HAND_IN_HAND,
+        SELECT_HAND_IN_HAND,
+
+        UPDATE_LIVE_ADDRESS_SUCCESS,
+        SHOW_PWD_DIALOG,
+        USER_IS_CLOSE_VOICE,
+        LOAD_MORE_USER,
+        REFRESH_READ_COUNT,
+        UPDATE_USER_MARK,
+        CLOSE_ROOM_ACTIVITY,
+        DISMISS_FLOAT_VIEW,
+        UPDATE_UNREAD_MESSAGE,
+        NOTIFY_MY_MESSAGE_READ,
+        NOTIFY_USER_FROM_TYPE,
+        PWD_ENTER_ROOM,
+        CANCEL_PWD_ENTER_ROOM,
+        CREATE_ROOM_SUCCESS,
+        USER_IS_CLOSE_ROOM_VOICE,
+        SHOW_USER_CARD,
+        SHOW_USER_CAR,
+        SHOW_USER_ENTER,
+        SHOW_USER_FRAME,
+        SHOW_USER_CHAT_BG,
+        SHOW_ROOM_FLOAT,
+        GO_TO_HOME,
+        GO_TO_GAME,
+        ENTER_PREVIEW_VIDEO_SHOW,
+        END_PREVIEW_VIDEO_SHOW,
+        PRIVATE_CHAT_ROOM_ERROR_FOLLOW,
+        CODE_PRIVATE_CHAT_LOW_LEVEL,
+        SET_PWD_SUCCESS,
+        BIND_PHONE_SUCCESS,
+        UN_BIND_PHONE_SUCCESS,
+        SWITCH_ROOM,
+        IS_OPPONENT_MIC_OPEN,
+
+        ROOM_NO_RED_PAK,
+        LUCKY_BAG_PRIZE,
+        SHOW_OPEN_GUARD,
+        ANCHOR_FOLLOW, //主播关注
+
+        /****** 连线 *******/
+        // 关闭连线
+        CLOSE_LIVE_LINK,
+        /****** 连线 *******/
+    }
+
+}
+
+fun LiveBusEvent.checkType(receiveType: LiveBusEvent.LiveBusEventType) = type == receiveType

+ 36 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/AppBarExt.kt

@@ -0,0 +1,36 @@
+package com.yc.baselibrary.ext
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import com.google.android.material.appbar.AppBarLayout
+
+/**
+ * AppBarExt
+ *
+ * @author wm
+ * @date 20-8-27
+ */
+inline fun AppBarLayout.offset(
+    lifecycle: Lifecycle,
+    crossinline offset: (verticalOffset: Int) -> Unit
+) {
+    val appBar = this
+    val onOffsetChangedListener = AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
+        offset.invoke(verticalOffset)
+    }
+    appBar.addOnOffsetChangedListener(onOffsetChangedListener)
+    lifecycle.addObserver(object :
+        LifecycleEventObserver {
+        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+            when (event) {
+                Lifecycle.Event.ON_DESTROY -> {
+                    removeOnOffsetChangedListener(onOffsetChangedListener)
+                }
+                else -> {
+
+                }
+            }
+        }
+    })
+}

+ 13 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/DialogExt.kt

@@ -0,0 +1,13 @@
+package com.yc.baselibrary.ext
+
+import android.app.Dialog
+import android.content.Context
+import com.yc.baselibrary.view.dialog.TopDialog
+
+/** Created by yc on 11/19/23
+ **/
+fun Context.showTopPopup(title: String, message: String): TopDialog {
+    val dialog = TopDialog(this,title, message)
+    dialog.show()
+    return dialog
+}

+ 20 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/Extension.kt

@@ -0,0 +1,20 @@
+package com.yc.baselibrary.ext
+
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+
+fun <T> threadUnsafeLazy(initializer: () -> T) = lazy(LazyThreadSafetyMode.NONE, initializer)
+
+fun tryCatch(hintContent: String? = null, error: (() -> Unit)? = null, block: () -> Unit) {
+    try {
+        block.invoke()
+    } catch (e: Exception) {
+        hintContent?.let { toast("异常捕获:----------$it") }
+        e.printStackTrace()
+    }
+}
+
+fun <T> Observable<T>.exThread() = this
+    .subscribeOn(Schedulers.io())
+    .observeOn(AndroidSchedulers.mainThread())

+ 16 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/HandlerExt.kt

@@ -0,0 +1,16 @@
+package com.yc.baselibrary.ext
+
+import android.os.Handler
+import android.os.Looper
+
+fun postDelay(time: Long, method: () -> Unit) {
+    Handler(Looper.getMainLooper()).postDelayed({
+        method.invoke()
+    }, time)
+}
+
+fun post(method: () -> Unit) {
+    Handler(Looper.getMainLooper()).post {
+        method.invoke()
+    }
+}

+ 428 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/ImgExt.kt

@@ -0,0 +1,428 @@
+package com.yc.baselibrary.ext
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LevelListDrawable
+import android.text.Html
+import android.text.TextUtils
+import android.util.TypedValue
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.MultiTransformation
+import com.bumptech.glide.load.Transformation
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
+import com.bumptech.glide.load.resource.bitmap.CenterInside
+import com.bumptech.glide.load.resource.bitmap.FitCenter
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.target.SimpleTarget
+import com.bumptech.glide.request.transition.DrawableCrossFadeFactory
+import com.bumptech.glide.request.transition.Transition
+import com.xueyu.kotlinextlibrary.color
+import com.xueyu.kotlinextlibrary.dp
+import com.yc.baselibrary.R
+import com.yc.baselibrary.utils.ComponentUtil
+import com.yc.baselibrary.widget.image.BlurTransformation
+import com.yc.baselibrary.widget.image.CircleTransformation
+import com.yc.baselibrary.widget.image.FitXYTransformation
+import com.yc.baselibrary.widget.image.ImagePath
+import com.yc.baselibrary.widget.image.RoundedCornersTransformation
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+
+/**
+ * ImgExt
+ *
+ * @author wm
+ * @date 20-6-30
+ */
+
+const val placeholder = 0x110
+const val placeholder2 = 0x111
+const val placeholder3 = 0x112
+const val placeholder4 = 0x113
+
+@SuppressLint("CheckResult")
+fun ImageView.loadImage(
+    url: Any? = null,
+    placeholder: Drawable? = null,
+    error: Drawable? = null,
+    circle: Boolean = false,
+    lifecycle: Any? = null,
+    noAnimation: Boolean = false,
+    corner: Int = 0,
+    scaleType: ImageView.ScaleType? = null,
+    cornerType: RoundedCornersTransformation.CornerType? = null,
+    width: Int = 0,
+    height: Int = 0,
+    placeholderOrErrorColor: Int = 0,
+    strokeWidth: Float = 0f,
+    strokeColor: Int = 0,
+    blur: Boolean = false,
+    quality: Int = ImagePath.DEFAULT_QUALITY,
+    blurRadius: Int = 10,
+    formatType: Int = 0,
+    noPlaceholder: Boolean = false,
+    requestListener: RequestListener<Drawable>? = null
+) {
+    val view = this
+    var realUrl = url
+    (url as? String)?.let {
+        if ((width > 0 || height > 0) && isHttpUrl(url) && isQiniuUrl(url)) {
+            realUrl = ImagePath.buildPath(url)
+                .quality(quality)
+                .width(width)
+                .height(height)
+                .formatType(formatType)
+                .cropPath()
+        }
+    }
+    val realCorner = dp2px(view.context, corner).toFloat()
+    val realStrokeColor = if (strokeColor == 0) Color.parseColor("#e6e6e6") else strokeColor
+    val realStroke = dp2px(view.context, strokeWidth)
+    val requestManager = createRequestManager(lifecycle, view)
+    val multiTransformation = createTransform(
+        circle, blur, scaleType?.toTransformation() ?: CenterCrop(), realStroke,
+        realStrokeColor, blurRadius, realCorner, cornerType
+    )
+    requestManager.load(realUrl).apply {
+        if (!noPlaceholder) {
+            thumbnail(
+                if (placeholder != null) {
+                    requestManager.load(placeholder).transform(multiTransformation)
+                } else {
+                    requestManager.load(R.drawable.module_core_wrap_default_placeholder_or_error)
+                        .transform(multiTransformation)
+                }
+            )
+        }
+        transform(multiTransformation)
+        if (!noAnimation) {
+            val factory = DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build()
+            transition(DrawableTransitionOptions.withCrossFade(factory))
+        }
+        requestListener?.let {
+            listener(it)
+        }
+        into(view)
+    }
+}
+
+fun ImageView.loadAvatar(
+    url: Any? = null,
+    width: Int = layoutParams.width,
+    height: Int = layoutParams.width,
+    strokeWidth: Float = 0.5f,
+    strokeColor: Int = color(R.color.colorLine)
+) {
+    loadImage(
+        url, width = width, height = height, circle = true,
+        strokeColor = strokeColor, strokeWidth = strokeWidth
+    )
+}
+
+private fun isHttpUrl(url: String? = null): Boolean {
+    return !TextUtils.isEmpty(url) && (url?.startsWith("http://") ?: false || (url?.startsWith("https://")
+        ?: false))
+}
+
+private fun isQiniuUrl(url: String? = null): Boolean {
+    return !TextUtils.isEmpty(url) && (url?.startsWith("https://resources.syjinshanshan.com/") ?: false || (url?.startsWith(
+        "https://resources.syjinshanshan.com/"
+    )
+        ?: false))
+}
+
+private fun createRequestManager(lifecycle: Any?, view: ImageView) =
+    when (lifecycle) {
+        is FragmentActivity -> {
+            Glide.with(lifecycle)
+        }
+
+        is Fragment -> {
+            Glide.with(lifecycle)
+        }
+
+        else -> {
+            Glide.with(view.context)
+        }
+    }
+
+private fun createTransform(
+    circle: Boolean,
+    blur: Boolean,
+    scaleTransform: Transformation<Bitmap>,
+    realStroke: Float,
+    realStrokeColor: Int,
+    blurRadius: Int,
+    realCorner: Float,
+    cornerType: RoundedCornersTransformation.CornerType?
+) = if (circle) {
+    when (blur) {
+        true -> MultiTransformation(
+            scaleTransform, CircleTransformation(
+                realStroke,
+                realStrokeColor
+            ), BlurTransformation(blurRadius, 3)
+        )
+
+        else -> {
+            MultiTransformation(
+                scaleTransform, CircleTransformation(
+                    realStroke,
+                    realStrokeColor
+                )
+            )
+        }
+    }
+} else {
+    when (blur) {
+        true -> MultiTransformation(
+            scaleTransform, RoundedCornersTransformation(
+                realCorner.toInt(),
+                realStroke,
+                cornerType ?: RoundedCornersTransformation.CornerType.ALL,
+                realStrokeColor
+            ), BlurTransformation(blurRadius, 3)
+        )
+
+        else -> {
+            MultiTransformation(
+                scaleTransform, RoundedCornersTransformation(
+                    realCorner.toInt(),
+                    realStroke,
+                    cornerType ?: RoundedCornersTransformation.CornerType.ALL,
+                    realStrokeColor
+                )
+            )
+        }
+    }
+}
+
+private fun dp2px(context: Context, dp: Int): Int {
+    return TypedValue.applyDimension(
+        TypedValue.COMPLEX_UNIT_DIP,
+        dp.toFloat(),
+        context.resources.displayMetrics
+    ).toInt()
+}
+
+private fun dp2px(context: Context, dp: Float): Float {
+    return TypedValue.applyDimension(
+        TypedValue.COMPLEX_UNIT_DIP,
+        dp,
+        context.resources.displayMetrics
+    )
+}
+
+private fun ImageView.ScaleType.toTransformation(): Transformation<Bitmap>? {
+    return when (this) {
+        ImageView.ScaleType.CENTER_INSIDE -> {
+            CenterInside()
+        }
+
+        ImageView.ScaleType.FIT_CENTER -> {
+            FitCenter()
+        }
+
+        ImageView.ScaleType.FIT_XY -> {
+            FitXYTransformation()
+        }
+
+        ImageView.ScaleType.CENTER_CROP -> {
+            CenterCrop()
+        }
+
+        else -> {
+            CenterCrop()
+        }
+    }
+}
+
+fun Bitmap.compress(
+    format: Bitmap.CompressFormat,
+    quality: Int = 80
+): Bitmap {
+    val baos = ByteArrayOutputStream()
+    compress(format, quality, baos)
+    return BitmapFactory.decodeStream(ByteArrayInputStream(baos.toByteArray()), null, null)!!
+}
+
+
+fun int2Hex2(colorInt: Int): String? {
+    var hexCode: String? = ""
+    val rgb: IntArray = int2Rgb(colorInt)
+    hexCode = rgb2Hex(rgb)
+    return hexCode
+}
+
+fun int2Rgb(colorInt: Int): IntArray {
+    val rgb = intArrayOf(0, 0, 0)
+    val red = Color.red(colorInt)
+    val green = Color.green(colorInt)
+    val blue = Color.blue(colorInt)
+    rgb[0] = red
+    rgb[1] = green
+    rgb[2] = blue
+    return rgb
+}
+
+fun rgb2Hex(rgb: IntArray): String? {
+    var hexCode = "#"
+    for (i in rgb.indices) {
+        var rgbItem = rgb[i]
+        if (rgbItem < 0) {
+            rgbItem = 0
+        } else if (rgbItem > 255) {
+            rgbItem = 255
+        }
+        val code =
+            arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F")
+        val lCode = rgbItem / 16 //先获取商,例如,255 / 16 == 15
+        val rCode = rgbItem % 16 //再获取余数,例如,255 % 16 == 15
+        hexCode += code[lCode] + code[rCode] //FF
+    }
+    return hexCode
+}
+
+fun msgImageGetter(context: Context, textView: TextView? = null) = Html.ImageGetter { source ->
+    val context: Context = context
+    if (TextUtils.isEmpty(source)) return@ImageGetter null
+    if (!ComponentUtil.isNumericZidai(source)) {
+        val drawable = LevelListDrawable()
+        val lengthSide: Int = 14.dp
+        Glide.with(context)
+            .asBitmap()
+            .load(source)
+            .into(object : SimpleTarget<Bitmap?>() {
+                override fun onResourceReady(
+                    resource: Bitmap,
+                    transition: Transition<in Bitmap?>?
+                ) {
+                    val bitmapDrawable = BitmapDrawable(resource)
+                    drawable.addLevel(1, 1, bitmapDrawable);
+                    drawable.setBounds(0, 0, lengthSide, lengthSide)
+                    drawable.level = 1
+                    textView?.invalidate()
+                    textView?.text = textView?.text
+                }
+            })
+        drawable
+
+    } else {
+        var drawable: Drawable? = null
+        val id = source.toInt()
+        when (id) {
+            placeholder -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 42.dp, 15.dp)
+            }
+
+            placeholder2 -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 66.dp, 15.dp)
+            }
+
+            placeholder3 -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 74.dp, 15.dp)
+            }
+
+            placeholder4 -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 45.dp, 15.dp)
+            }
+
+            else -> {
+                try {
+                    if (id == 0 || context.resources.getDrawable(id).also { drawable = it } == null)
+                        return@ImageGetter null
+                    drawable!!.setBounds(
+                        0, 0, (drawable!!.intrinsicWidth * 0.6).toInt(),
+                        (drawable!!.intrinsicHeight * 0.6).toInt()
+                    )
+                } catch (e: OutOfMemoryError) {
+                    System.gc()
+                }
+            }
+        }
+        drawable!!
+    }
+}
+
+fun liveChat(context: Context, textView: TextView? = null) = Html.ImageGetter { source ->
+    val context: Context = context
+    if (TextUtils.isEmpty(source)) return@ImageGetter null
+    if (!ComponentUtil.isNumericZidai(source)) {
+        val drawable = LevelListDrawable()
+        val lengthSide: Int = 14.dp
+        Glide.with(context)
+            .asBitmap()
+            .load(source)
+            .into(object : SimpleTarget<Bitmap?>() {
+                override fun onResourceReady(
+                    resource: Bitmap,
+                    transition: Transition<in Bitmap?>?
+                ) {
+                    val bitmapDrawable = BitmapDrawable(resource)
+                    drawable.addLevel(1, 1, bitmapDrawable);
+                    drawable.setBounds(0, 0, lengthSide, lengthSide)
+                    drawable.level = 1
+                    textView?.invalidate()
+                    textView?.text = textView?.text
+                }
+            })
+        drawable
+
+    } else {
+        var drawable: Drawable? = null
+        val id = source.toInt()
+        when (id) {
+            placeholder -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 42.dp, 15.dp)
+            }
+
+            placeholder2 -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 66.dp, 15.dp)
+            }
+
+            placeholder3 -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 74.dp, 15.dp)
+            }
+
+            placeholder4 -> {
+                drawable = ColorDrawable(Color.TRANSPARENT)
+                drawable.setBounds(0, 0, 45.dp, 15.dp)
+            }
+
+            else -> {
+                try {
+                    if (id == 0 || context.resources.getDrawable(id).also { drawable = it } == null)
+                        return@ImageGetter null
+                    val width = drawable!!.intrinsicWidth
+                    val height = drawable!!.intrinsicHeight
+                    val scale = width / height
+
+                    drawable!!.setBounds(
+                        0, 0, 16.dp * scale, 16.dp
+                    )
+                } catch (e: OutOfMemoryError) {
+                    System.gc()
+                }
+            }
+        }
+        drawable!!
+    }
+}

+ 80 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/JsonExt.kt

@@ -0,0 +1,80 @@
+package com.yc.baselibrary.ext
+
+import com.google.gson.JsonElement
+import com.google.gson.reflect.TypeToken
+import com.yc.baselibrary.utils.GsonUtil
+
+/**
+ * JsonExt
+ *
+ * @author hua_rong
+ * @date 20-8-21
+ */
+
+inline fun <reified T> String?.parseObj(): T? {
+    return try {
+       GsonUtil.getGsonInstance().fromJson(this, T::class.java)
+    } catch (e: Exception) {
+        e.printStackTrace()
+        null
+    }
+}
+fun checkData(str: String?): String {
+    val str1: String = str?.replace("\"{", "{") ?: ""
+    return str1.replace("}\"", "}")
+}
+
+inline fun <reified T> JsonElement?.parseObj(): T? {
+    return try {
+        GsonUtil.getGsonInstance().fromJson(this, T::class.java)
+    } catch (e: Exception) {
+        e.printStackTrace()
+        null
+    }
+}
+
+inline fun <reified T> String?.parseList(): List<T>? {
+    return try {
+        GsonUtil.getGsonInstance().fromJson<List<T>>(this, object : TypeToken<List<T?>?>() {}.type)
+    } catch (e: Exception) {
+        e.printStackTrace()
+        null
+    }
+}
+
+fun Any?.toJson(): String {
+    return GsonUtil.getGsonInstance().toJson(this)
+}
+
+/**
+ * toJsonTree
+ */
+fun Any?.toJsonTree(): JsonElement {
+    return GsonUtil.getGsonInstance().toJsonTree(this)
+}
+
+/**
+ * isJsonEmpty
+ */
+fun JsonElement?.isJsonEmpty(): Boolean {
+    try {
+        if (this == null) {
+            return true
+        }
+        if (isJsonNull) {
+            return true
+        }
+        if (isJsonPrimitive) {
+            return asString.isNullOrEmpty()
+        }
+        if (isJsonObject) {
+            return asJsonObject.size() == 0
+        }
+        if (isJsonArray) {
+            return asJsonArray.size() == 0
+        }
+    } catch (e: Exception) {
+        e.printStackTrace()
+    }
+    return false
+}

+ 23 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/LifecycleExt.kt

@@ -0,0 +1,23 @@
+package com.yc.baselibrary.ext
+
+import androidx.lifecycle.*
+
+/**
+ * Created by chen_bin on 3/2/21.
+ */
+inline fun Lifecycle?.addObserver(
+    crossinline onChanged: (source: LifecycleOwner, event: Lifecycle.Event) -> Unit
+) {
+    this?.addObserver(object : LifecycleEventObserver {
+        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+            onChanged.invoke(source, event)
+        }
+    })
+}
+
+val Lifecycle.lifecycleScope
+    get() = coroutineScope
+
+fun <T> LifecycleOwner.observeData(liveData: LiveData<T>, observer: (t: T) -> Unit) {
+    liveData.observe(this, Observer { it?.let { t -> observer(t) } })
+}

+ 6 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/LottieExt.kt

@@ -0,0 +1,6 @@
+package com.yc.baselibrary.ext
+
+/** Created by yc on 2021/9/25
+ **/
+class LottieExt {
+}

+ 67 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/OnClickExt.kt

@@ -0,0 +1,67 @@
+package com.yc.baselibrary.ext
+
+import android.view.MotionEvent
+import android.view.View
+
+/** Created by yc on 2021/2/20
+ **/
+inline fun View.setSafeListener(crossinline action: () -> Unit) {
+    var lastClick = 0L
+    setOnClickListener {
+        val gap = System.currentTimeMillis() - lastClick
+        lastClick = System.currentTimeMillis()
+        if (gap < 500) return@setOnClickListener
+        action.invoke()
+    }
+}
+
+inline fun View.set1sSafeListener(crossinline action: () -> Unit) {
+    var lastClick = 0L
+    setOnClickListener {
+        val gap = System.currentTimeMillis() - lastClick
+        lastClick = System.currentTimeMillis()
+        if (gap < 1000) return@setOnClickListener
+        action.invoke()
+    }
+}
+inline fun View.set200SafeListener(crossinline action: () -> Unit) {
+    var lastClick = 0L
+    setOnClickListener {
+        val gap = System.currentTimeMillis() - lastClick
+        if (gap < 3000){
+
+        }else{
+            lastClick = System.currentTimeMillis()
+            action.invoke()
+        }
+
+    }
+}
+inline fun View.setLoginListener(crossinline action: () -> Unit) {
+    var lastClick = 0L
+    setOnClickListener {
+        val gap = System.currentTimeMillis() - lastClick
+        lastClick = System.currentTimeMillis()
+        if (gap < 1500) return@setOnClickListener
+        action.invoke()
+    }
+}
+
+class OnTouchListenerInterval(val block: (View) -> Unit) : View.OnTouchListener {
+    private var lastTime = 0L
+    override fun onTouch(v: View, event: MotionEvent): Boolean {
+        if (event.action == MotionEvent.ACTION_UP) {
+            if (System.currentTimeMillis() - lastTime < 500)
+                return true
+            lastTime = System.currentTimeMillis()
+            block.invoke(v)
+        }
+        return true
+    }
+}
+/**
+ * 自定义点击事件
+ * 500ms内只相应一次点击
+ */
+fun View.setMyOnClickListenerInterval(block: (View) -> Unit) =
+    setOnTouchListener(OnTouchListenerInterval(block))

+ 236 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/RecyclerViewExt.kt

@@ -0,0 +1,236 @@
+package com.yc.baselibrary.ext
+
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.StaggeredGridLayoutManager
+import com.xueyu.kotlinextlibrary.dp
+import java.lang.Exception
+import kotlin.math.roundToInt
+
+/**
+ * RecyclerViewExt
+ *
+ * @author wm
+ * @date 20-8-13
+ */
+inline fun RecyclerView.itemDecoration(
+        crossinline decoration: (outRect: Rect, view: View, adapterPosition: Int, itemViewType: Int) -> Unit
+) {
+    val recyclerView = this
+    recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
+        override fun getItemOffsets(
+                outRect: Rect,
+                view: View,
+                parent: RecyclerView,
+                state: RecyclerView.State
+        ) {
+            val adapterPosition = parent.getChildAdapterPosition(view)
+            val itemViewType = parent.adapter?.getItemViewType(adapterPosition) ?: 0
+            decoration.invoke(outRect, view, adapterPosition, itemViewType)
+        }
+    })
+}
+
+inline fun createItemDecoration(
+        crossinline decoration: (outRect: Rect, view: View, adapterPosition: Int, itemViewType: Int) -> Unit
+): RecyclerView.ItemDecoration =
+        object : RecyclerView.ItemDecoration() {
+            override fun getItemOffsets(
+                    outRect: Rect,
+                    view: View,
+                    parent: RecyclerView,
+                    state: RecyclerView.State
+            ) {
+                val holder = parent.getChildViewHolder(view) ?: return
+                decoration.invoke(outRect, view, holder.adapterPosition, holder.itemViewType)
+            }
+        }
+
+inline fun createDividerDecoration(
+        divider: Drawable? = null,
+        color: Int = Color.TRANSPARENT,
+        leftOffset: Int = 0,
+        rightOffset: Int = 0,
+        crossinline showDivider: (pos: Int) -> Boolean = { it > 0 }
+): RecyclerView.ItemDecoration =
+        object : RecyclerView.ItemDecoration() {
+            private val d = divider ?: GradientDrawable().apply {
+                setColor(color)
+                setSize(0, 0.5f.dp.toInt())
+            }
+            private val bounds = Rect()
+            override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+                canvas.save()
+
+                val left: Int
+                val right: Int
+                if (parent.clipToPadding) {
+                    left = parent.paddingLeft + leftOffset
+                    right = parent.width - parent.paddingRight - rightOffset
+                    canvas.clipRect(left, parent.paddingTop, right, parent.height - parent.paddingBottom)
+                } else {
+                    left = leftOffset
+                    right = parent.width - rightOffset
+                }
+
+                val childCount = parent.childCount
+                for (i in 0 until childCount) {
+                    if (!showDivider(i)) continue
+                    val child = parent.getChildAt(i)
+                    parent.getDecoratedBoundsWithMargins(child, bounds)
+                    val top = bounds.top + child.translationY.roundToInt()
+                    val bottom = top + d.intrinsicHeight
+                    d.setBounds(left, top, right, bottom)
+                    d.draw(canvas)
+                }
+                canvas.restore()
+            }
+        }
+
+inline fun RecyclerView.scrolled(
+        lifecycle: Lifecycle,
+        crossinline scrolled: (recyclerView: RecyclerView, dx: Int, dy: Int) -> Unit
+) {
+    val recyclerView = this
+    val listener = object : RecyclerView.OnScrollListener() {
+        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+            scrolled.invoke(recyclerView, dx, dy)
+        }
+    }
+    recyclerView.addOnScrollListener(listener)
+    lifecycle.addObserver(object :
+            LifecycleEventObserver {
+        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+            when (event) {
+                Lifecycle.Event.ON_DESTROY -> {
+                    removeOnScrollListener(listener)
+                }
+                else -> {
+
+                }
+            }
+        }
+    })
+}
+
+inline fun RecyclerView.scrollStateChanged(
+        lifecycle: Lifecycle,
+        crossinline scrollStateChanged: (recyclerView: RecyclerView, newState: Int) -> Unit
+) {
+    val recyclerView = this
+    val listener = object : RecyclerView.OnScrollListener() {
+        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+            scrollStateChanged.invoke(recyclerView, newState)
+        }
+    }
+    recyclerView.addOnScrollListener(listener)
+    lifecycle.addObserver(object :
+            LifecycleEventObserver {
+        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
+            when (event) {
+                Lifecycle.Event.ON_DESTROY -> {
+                    removeOnScrollListener(listener)
+                }
+                else -> {
+
+                }
+            }
+        }
+    })
+}
+
+fun createRecyclerViewScrollListener(
+        scrollStateChanged: ((recyclerView: RecyclerView, newState: Int) -> Unit)? = null,
+        scrolled: ((recyclerView: RecyclerView, dx: Int, dy: Int) -> Unit)? = null
+) = object : RecyclerView.OnScrollListener() {
+    override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+        scrollStateChanged?.invoke(recyclerView, newState)
+    }
+
+    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+        scrolled?.invoke(recyclerView, dx, dy)
+    }
+}
+
+fun RecyclerView.bind(
+        adapter: RecyclerView.Adapter<*>,
+        layoutManager: RecyclerView.LayoutManager = LinearLayoutManager(context),
+        hasFixedSize: Boolean = true,
+        clipChildren: Boolean? = null,
+        isNestedScrollingEnabled: Boolean? = null,
+        itemDecoration: RecyclerView.ItemDecoration? = null,
+        descendantFocusability: Int? = null,
+        scrollTopBtn: View? = null,
+        filterPos: Int = 15,
+        onScrollTop: (() -> Unit)? = null,
+        lifecycle: Lifecycle? = null,
+        onScrolled: ((dx: Int, dy: Int) -> Unit)? = null
+) {
+    val recyclerView = this
+    recyclerView.layoutManager = layoutManager
+    recyclerView.itemAnimator = null
+    recyclerView.setHasFixedSize(hasFixedSize)
+    isNestedScrollingEnabled?.let {
+        recyclerView.isNestedScrollingEnabled = it
+    }
+    clipChildren?.let {
+        recyclerView.clipChildren = it
+    }
+    itemDecoration?.let {
+        recyclerView.addItemDecoration(it)
+    }
+    descendantFocusability?.let {
+        recyclerView.descendantFocusability = it
+    }
+    recyclerView.adapter = adapter
+//    scrollTopBtn?.let {
+//        val scrollTopHelper = ScrollTopHelper(context, scrollTopBtn, filterPos)
+//        scrollTopHelper.attachToRecyclerView(this)
+//        scrollTopHelper.setScrollTopListener {
+//            onScrollTop?.invoke()
+//        }
+//    }
+    if (lifecycle != null && onScrolled != null) {
+        recyclerView.scrolled(lifecycle) { _, dx, dy ->
+            onScrolled.invoke(dx, dy)
+        }
+    }
+}
+
+fun RecyclerView.scrollToPositionWithOffset(position: Int, offset: Int) {
+    when (val lm = layoutManager) {
+        is LinearLayoutManager -> lm.scrollToPositionWithOffset(position, offset)
+        is StaggeredGridLayoutManager -> lm.scrollToPositionWithOffset(position, offset)
+    }
+}
+
+/**
+ * findFirstVisibleItemPosition
+ */
+fun RecyclerView.findFirstVisibleItemPosition() =
+        when (val lm = layoutManager) {
+            is LinearLayoutManager -> lm.findFirstVisibleItemPosition()
+            is GridLayoutManager -> lm.findFirstVisibleItemPosition()
+            is StaggeredGridLayoutManager -> {
+                try {
+                    val firstPosition = IntArray(lm.spanCount)
+                    lm.findFirstVisibleItemPositions(firstPosition)[0]
+                } catch (e: Exception) {
+                    -1
+                }
+            }
+            else -> -1
+        }
+
+
+

+ 88 - 0
baselibrary/src/main/java/com/yc/baselibrary/ext/ShapeDrawableExt.kt

@@ -0,0 +1,88 @@
+package com.yc.baselibrary.ext
+
+
+import android.annotation.SuppressLint
+import android.content.res.ColorStateList
+import android.graphics.Paint
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.ViewCompat
+import com.google.android.material.shape.*
+import com.xueyu.kotlinextlibrary.color
+import com.xueyu.kotlinextlibrary.dp
+import com.xueyu.kotlinextlibrary.parseColor
+import com.yc.baselibrary.R
+
+/**
+ * 给控件画阴影
+ *
+ * @param radius
+ * @param tint
+ * @param shadowColor
+ * @param strokeWidth
+ * @param strokeColor
+ * @param elevation
+ * @param shadowVerticalOffset
+ */
+@SuppressLint("RestrictedApi")
+fun View.drawShadow(
+    radius: Float = 8f.dp,
+    tint: Int = color(R.color.colorWhite),
+    strokeColor: ColorStateList? = null,
+    strokeWidth: Float = 0f,
+    shadowColor: Int = parseColor("#dddddd"),
+    elevation: Float = 16f.dp,
+    shadowVerticalOffset: Int = 2.dp
+) {
+    val builder = ShapeAppearanceModel.builder().apply {
+        setAllCorners(RoundedCornerTreatment())
+        setAllCornerSizes(radius)
+    }
+    val drawable = MaterialShapeDrawable(builder.build()).apply {
+        setTint(tint)
+        paintStyle = Paint.Style.FILL_AND_STROKE
+        this.strokeColor = strokeColor
+        this.strokeWidth = strokeWidth
+        shadowCompatibilityMode = MaterialShapeDrawable.SHADOW_COMPAT_MODE_ALWAYS
+        this.elevation = elevation
+        setShadowColor(shadowColor)
+        this.shadowVerticalOffset = shadowVerticalOffset
+    }
+    (parent as? ViewGroup)?.clipChildren = false
+    ViewCompat.setBackground(this, drawable)
+}
+
+/**
+ * ToolTips控件,给控件画三角跟带圆角的背景
+ *
+ * @param tint
+ * @param radius
+ * @param gravity
+ * @param triangleSize
+ * @param triangleOffset
+ */
+fun View.drawTriangleEdge(
+    tint: Int = color(R.color.colorBlack2),
+    radius: Float = 8f.dp,
+    gravity: Int = Gravity.TOP,
+    triangleSize: Float = 8f.dp,
+    triangleOffset: Float = 0f
+) {
+    val builder = ShapeAppearanceModel.builder().apply {
+        setAllCornerSizes(radius)
+        val edge = OffsetEdgeTreatment(TriangleEdgeTreatment(triangleSize, false), triangleOffset)
+        when (gravity) {
+            Gravity.TOP -> setTopEdge(edge)
+            Gravity.BOTTOM -> setBottomEdge(edge)
+            Gravity.START -> setLeftEdge(edge)
+            Gravity.END -> setLeftEdge(edge)
+        }
+    }
+    val drawable = MaterialShapeDrawable(builder.build()).apply {
+        paintStyle = Paint.Style.FILL
+        setTint(tint)
+    }
+    (parent as? ViewGroup)?.clipChildren = false
+    ViewCompat.setBackground(this, drawable)
+}

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff